WebGL-BatchDraw代码阅读+理解

WebGL-BatchDraw代码阅读+理解

WebGL2和WebGL1的区别

推荐相关文章:WebGL2系列之从WebGL1迁移到WebGL2

代码中的shader部分几乎没什么区别,只是用了一些300 es版本的新特性:

  1. 用了in/out代替varing
  2. 片元着色器中不再使用gl_FragColor

关于WebGL-BatchDraw的渲染系统(pixels坐标系统)

点渲染

首先,我们知道webGL的坐标系统是右手坐标系(z轴垂直于屏幕向外,如下图左):

1557915075222
1557915075222

而为我们所熟悉的坐标系往往像上图右边这样的(也就是源码中对应的pixels坐标系统)。

在webGL-BatchDraw里面,用菱形来拟合一个点,这里的一个菱形是用两个三角形拼起来的:

1
2
3
4
5
6
7
8
[-0.5,  0.0,  1.0,
0.0, -0.5, 1.0,
0.0, 0.5, 1.0,
0.5, 0.0, 1.0]
// TRIANGLE_STRIP的意思是:
// 第一个第二个第三个坐标用来画第一个三角形;
// 第二个第三个第四个坐标用来画第二个三角形
this.GL.drawArraysInstanced(this.GL.TRIANGLE_STRIP, 0, 4, this.numDots);
1557915075222
1557915075222

而文章使用了实例化(Instancing)的技术,用来加速绘制。

实例化是一种只调用一次渲染函数却能绘制出很多物体的技术,它节省渲染物体时从CPU到GPU的通信时间,而且只需做一次即可。

每当你需要绘制一个新的点,你就需要对已经实例化好的对象进行变换。

  1. 对现有的实例进行缩放;我们知道 现有的点太小了(长度和高度都只有1);假设我们设置的size为\(s\),那么我们就需要对现有的实例放大\(s\)倍(z轴不放大);其缩放矩阵为: \[ \left[ \begin{array}{ccc}{s} & {0} & {0} \\ {0} & {s} & {0}\\ {0} & {0} & {1}\end{array}\right] \]

  2. 位移;要把实例对象移动到我们想要的位置:\((x, y)\),那么平移矩阵是: \[ \left[ \begin{array}{ccc}{1} & {0} & {x} \\ {0} & {1} & {y}\\ {0} & {0} & {1}\end{array}\right] \] 我们把这两个矩阵相乘合并,也就得到了在原始空间内的变换矩阵: \[ \left[ \begin{array}{ccc}{s} & {0} & {x} \\ {0} & {s} & {y}\\ {0} & {0} & {1}\end{array}\right] \]

    也就是源代码的translate矩阵(577行,779行,857行,947行):

    1
    2
    3
    4
    5
    // 因为glsl中是列主序的,相当于做了一个转置
    mat3 translate = mat3(
    dotSize, 0, 0,
    0, dotSize, 0,
    dotPos.x, dotPos.y, 1);
  3. projection;将原始空间的坐标,映射到webGL中。

    假如一个点的在原始空间的坐标是\((x, y)\),其对应的webGL坐标应该是: \[ (\frac{x-\frac{w}{2}}{w / 2}, -\frac{y-\frac{h}{2}}{h / 2}) = \\\ (x\times\frac{2}{w}-1,-y\times\frac{2}{h}+1) \]

    相当于进行了一次缩放和一次平移,那么我们将它写成矩阵: \[ \left[ \begin{array}{ccc}{2/w} & {0} & {-1} \\ {0} & {-2/h} & {1}\\ {0} & {0} & {1}\end{array}\right] \] 这个矩阵也就是原来的projection矩阵(221行):

    1
    2
    3
    let projection = new Float32Array([2 / this.canvas.width, 0, 0,
    0, -2 / this.canvas.height, 0,
    -1, 1, 1]);

最后整合一下:

就变成了如何把实例对象的坐标(vertexPos)变成需要绘制的对象的坐标:

1
gl_Position = vec4(projection * translate * vertexPos, 1.0);

边渲染

首先,BatchDraw中,同样也是用两个三角形组成的正方形来拟合一条线。

其实例化对象:

1557915075222
1557915075222
  1. 缩放;要将实例化对象缩放到目标的长宽:\((ll, lw)\)(分别指代线的长度和线的宽度),其缩放矩阵(scale矩阵): \[ \left[ \begin{array}{ccc}{ll} & {0} & {0} \\ {0} & {lw} & {0}\\ {0} & {0} & {1}\end{array}\right] \]

  2. 旋转;假设 边连接的两个点的坐标分别是:\((start.x, start.y), (end.x, end.y)\),那么需要旋转的夹角是: \[ \phi = atan (\frac{end.y-start.y}{end.x-start.x})=atan(\frac{delta.y}{delta.x}) \] 旋转矩阵(rotate矩阵)就可以写作: \[ \left[ \begin{array}{ccc}{\cos\phi} & {-\sin\phi} & {0} \\{\sin\phi} & {\cos\phi} & {0} \\ {0} & {0} & {1}\end{array}\right] \]

  3. 平移;经过以上两步,实例的中心位置仍然停留在\((0,0)\)上,需要平移到目标的中心点\(((start.x+end.x)/2, (start.y+end.y)/2)\)。平移矩阵(translate矩阵)写作: \[ \left[ \begin{array}{ccc}{1} & {0} & {(start.x+end.x)/2} \\ {0} & {1} & {(start.y+end.y)/2)}\\ {0} & {0} & {1}\end{array}\right] \]

  4. 映射;映射到webGL坐标系中,原理同点渲染一致 \[ \left[ \begin{array}{ccc}{2/w} & {0} & {-1} \\ {0} & {-2/h} & {1}\\ {0} & {0} & {1}\end{array}\right] \]