Skip to content

WebGL - 示例应用

WebGL - 示例应用:绘制一个三角形

Section titled “WebGL - 示例应用:绘制一个三角形”

在涵盖了 WebGL 的基础概念如上下文(contexts)、几何体(geometry)、缓冲区(buffers)和着色器(shaders)之后,让我们将它们组合起来,通过一个最小的完整示例来绘制一个简单的 2D 三角形。这展示了一个 WebGL 应用的基本结构和工作流程。

典型的 WebGL 应用包括:

  • HTML: 一个 <canvas> 元素作为绘制表面。
  • JavaScript: 主逻辑,协调 WebGL API 调用。它准备数据、编译着色器、设置缓冲区、配置 attributes/uniforms,并触发绘制命令。
  • GLSL(着色器代码): 顶点和片段着色器程序,定义 GPU 如何处理顶点并确定像素颜色。

JavaScript 扮演着指挥者的角色,准备所有内容并通过 WebGL API 将指令和数据发送到 GPU。GLSL 着色器在 GPU 上执行,负责繁重的渲染工作。

此示例演示了绘制一个静态、单色三角形的核心步骤。

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebGL Simple Triangle</title>
<style>canvas { border: 1px solid black; }</style>
</head>
<body>
<canvas width="300" height="300" id="my_Canvas"></canvas>
<script>
/* 步骤 1: 准备 canvas 并获取 WebGL 上下文 */
const canvas = document.getElementById('my_Canvas');
const gl = canvas.getContext('webgl'); // 请求 WebGL 1 上下文
if (!gl) {
alert('WebGL is not supported by your browser.');
throw new Error('WebGL not supported');
}
/* 步骤 2: 定义几何体并将其存储在缓冲区对象中 */
// 定义三角形的顶点 (X, Y 坐标)
const vertices = new Float32Array([
-0.5, 0.5, // 顶部顶点
-0.5, -0.5, // 左下角顶点
0.0, -0.5 // 右下角顶点 (已修改为非对称)
]);
// 在 GPU 上创建一个缓冲区对象
const vertexBuffer = gl.createBuffer();
// 将缓冲区绑定到 ARRAY_BUFFER 目标 (使其成为当前的 VBO)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 将顶点数据 (TypedArray) 传递给当前绑定的缓冲区
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 解除绑定缓冲区 (一个好习惯,尽管在此处并非强制要求)
// gl.bindBuffer(gl.ARRAY_BUFFER, null);
/* 步骤 3: 创建并编译着色器程序 */
// 顶点着色器源代码 (GLSL ES 1.00)
const vsSource = `
attribute vec2 aVertexPosition; // 输入顶点位置 (2D)
void main(void) {
// 输出剪辑空间中的位置 (XY 来自 attribute, Z=0, W=1)
gl_Position = vec4(aVertexPosition, 0.0, 1.0);
}
`;
// 片段着色器源代码 (GLSL ES 1.00)
const fsSource = `
// 设置浮点变量的精度(片段着色器中必需)
precision mediump float;
void main(void) {
// 输出颜色:红色 (R=1, G=0, B=0, Alpha=1)
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
// 用于编译着色器的辅助函数
function compileShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(`Shader compilation error (${type === gl.VERTEX_SHADER ? 'Vertex' : 'Fragment'}):`, gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = compileShader(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('Shader program linking error:', gl.getProgramInfoLog(shaderProgram));
throw new Error('Shader program link failed');
}
// 告诉 WebGL 在后续绘制中使用此程序
gl.useProgram(shaderProgram);
/* 步骤 4: 将着色器程序与缓冲区对象关联 */
// 在着色器程序中获取 attribute 的位置 (索引)
const positionAttributeLocation = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
// 确保顶点缓冲区再次绑定 (如果之前解除了绑定)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 告诉 WebGL 如何从绑定的缓冲区读取数据到 attribute
const numComponents = 2; // 每个顶点 2 个值 (X, Y)
const type = gl.FLOAT; // 数据类型为 32 位浮点数
const normalize = false; // 不对数据进行归一化
const stride = 0; // 0 = 使用 size * sizeof(type) 获取下一个顶点
const offset = 0; // 从缓冲区的开始位置 (偏移量为 0) 开始
gl.vertexAttribPointer(
positionAttributeLocation,
numComponents,
type,
normalize,
stride,
offset);
// 启用顶点 attribute 数组
gl.enableVertexAttribArray(positionAttributeLocation);
/* 步骤 5: 绘制所需对象 (三角形) */
// 设置清除颜色(用于清除 canvas 的颜色)
gl.clearColor(0.8, 0.8, 0.8, 1.0); // 浅灰色
// 用指定的清除颜色清除颜色缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
// 设置视口(将 NDC -1 到 1 映射到 canvas 像素 0 到 width/height)
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// 绘制三角形
const primitiveType = gl.TRIANGLES; // 绘制填充三角形
const drawOffset = 0; // 从第一个顶点开始
const vertexCount = 3; // 绘制 3 个顶点 (形成一个三角形)
gl.drawArrays(primitiveType, drawOffset, vertexCount);
</script>
</body>
</html>

预期结果:

这段代码将在 300x300 的 canvas 中,以浅灰色背景显示一个实心红色三角形。

  1. 获取上下文: 从 HTML <canvas> 中获取 WebGL 渲染上下文。

  2. 定义几何体和缓冲区: 为顶点数据创建 JavaScript Typed Arrays。创建 WebGL 缓冲区对象(gl.createBuffer),绑定它们(gl.bindBuffer),并上传数据(gl.bufferData)。

  3. 创建并编译着色器: 编写顶点和片段着色器的 GLSL 代码。编译它们(gl.compileShader)并链接到一个着色器程序(gl.createProgram, gl.attachShader, gl.linkProgram)。在每个阶段检查错误。

  4. 关联 Attributes/缓冲区: 获取 attribute 位置(gl.getAttribLocation)。绑定相应的顶点缓冲区。配置 attribute 如何从缓冲区读取数据(gl.vertexAttribPointer)。启用 attribute(gl.enableVertexAttribArray)。(如果使用了 Uniforms,也在这里设置)。

  5. 绘制: 设置视口(gl.viewport),清除 canvas(gl.clearColor, gl.clear)。使用配置好的程序和缓冲区发出绘制命令(gl.drawArrays 或 gl.drawElements)。

这些核心步骤构成了所有 WebGL 渲染的基础。