跑完一个完整的 WebGL 应用闭环之后,我们来深入理解 OpenGL 的渲染流程/管线(该图也在第一章第一小节中出现过):
接下来详细介绍这个流程中每个环节:
首先需要普及一些概念:
图元装配 (primitives assembly) 就是将顶点数据计算成一个个图元(几何图形的类别由 gl.drawArray() 函数的第一个参数决定),在这个阶段会执行裁剪、透视分割和 Viewport 变换操作(举个例子:当创建了摄像机后,坐标系会发生变化,所以顶点有可能会发生位移/旋转/缩放等操作,同时也需要对超出坐标空间的顶点进行剔除)。最终确定出顶点形成的图形。如下图所示,由原始顶点坐标变成转化后的坐标:
一旦一个顶点被赋值给了 gl_Position,它就进入了图形装配区域,并暂时存储在那里。drawArray(mode, first, count) 中 count 的数值就是顶点着色器的执行次数。
光栅化 (rasterization) 又可以成为「栅格化」,用白话来说就是通过像素绘制图案,由于图元数据是矢量且连续的,但是屏幕像素是离散的。需要有一种方法将连续数据转化为离散数据。所以光栅化本质上也可以描述成一组离散的点在连续数据上的采样过程(Sampling)。下面的动图很形象地展示了「采样」的过程。
光栅化也可以理解为将一个图元转化成一组二维片元 (fragment) 的过程。可以把片元理解为像素,它就是光栅化之后的图案。
在光栅化的过程中,可能会存在一个插值计算(Interpolation)的过程,它是否存在取决于我们是否使用了 varying 变量。以上一节的 demo 为例,当我们在顶点着色器中使用 varying 变量传值给片元着色器之前,它就会进行插值计算,通过顶点计算出它们之间的连续的值:
<!-- 定义顶点着色器 -->
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec3 a_Position;
attribute vec3 a_Color;
varying vec3 v_Color;
void main() {
gl_Position = vec4(a_Position, 1.0);
v_Color = a_Color;
}
</script>
<!-- 定义片元着色器 -->
<script id="fragmentShader" type="x-shader/x-fragment">
varying vec3 v_Color;
void main() {
gl_FragColor = vec4(v_Color, 1.0); // 设置顶点颜色
}
</script>
See the Pen 1. Draw Point & Line & Triangle (part4) - WebGL by ShaderFans (@shaderfans) on CodePen.
只要在顶点着色器中定义了 varying 变量,WebGL 就会自动计算出连续的值,传递值给片元着色器。这个过程也被称为内插过程 (interpolation process) 。每一个 varying 变量都会经过这样的插值计算。下图为内插的示意,通过定义起点和终点的颜色,两点之间的像素颜色都会被自动计算出来。
颜色缓冲区则是渲染的最后一个流程,它往往会和深度缓冲区一起被讨论,它们共同决定最终画布上图像的显示信息。从片元到颜色缓冲区之间,还存在 alpha 测试、模板测试和深度测试,最后经过混合输出到缓冲区上,最终渲染到屏幕中。
这里,我将整个流程以动图形式展示: