Skip to main content Link Menu Expand (external link) Document Search Copy Copied

WebGL 执行流程

WebGL 坐标系

在开始之前,我们先介绍一下 WebGL 的的坐标系,WebGL 坐标原点默认在画布中间,且画布大小为-1.01.0之间,画布向右为 x 轴正方向,向上为 y 轴正方向,垂直画布向里为 z 轴正方向。可以到Z 轴默认方向中实际观察一下。

坐标

MDN 裁剪空间说明:https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_model_view_projection#clip_space

clip_space

特别说明一下,网上常说的 WebGL 是右手坐标系,都是不太正确的,只是人们习惯使用右手坐标系描述三维空间,所以会改变默认的坐标系为右手坐标系,而未经修改的 WebGL 的坐标系默认为左手坐标系。

3D_Cartesian_Coodinate_Handedness

执行流程

WebGL 执行流程比较多,作为入门,可简单理解为:初始化 WebGL 上下文 -> 编译着色器代码 -> 创建着色器程序-> 写入数据到缓冲区 -> 执行顶点着色器代码 -> 光栅化 -> 执行片段着色器 -> 绘制到画布上

比较完整的流程可以参考WebGL 零基础入门教程(郭隆邦)中的渲染管线流程图 WebGL 执行流程

初始化 WebGL 上下文

使用getContext('webgl')或者getContext('webgl2')获取到 webgl1 或者 webgl2 的上线文对象,后续所有的操作都是在这个上下文中进行的。

const $canvas = document.querySelector('#canvas')
// 初始化 WebGL 上下文
const gl = $canvas.getContext('webgl2')

编译着色器代码

  • 编译顶点着色器
// 创建 vertex shader
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const vsSource = `
// attribute 声明 vec4 类型变量 a_position
// vec4 前三个值为点的 x,y,z 坐标
attribute vec4 a_position;

void main() {
  // 顶点坐标 a_position 赋值给内置变量 gl_Position
  // 逐顶点处理数据
  gl_Position = a_position;
}`

gl.shaderSource(vertexShader, vsSource)
// 编译顶点着色器
gl.compileShader(vertexShader)
  • 编译片段着色器
// 创建 fragment shader
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
const fsSource = `
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}`

gl.shaderSource(fragmentShader, fsSource)
// 编译片段着色器
gl.compileShader(fragmentShader)

创建着色器程序

创建的着色器程序需包含顶点着色器片段着色器

const shaderProgram = gl.createProgram()
// 添加顶点着色器到着色器程序
gl.attachShader(shaderProgram, vertexShader)
// 添加片段着色器到着色器程序
gl.attachShader(shaderProgram, fragmentShader)
gl.linkProgram(shaderProgram)

// 使用 program
gl.useProgram(shaderProgram)

写入数据到缓冲区

这个步骤的流程可拆解为:获取属性的下标指向位置 -> 创建缓冲区 -> 写入数据到缓冲区 -> 激活属性

// 找到 a_position 的下标指向位置
const aPositionLocation = gl.getAttribLocation(shaderProgram, 'a_position')
// 创建缓存区
const buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
// 把数据写入到缓存区
gl.bufferData(
  gl.ARRAY_BUFFER,
  new Float32Array([
    // 第一个点坐标
    -0.5, 0.0, 0.0,
    // 第二个点坐标
    0.5, 0.0, 0.0
  ]),
  gl.STATIC_DRAW
)

// 绑定缓存区数据到属性
gl.vertexAttribPointer(aPositionLocation, 3, gl.FLOAT, false, 0, 0)
// 在当前程序中激活属性 a_position
gl.enableVertexAttribArray(aPositionLocation)

执行顶点着色器代码

要想把图形绘制到画布中,需先执行WebGLRenderingContext.drawArrays()方法, drawArrays方法第一个参数指定绘制模式,第二个参数指定从哪个点开始绘制,第三个参数指定绘制需要使用到多少个点。在示例中使用如下参数gl.drawArrays(gl.LINE_STRIP, 0, 2),就能绘制出一条线段。

开始绘制后,会逐顶点执行顶点着色器代码,如下图所示,一个顶点会运行一次顶点着色器代码,然后生成顶点的坐标。 顶点着色器执行流程

光栅化

这个步骤可以简单理解为:根据绘制模式的不同,会使用顶点生成一些的像素点。例如绘制一条线,会使用两个顶点生成线段上的所有像素点。

片段着色器

片段着色器的作用可以简单理解为就是给一个个像素点添加颜色,上面光栅化后生成了很多像素点,每个像素点都会执行一次片段着色器去获取最终的颜色值。

绘制到画布上

执行完上面的流程后,一个最简单的 WebGL 流程就可以算是结束了,就可以看到在画布中绘制了一条直线。

示例效果

查看示例效果