WebGL - 绘制三角形

你好,未来的 WebGL 巫师们!今天,我们将踏上一段激动人心的旅程,进入计算机图形学的世界。我们将学习如何使用 WebGL 绘制一个三角形,这可能听起来很简单,但相信我,它是所有你在游戏和电影中看到惊人 3D 图形的基础。所以,系好安全带,让我们一起潜入!

WebGL - Drawing a Triangle

什么是 WebGL?

在我们开始绘制三角形之前,让我们花点时间了解下 WebGL 是什么。WebGL(Web 图形库)是一个 JavaScript API,它允许我们在不使用任何插件的情况下,在网页浏览器中渲染 2D 和 3D 图形。它就像一种超能力,让你能够直接在网页浏览器中创建惊人的视觉效果!

绘制三角形所需的步骤

在 WebGL 中绘制一个三角形可能一开始看起来像是一项艰巨的任务,但别担心!我们会将其分解成可管理的步骤。以下是我们需要做的:

  1. 设置 HTML 画布
  2. 获取 WebGL 上下文
  3. 创建和编译顶点着色器
  4. 创建和编译片段着色器
  5. 创建着色器程序
  6. 定义三角形顶点
  7. 创建缓冲区并加载顶点数据
  8. 链接顶点属性
  9. 绘制三角形

现在,让我们详细地走过每个步骤。

1. 设置 HTML 画布

首先,我们需要在我们的 HTML 文件中创建一个画布,WebGL 将在其中渲染我们的三角形。下面是如何操作:

<canvas id="glCanvas" width="640" height="480"></canvas>

这会创建一个 ID 为 "glCanvas",尺寸为 640x480 像素的画布。你可以根据需要调整这些尺寸。

2. 获取 WebGL 上下文

现在,让我们切换到 JavaScript。我们需要从我们的画布中获取 WebGL 上下文:

const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');

if (!gl) {
console.error('无法初始化 WebGL。您的浏览器可能不支持。');
return;
}

这段代码找到我们的画布,请求 WebGL 上下文,并检查浏览器是否支持 WebGL。

3. 创建和编译顶点着色器

顶点着色器是一个处理顶点数据的程序。下面是如何创建一个:

const vsSource = `
attribute vec4 aVertexPosition;
void main() {
gl_Position = aVertexPosition;
}
`;

function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('编译着色器时发生错误: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}

return shader;
}

const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);

这段代码定义了我们的顶点着色器源和一个用于创建并编译着色器的函数。

4. 创建和编译片段着色器

片段着色器决定了每个像素的颜色。下面是如何创建一个:

const fsSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);

这个片段着色器会将我们的三角形染成红色。

5. 创建着色器程序

现在我们需要将我们的着色器链接成一个程序:

const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.error('无法初始化着色器程序: ' + gl.getProgramInfoLog(shaderProgram));
return;
}

6. 定义三角形顶点

让我们定义我们的三角形顶点:

const vertices = [
0.0,  0.5,  0.0,
-0.5, -0.5,  0.0,
0.5, -0.5,  0.0
];

这些坐标在裁剪空间中定义了一个三角形,每个坐标的范围从 -1 到 1。

7. 创建缓冲区并加载顶点数据

现在我们需要创建一个缓冲区并将我们的顶点数据加载到其中:

const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

8. 链接顶点属性

我们需要告诉 WebGL 如何解释我们的顶点数据:

const aVertexPosition = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aVertexPosition);

9. 绘制三角形

最后,我们可以绘制我们的三角形:

gl.useProgram(shaderProgram);
gl.drawArrays(gl.TRIANGLES, 0, 3);

把所有东西放在一起

下面是我们绘制三角形的完整代码:

// 获取 WebGL 上下文
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');

if (!gl) {
console.error('无法初始化 WebGL。您的浏览器可能不支持。');
return;
}

// 顶点着色器源
const vsSource = `
attribute vec4 aVertexPosition;
void main() {
gl_Position = aVertexPosition;
}
`;

// 片段着色器源
const fsSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

// 创建着色器函数
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('编译着色器时发生错误: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}

return shader;
}

// 创建着色器
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);

// 创建着色器程序
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.error('无法初始化着色器程序: ' + gl.getProgramInfoLog(shaderProgram));
return;
}

// 定义顶点
const vertices = [
0.0,  0.5,  0.0,
-0.5, -0.5,  0.0,
0.5, -0.5,  0.0
];

// 创建缓冲区并加载顶点数据
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

// 链接顶点属性
const aVertexPosition = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aVertexPosition);

// 绘制三角形
gl.useProgram(shaderProgram);
gl.drawArrays(gl.TRIANGLES, 0, 3);

就这样!你已经用 WebGL 绘制了你的第一个三角形。这可能看起来像是绘制一个简单三角形的很多步骤,但记住,这是创建复杂 3D 图形的基础。我们走过的每个步骤对于更高级的渲染都是至关重要的。

结论

恭喜你绘制了你的第一个 WebGL 三角形!你已经迈出了进入计算机图形编程激动人心世界的第一步。记住,每一次旅程都是从第一步开始的——或者在我们的案例中,是第一个三角形。继续练习,继续探索,在你意识到之前,你将会创造出令人惊叹的 3D 图形,让人眼前一亮!

下面是我们使用的主要 WebGL 方法总结表:

方法 描述
getContext('webgl') 获取 WebGL 渲染上下文
createShader() 创建着色器对象
shaderSource() 设置着色器的源代码
compileShader() 编译着色器
createProgram() 创建程序对象
attachShader() 将着色器附加到程序
linkProgram() 链接程序对象
createBuffer() 创建缓冲区对象
bindBuffer() 将缓冲区对象绑定到目标
bufferData() 创建并初始化缓冲区对象的数据存储
getAttribLocation() 返回属性变量的位置
vertexAttribPointer() 指定顶点属性的布局
enableVertexAttribArray() 启用顶点属性数组
useProgram() 设置当前渲染状态下的指定程序
drawArrays() 从数组数据渲染图元

在继续你的 WebGL 旅程时,请将这个表格随时备查。编码愉快,愿你的三角形总是渲染得完美无瑕!

Credits: Image by storyset