WebGL渲染的工作原理 study from https://webglfundamentals.org/webgl/lessons/ step by step

How It Works?

var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 9; //9个顶点
gl.drawArrays(primitiveType, offset, count);

gl_Position:该顶点转换到裁剪空间中的坐标值,GPU接收该值并将其保存起来。 gl_FragColor: fragment shader查询到顶点的颜色 rasterize 光栅化:即用像素画,用gl_FragColor的颜色渲染。

Example1: 三角形

// Fill the buffer with the values that define a triangle.
function setGeometry(gl) {
  gl.bufferData(          // 将数据拷贝到缓冲,这个操作一般在初始化完成
      gl.ARRAY_BUFFER,
      new Float32Array([
             0, -100,
           150,  125,
          -175,  100]),
      gl.STATIC_DRAW);
}

// Draw the scene.
function drawScene() {
  ...
  // Draw the geometry.
  var primitiveType = gl.TRIANGLES;
  var offset = 0;
  var count = 3; //3个顶点
  gl.drawArrays(primitiveType, offset, count);
}
////////////////////////////////////////////////
varying vec4 v_color; // declare a varying to pass data to the fragment shader.
...
void main() {
  // Multiply the position by the matrix.
  gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
 
  // Convert from clip space to color space.
  // Clip space goes -1.0 to +1.0
  // Color space goes from 0.0 to 1.0
  v_color = gl_Position * 0.5 + 0.5;
}
////////////////////////////////////////////////
precision mediump float;
 
varying vec4 v_color; // declare the same varying in the fragment shader
 
void main() {
  gl_FragColor = v_color;
}

上面这段代码画出来的效果是每个顶点的颜色与其在图中的位置密切相关,而跟图片的形状没有关系。

⬇️

顶点
0-100
150125
-175100
写入 gl_Position 的值
0.0000.660
0.750-0.830
-0.875-0.660
写入 v_color 的值
0.50000.8300.5
0.87500.0860.5
0.06250.1700.5

渲染的过程如下图所示,教程里是一个动图,但没有链接我是用不了,它就是从头上的顶点开始,一层一层的渲染颜色。

Example2: 两个三角形

attribute vec2 a_position;
attribute vec4 a_color;
...
varying vec4 v_color;
 
void main() {
   ...
  // 直接把属性值中的数据赋给可变量
  v_color = a_color;
}

  // 寻找顶点着色器中需要的数据,获取WebGL给属性分配的地址
  var positionLocation = gl.getAttribLocation(program, "a_position");
  var colorLocation = gl.getAttribLocation(program, "a_color");
  ...
  // 给颜色数据创建一个缓冲
  var colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); //设置colorBuffer为当前使用缓冲
  // 设置颜色
  setColors(gl);
  ...
 
// 给矩形的两个三角形
// 设置颜色值并发到缓冲
function setColors(gl) {
  // 给每个顶点定义不同的颜色
  gl.bufferData(          // 将数据拷贝到缓冲,这个操作一般在初始化完成
      gl.ARRAY_BUFFER,
      new Float32Array(
        [ Math.random(), Math.random(), Math.random(), 1,
          Math.random(), Math.random(), Math.random(), 1,
          Math.random(), Math.random(), Math.random(), 1,
          Math.random(), Math.random(), Math.random(), 1,
          Math.random(), Math.random(), Math.random(), 1,
          Math.random(), Math.random(), Math.random(), 1]),
      gl.STATIC_DRAW);
}

// 告诉WebGL我们想从缓冲中提供数据
gl.enableVertexAttribArray(colorLocation);
 
// 将缓冲绑定到 ARRAY_BUFFER 绑定点,它是WebGL内部的一个全局变量。
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); //设置colorBuffer为当前使用缓冲
 
// 告诉颜色属性怎么从 colorBuffer (ARRAY_BUFFER) 中读取颜色值
var size = 4;          // 每次迭代使用4个单位的数据
var type = gl.FLOAT;   // 单位数据类型是32位的浮点型
var normalize = false; // 不需要归一化数据
var stride = 0;        // 0 = 移动距离 * 单位距离长度sizeof(type) 
                       // 每次迭代跳多少距离到下一个数据
var offset = 0;        // 从绑定缓冲的起始处开始


//这个命令告诉WebGL从 ARRAY_BUFFER 绑定点当前绑定的缓冲获取数据。 
//每个顶点有几个单位的数据(1 - 4),单位数据类型是什么(BYTE, FLOAT, INT, UNSIGNED_SHORT, 等等...)
//stride 是从一个数据到下一个数据要跳过多少位,最后是数据在缓冲的什么位置。单位个数永远是 1 到 4 之间。
//如果每个类型的数据都用一个缓冲存储,stride 和 offset 都是 0 。 对 stride 来说 0 表示 “用符合单位类型和单位个数的大小”。 对 offset 来说 0 表示从缓冲起始位置开始读取。 它们使用 0 以外的值时会复杂得多,虽然这样会取得一些性能能上的优势, 但是一般情况下并不值得,除非你想充分压榨WebGL的性能。
gl.vertexAttribPointer(
    colorLocation, size, type, normalize, stride, offset)

// 画几何体
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);

⬇️插值的varying

总结

总的来说它就干了俩事:一是把顶点映射到clip space,二是基于一得到的顶点在clip space的位置,渲染每个像素点的颜色。