Skip to content

WebGL - 基础知识

WebGL (Web Graphics Library) 是一种 JavaScript API,用于在任何兼容的 Web 浏览器中无需插件即可渲染交互式 2D 和 3D 图形。它本质上是一个低级 API,基于 OpenGL ES 标准,为在浏览器中直接进行图形渲染提供 GPU 加速。

要使用 WebGL 绘制任何东西,你需要提供数据(例如顶点位置和颜色)和程序(着色器),这些程序告诉 GPU 如何处理这些数据并将其转换为屏幕上的像素。本章将介绍这些基本概念。

WebGL – 坐标系 (归一化设备坐标)

Section titled “WebGL – 坐标系 (归一化设备坐标)”

WebGL 在 3D 坐标系中操作。顶点数据经过顶点着色器处理后,WebGL 要求坐标位于一个特定范围内,称为“裁剪空间(Clip Space)”。超出此空间的坐标将被“裁剪”(不会被绘制)。裁剪空间坐标随后被转换为归一化设备坐标(Normalized Device Coordinates,NDC),NDC 在所有三个轴(X、Y、Z)上的范围都是 -1 到 +1。

将可见区域想象为一个中心位于 (0,0,0) 的立方体:

  • X 轴:-1 是左边缘,+1 是右边缘。
  • Y 轴:-1 是底边缘,+1 是顶边缘。
  • Z 轴:-1 是最远可见点,+1 是最近可见点。(注意:这个 Z 方向最初可能看起来与直觉相反,但这与投影矩阵通常的设置方式有关)。

任何超出 NDC 中这个 [-1, +1] 立方体的部分都不会被渲染。你的着色器程序负责将模型的坐标转换到这个裁剪空间,然后它会变成 NDC,最后再映射到屏幕像素(窗口坐标)。

从 HTML canvas 元素获取 WebGL 渲染上下文后,就可以通过 JavaScript 开始使用 WebGL API。

以下是一些关键术语:

顶点是 2D 或 3D 空间中的一个点,通常由其位置坐标(X、Y 以及可选的 Z)定义。顶点是几何形状(如三角形、线或点)的基本构建块。除了位置,顶点还可以拥有其他相关数据,如颜色、纹理坐标或法线(指示曲面方向的向量,用于光照)。

要在 2D 空间(Z 隐式为 0)中定义一个角点位于 (0.0, 0.5)、(-0.5, -0.5) 和 (0.5, -0.5) 的三角形,你可以将这些坐标存储起来,通常存储在 JavaScript 数组或 TypedArray 中:

// Using a standard JavaScript Array (often converted to TypedArray later)
const triangleVertices = [
0.0, 0.5, // Top vertex
-0.5, -0.5, // Bottom-left vertex
0.5, -0.5 // Bottom-right vertex
];
// Using Float32Array (preferred for WebGL buffers)
const triangleVerticesTyped = new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
]);

这些数据需要使用 WebGL 缓冲区(Buffers)传输到 GPU。

通常,多个形状会共享顶点。例如,一个正方形可以使用两个共享两个顶点的三角形来绘制。与其重复顶点数据,不如只列出每个唯一的顶点一次,然后使用索引指定绘制它们的顺序。索引仅仅是顶点列表中的一个数字引用(如 0、1、2…)。

考虑一个顶点为:0=(-0.5, 0.5),1=(0.5, 0.5),2=(0.5, -0.5),3=(-0.5, -0.5) 的正方形。我们可以用两个三角形来绘制它:(0, 3, 1) 和 (1, 3, 2)。

const squareVertices = new Float32Array([
-0.5, 0.5, // Vertex 0
0.5, 0.5, // Vertex 1
0.5, -0.5, // Vertex 2
-0.5, -0.5 // Vertex 3
]);
// Indices refer to the positions in squareVertices
const squareIndices = new Uint16Array([
0, 3, 1, // First triangle (Top-left, Bottom-left, Top-right)
1, 3, 2 // Second triangle (Top-right, Bottom-left, Bottom-right)
]);

使用索引可以减少内存使用,并可以提高渲染性能。索引数据存储在 GPU 上特定的索引缓冲区(Index Buffers)中。

WebGL 要求数值数据(如顶点坐标或索引)采用二进制格式,以便高效地传输到 GPU。JavaScript 类型化数组(Float32Array、Uint16Array 等)提供了这种机制,它们代表原始二进制数据缓冲区。

缓冲区是分配在 GPU 上的内存块,用于存储渲染数据。关键类型包括:

  • 顶点缓冲区对象 (Vertex Buffer Objects, VBOs):存储顶点属性数据(位置、颜色、纹理坐标、法线)。通常绑定到 gl.ARRAY_BUFFER。
  • 索引缓冲区对象 (Index Buffer Objects, IBOs):存储顶点索引。通常绑定到 gl.ELEMENT_ARRAY_BUFFER。
  • 帧缓冲区 (Framebuffers):表示渲染的目标,例如屏幕(默认帧缓冲区)或离屏纹理(自定义帧缓冲区)。

你使用 WebGL API 调用来创建缓冲区,绑定它们,然后将你的类型化数组数据上传到其中。

顶点数组对象 (Vertex Array Objects, VAOs)

Section titled “顶点数组对象 (Vertex Array Objects, VAOs)”

设置顶点属性(如位置、颜色)如何从 VBO 读取数据涉及多个 WebGL 调用(vertexAttribPointer、enableVertexAttribArray)。顶点数组对象(VAO)封装了所有这些配置状态。通过绑定一个 VAO,你可以用一个简单的调用恢复所有相关的属性设置。VAO 在 WebGL 2 中是强制要求的,并且在 WebGL 1 中(通过扩展或 WebGL 2 标准)为了性能强烈推荐使用。

网格是顶点、边和面(通常是三角形)的集合,用于定义 3D 对象的形状。在 WebGL 中,通常通过在缓冲区中提供顶点数据和(可选的)索引数据,然后进行绘制调用来渲染网格。

WebGL 提供了两种主要的绘制调用:

  • gl.drawArrays(mode, first, count):使用当前绑定的 VBO 中的连续顶点绘制图元,从索引 first 开始,使用 count 个顶点。
  • gl.drawElements(mode, count, type, offset):使用当前绑定的 IBO 中的索引绘制图元。count 是要使用的索引数量,type 是索引的数据类型(例如 gl.UNSIGNED_SHORT),offset 是 IBO 中的字节偏移量。

这两个函数中的 mode 参数指定要绘制的图元类型(例如 gl.TRIANGLES、gl.LINES、gl.POINTS)。复杂的 3D 模型通常由三角形网格构成。