每个顶点的数据:attribute
attribute 是逐顶点的输入,比如位置、颜色、法线、UV。在 GLSL ES 3.00 里用 in 声明,并可以用 layout(location = N) 固定它的位置编号,JS 端就不用再查询:
glsl
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec3 aColor;
out vec3 vColor; // varying:传给片元着色器
void main() {
vColor = aColor;
gl_Position = vec4(aPosition, 0.0, 1.0);
}从顶点到像素:varying
顶点着色器的 out 变量叫 varying。光栅化器会按像素到三个顶点的距离(重心坐标)把它们插值,片元着色器用同名、同类型的 in 接收。所以三个顶点分别是红、绿、蓝时,三角形内部会出现平滑的渐变,而你没写任何渐变代码。
在声明前加 flat 可以关闭插值:整个三角形都使用“激发顶点”(provoking vertex,默认是最后一个顶点)的值。切换面板里的 interpolation 对比效果。
交错缓冲区:步长与偏移
把一个顶点的所有数据挨着放,[x, y, r, g, b, x, y, r, g, b, …],叫交错布局(interleaved)。读取时要告诉 GPU 两件事:
- stride(步长):从一个顶点跳到下一个顶点要走多少字节。这里 5 个 float × 4 字节 = 20 字节。
- offset(偏移):这个属性从顶点数据的第几个字节开始。位置在 0,颜色在 2 × 4 = 8。
glsl
// JS: gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 20, 8)交错布局的好处是缓存友好:GPU 读取一个顶点时,需要的数据都在相邻的内存里。
动态更新
本例每帧用 gl.bufferSubData 改写颜色,让彩虹缓慢旋转。缓冲区创建时用 gl.DYNAMIC_DRAW 提示驱动:这块数据会频繁变化。
打开“show triangles”,你会看到花朵其实是 12 个三角形拼成的扇形,中心顶点是白色。
核心 API
layout(location)in / outflatgl.vertexAttribPointergl.enableVertexAttribArraygl.bufferSubDatagl.DYNAMIC_DRAW动手试试
- 1把 SEGMENTS 改成 3 或 4,观察插值在大三角形上的样子。
- 2把中心顶点的颜色改成黑色,看渐变如何变化。
- 3故意把颜色属性的 offset 写成 4,猜猜会出现什么颜色,再运行验证。
还没有输出。console.log 的内容会显示在这里。
运行中