跳到主要内容
3D Playground
WebGL 与着色器第 1 / 12 节

GPU 渲染管线 · Hello Triangle

入门WebGL 2

一帧画面是怎么来的

GPU 把绘制拆成一条固定的流水线(pipeline),数据从左流到右:

  1. 顶点数据:你上传到显存的数字,比如三个点的坐标。
  2. 顶点着色器(vertex shader):每个顶点运行一次,输出它在 裁剪空间 中的位置 gl_Position,x、y 范围是 -1 到 1。
  3. 图元装配:按 gl.TRIANGLES、gl.LINE_LOOP 等模式把顶点连成三角形或线段。
  4. 光栅化:找出三角形覆盖了哪些像素,为每个像素生成一个片元(fragment)。
  5. 片元着色器(fragment shader):每个片元运行一次,输出颜色。
  6. 帧缓冲:颜色写进画布。

其中第 2 步和第 5 步是可编程的,用 GLSL 编写。

着色器:编译与链接

着色器像 C 程序一样需要先编译、再链接成一个 program。编译失败不会抛异常,必须主动查询状态并读取日志:

glsl
#version 300 es
in vec2 aPosition;
void main() {
  gl_Position = vec4(aPosition, 0.0, 1.0);
}

gl.getShaderParameter(shader, gl.COMPILE_STATUS) 为 false 时,用 gl.getShaderInfoLog 拿到错误信息并打印出来。链接同理,用 LINK_STATUS 和 getProgramInfoLog。

Buffer 与 VAO

  • Buffer 是显存里的一块字节数组,gl.bufferData 把 Float32Array 上传进去。
  • VAO(顶点数组对象)记录“怎样读取这块内存”:gl.vertexAttribPointer(位置, 分量数, 类型, 是否归一化, 步长, 偏移)。之后每次绘制只需绑定 VAO。

绘制

每帧先 gl.viewport 把裁剪空间映射到像素,gl.clear 清屏,然后 useProgram、bindVertexArray,最后 gl.drawArrays(gl.TRIANGLES, 0, 3):3 个顶点,组成 1 个三角形。

点一下面板里的“compile a broken shader”,在控制台读一读真实的编译错误日志:以后调试着色器,这就是你的第一手线索。

核心 API

canvas.getContext('webgl2')gl.createShadergl.compileShadergl.getShaderInfoLoggl.linkProgramgl.createBuffergl.bufferDatagl.createVertexArraygl.vertexAttribPointergl.drawArrays

动手试试

  1. 1把三个顶点之一改到 (0.9, 0.9),看三角形如何变形。
  2. 2把 primitive 切到 POINTS,再在顶点着色器里改 gl_PointSize。
  3. 3在顶点着色器里写错一个变量名,读一读控制台中的报错行号。
运行中