(五)WebGL中vertexAttribPointer方法的使用详解
vertexAttribPointer 是 WebGL 中用于定义如何在顶点着色器中访问顶点数据的一种方法。它的作用是告诉 WebGL 如何解释和从当前绑定的缓冲区中获取顶点数据。
在 WebGL 中,顶点数据通常存储在一个缓冲区对象中,这些数据包括位置、颜色、法线、纹理坐标等。然后,使用 vertexAttribPointer 来定义这些数据如何映射到着色器中的顶点属性。
我们将通过一个简单的 WebGL 示例,详细讲解 vertexAttribPointer 的使用。我们将创建一个渲染一个简单三角形的程序,展示如何使用 vertexAttribPointer 设置顶点属性。
代码示例
1. HTML 基础结构
这部分与之前一样。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebGL vertexAttribPointer 示例(包含颜色)</title>
</head>
<body><canvas id="webglCanvas" width="500" height="500"></canvas><script src="app.js"></script>
</body>
</html>
2. JavaScript 代码
我们将在 app.js
中修改数据,传递顶点位置和颜色,同时更新顶点着色器和片段着色器。
// 获取Canvas元素并初始化WebGL上下文
const canvas = document.getElementById("webglCanvas");
const gl = canvas.getContext("webgl");// 检查WebGL是否可用
if (!gl) {console.error("无法初始化WebGL!");
}// 定义顶点着色器代码
const vsSource = `attribute vec4 a_position; // 顶点位置attribute vec4 a_color; // 顶点颜色varying vec4 v_color; // 从顶点着色器传递到片段着色器的颜色变量void main() {gl_Position = a_position; // 设置顶点的位置v_color = a_color; // 将颜色传递到片段着色器}
`;// 定义片段着色器代码
const fsSource = `precision mediump float;varying vec4 v_color; // 接收来自顶点着色器的颜色void main() {gl_FragColor = v_color; // 输出顶点颜色}
`;// 创建着色器
function createShader(type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {console.error("Shader编译失败: ", gl.getShaderInfoLog(shader));return null;}return shader;
}// 创建程序
function createProgram(vertexShaderSource, fragmentShaderSource) {const vertexShader = createShader(gl.VERTEX_SHADER, vertexShaderSource);const fragmentShader = createShader(gl.FRAGMENT_SHADER, fragmentShaderSource);const program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {console.error("Program链接失败: ", gl.getProgramInfoLog(program));return null;}return program;
}// 创建着色器程序
const program = createProgram(vsSource, fsSource);// 获取属性和均匀变量的位置
const a_position = gl.getAttribLocation(program, "a_position");
const a_color = gl.getAttribLocation(program, "a_color");
const v_color = gl.getAttribLocation(program, "v_color"); // 片段着色器接收的颜色gl.useProgram(program);// 定义包含位置和颜色的顶点数据
const vertices = new Float32Array([// 顶点1 位置(x, y) 和颜色(r, g, b, a)0.0, 0.5, 1.0, 0.0, 0.0, 1.0, // 红色// 顶点2 位置(x, y) 和颜色(r, g, b, a)-0.5, -0.5, 0.0, 1.0, 0.0, 1.0, // 绿色// 顶点3 位置(x, y) 和颜色(r, g, b, a)0.5, -0.5, 0.0, 0.0, 1.0, 1.0 // 蓝色
]);// 创建并绑定缓冲区
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);// 设置顶点位置属性指针
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.enableVertexAttribArray(a_position);// 设置颜色属性指针
gl.vertexAttribPointer(a_color, 4, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 2 * Float32Array.BYTES_PER_ELEMENT);
gl.enableVertexAttribArray(a_color);// 清除背景,并绘制三角形
gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置清除颜色为黑色
gl.clear(gl.COLOR_BUFFER_BIT);// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);
3. 代码解析
1. 顶点数据的结构
在这个例子中,每个顶点不仅包含位置坐标,还包含颜色信息。我们将这些数据存储在一个 Float32Array
中:
const vertices = new Float32Array([// 顶点1 位置(x, y) 和颜色(r, g, b, a)0.0, 0.5, 1.0, 0.0, 0.0, 1.0, // 红色// 顶点2 位置(x, y) 和颜色(r, g, b, a)-0.5, -0.5, 0.0, 1.0, 0.0, 1.0, // 绿色// 顶点3 位置(x, y) 和颜色(r, g, b, a)0.5, -0.5, 0.0, 0.0, 1.0, 1.0 // 蓝色
]);
每个顶点由 6 个值组成,其中前 2 个值是顶点的位置(x, y),后 4 个值是颜色信息(r, g, b, a)。例如,第一个顶点的位置是 (0.0, 0.5)
,颜色是红色 (1.0, 0.0, 0.0, 1.0)
。
2. 设置顶点位置属性指针
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.enableVertexAttribArray(a_position);
a_position
: 顶点着色器中的位置属性。2
: 每个顶点的属性数量,这里是 2 个(x, y)。gl.FLOAT
: 数据类型是浮点数。false
: 不进行标准化,直接使用浮点数值。6 * Float32Array.BYTES_PER_ELEMENT
: 每个顶点总共有 6 个浮点数(2 个位置坐标和 4 个颜色值),因此步长是 6 个浮点数的字节数。0
: 顶点数据的起始偏移量,表示从数据的开始位置读取。
3. 设置颜色属性指针
gl.vertexAttribPointer(a_color, 4, gl.FLOAT, false, 6 * Float32Array.BYTES_PER_ELEMENT, 2 * Float32Array.BYTES_PER_ELEMENT);
gl.enableVertexAttribArray(a_color);
a_color
: 顶点着色器中的颜色属性。4
: 每个顶点的颜色数据包含 4 个浮点数(r, g, b, a)。6 * Float32Array.BYTES_PER_ELEMENT
: 步长与顶点位置数据一样。2 * Float32Array.BYTES_PER_ELEMENT
: 偏移量为 2 个浮点数,即从位置数据后开始读取颜色数据。
4. 顶点着色器和片段着色器
-
顶点着色器:
接收位置和颜色数据,并将颜色传递到片段着色器。attribute vec4 a_position; // 顶点位置 attribute vec4 a_color; // 顶点颜色 varying vec4 v_color; // 从顶点着色器传递到片段着色器的颜色变量 void main() {gl_Position = a_position; // 设置顶点的位置v_color = a_color; // 将颜色传递到片段着色器 }
-
片段着色器:
接收来自顶点着色器的颜色并将其输出为最终的片段颜色。precision mediump float; varying vec4 v_color; // 接收来自顶点着色器的颜色 void main() {gl_FragColor = v_color; // 输出顶点颜色 }
5. 渲染三角形
gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置清除颜色为黑色
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
在这个例子中,我们通过 gl.drawArrays
渲染一个包含 3 个顶点的三角形,每个顶点都有位置和颜色数据。
总结
- 通过
vertexAttribPointer
可以指定顶点属性的位置和格式,我们在这个示例中将位置和颜色数据一起传递。 a_position
和a_color
分别对应顶点着色器中的位置和颜色属性。- 顶点数据的布局包含位置和颜色信息,在
vertexAttribPointer
中通过偏移量来分别指示位置和颜色数据的起始位置。 - 顶点着色器将颜色传递给片段着色器,片段着色器最终渲染三角形。
这样,你可以在 WebGL 中渲染带有颜色的三角形,并通过 vertexAttribPointer
控制如何传递和使用顶点数据。