RTR4读书笔记(一) 图形渲染管线
实时渲染管线大致分为四个阶段:
- 应用阶段
- 几何处理阶段
- 光栅化阶段
- 像素处理阶段
每个阶段通常本身就是一条管线,由几个子阶段构成。
1. 应用阶段
这个阶段通常在CPU上执行,主要是做准备输入的工作,即准备本帧需要渲染的数据。
从游戏渲染来理解的话,逻辑线程所做的模拟更新(物理、AI、动画、脚本)、场景管理、可见性剔除、排序、设置渲染状态并提交 Draw Call等工作都属于应用阶段。
应用阶段也包括渲染线程的工作——把主线程提交的渲染指令整理成 GPU 命令缓冲。
2. 几何处理阶段
这个阶段的操作对象主要是顶点,这个阶段可以细分为:
- 顶点着色
- 投影
- 裁剪
- 屏幕映射
2.1 顶点着色
这里就是我们熟悉的Vertex Shader执行的主要阶段了,该阶段主要完成两个任务:
- 计算顶点位置,比如执行model变化、view变换,将模型顶点转换到观察空间中。
- 计算开发者可能希望作为顶点数据输出的内容,比如蒙皮和顶点动画等逐顶点计算的内容。
2.2 投影
投影就是应用投影矩阵 P,把视锥体 (view frustum) 压成标准立方体(canonical view volume),分正交/透视两种投影。
投影阶段其实也是vertex shader的一部分,经过投影后,输出的顶点坐标叫做裁剪空间坐标(x, y, z, w),注意这里还没做透视除法,坐标没有完成归一化。
之所以概念上把顶点着色和投影区分开,是因为顶点做完mv变换后, 先会用材质和光源属性完成顶点着色或其他计算,再做投影。
2.3 可选的顶点处理
完成投影后,有几个可选的阶段:
- 曲面细分:将曲面在GPU上划分成更多精细的三角形,提高渲染质量的同时避免需求应用阶段输入大量顶点数据。
- 几何着色器:可以读取图元然后产生新的顶点,常用于粒子生成,比如模拟烟花爆炸,将每个火球表示为一个点,该阶段可以将基于点生成一个正方形覆盖多个像素。
- 流输出:将顶点的计算结果输出到数组做后续处理,而不是往下继续执行渲染管线,常用于粒子模拟。
2.4 裁剪
这个阶段剔除掉相机视域体外的图元,把相机视角内看不到的图元剔除掉,对于部分在视椎体内的图元,会在交界处生成新的顶点。
这里的判断规则也很简单:
在裁剪空间(齐次坐标)中判断,不除以 w。顶点 (x, y, z, w) 在视锥体内当且仅当满足六个裁剪平面:
-w ≤ x ≤ w
-w ≤ y ≤ w
-w ≤ z ≤ w (OpenGL;D3D 是 0 ≤ z ≤ w)
任何一项不满足就说明顶点在立方体(视锥体)外,需要被剔除或在边上插值出交点。
裁剪完成后,会执行透视除法(除以分量w),将三角形顶点归一化到NDC坐标系。
2.5 屏幕映射
这个阶段把裁剪阶段的输出的归一化坐标转换为屏幕空间的屏幕坐标(实际的像素坐标)。
渲染API提供设置的viewport在这个阶段生效。
viewport描述了屏幕上的一个窗口,而裁剪阶段输出的是一个归一化坐标[-1, 1], 这个归一化坐标描述的就是窗口内顶点的相对位置。
举例: 裁剪阶段输出坐标 [x, y, z], 窗口描述为[x1, y1, x2, y2] (x1 < x2, y1 < y2)
顶点的屏幕坐标为:
X = x1 + (x2 - x1) * x
Y = y1 + (y2 - y1) * y
Z值根据API不同,映射到不同的空间, OpenGL是[-1, 1], DirectX是[0, 1]
这里描述的X,Y其实是像素的一个角,像素中心是(X + 0.5, Y + 0.5)
所有API的像素位置都是从左到右增加的,但不同API的顶边和底边的起始位置不一定一致。
OpenGL的坐标系:左下角是像素(0, 0)的位置
DirectX的坐标系:左上角是像素(0, 0)的位置
3. 光栅化阶段
完成几何处理阶段后,光栅化阶段负责找到需要渲染的图元中的所有像素,送给像素处理阶段处理。
光栅化阶段是不可编程的,主要分成两阶段:
- 三角形设置/图元装配阶段:计算三角形的微分方程、边缘方程及其他数据,用于三角形遍历以及几何阶段产生的着色数据的插值。
- 三角形遍历:检查每个像素中心被三角形覆盖的情况,找到被三角形覆盖的像素,送给下一阶段。
渲染API提供设置的scissor在这个阶段生效:
裁剪测试 (Scissor Test):光栅化阶段中逐片元的屏幕空间测试,判断片元是否落在用户指定的屏幕矩形区域内,不改变几何,只决定片元是否继续进入后续阶段
4. 像素处理阶段
这个阶段处理的单位是像素。该阶段可细分为:
- 像素着色
- 合并
4.1 像素着色
Fragment shader描述的就是这个阶段。
这个阶段计算出像素的最终颜色值。
可以使用discard显示丢弃该像素不往下继续执行。
5.1 合并
合并阶段负责将像素着色阶段生成的片元颜色与当前存储在缓冲区中的颜色进行组合。这个阶段是不可以完全可编程的,但是是高度可配置的。
可以完成以下操作:
- 像素的混合方式(blend)
- 颜色掩码,控制哪些颜色通道可写入。
- 可见性处理,比如深度测试、模板测试等。
注意,合并阶段在像素着色之后,也就意味着前面的计算实际都已经完成了,因此这里的深度测试、模板测试都只是控制像素最终是否写入缓冲区来保证最终渲染效果正确,这里被剔除的像素基本已经走完了一遍渲染管线,因此深度测试和模板测试对提升渲染效率帮助很小(只节省了混合和颜色缓冲写入)。
真正可以提升效率的是Early-Z,Hi-Z Culling。

浙公网安备 33010602011771号