入门精要总结——渲染管线
学习Shader的时候,渲染管线总是绕不开的一环。所以学习笔记的总结,也从渲染管线开始。
渲染管线描述的是渲染的流程。主要分为应用阶段、几何阶段和光栅化阶段。
应用阶段指的是我们写的应用程序,例如基于C++写的OpenGL或者DirectX程序,Unity3D客户端程序或者Unreal客户端程序等等。该部分内容在CPU中执行。在这一阶段简单地说就是为几何阶段准备待渲染的几何数据以及设置渲染状态,输出几何阶段需要使用的图元。虽然几句话就总结完了,但是这其中要做的工作是非常多的。主要解决下面几个问题:
- 明确渲染的图元类型及其几何表示
- 点
- 线
- 三角面
- ...
- 整理渲染的图元所使用的材质数据
- 纹理数据
- 其他材质数据
- ...
- 准备所有这些数据并提交给GPU
- 根据实际需求设计程序优化上面三个步骤
- 视锥剔除
- 遮挡剔除
- 缓存技术
- .....
几何阶段主要处理应用阶段传递过来的图元数据。该阶段的程序负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。这一阶段在渲染管线中我们可以自定义的部分包括顶点着色器和几何着色器,其中,在没有定义几何着色器时,会使用默认的几何着色器。其他部分会有各自的功能,在我们使用的传统的光栅器中,这部分的内容不允许我们修改,不过了解他们会让我们在我们更好地设计顶点着色器和几何着色器。**几何阶段一个重要的任务就是把顶点坐标变换到屏幕空间中,然后再交给光栅器进行处理。**这一阶段会输出屏幕空间的二维坐标、每个顶点的对应的深度值、着色相关的信息,并传递给下一个阶段。该阶段在GPU上运行。
光栅化阶段使用几何阶段传递过来的数据生成屏幕上的像素,并渲染出最终的图像。该阶段也是在GPU上运行,其中我们可以自定义的阶段包括片元着色器,而其中有些步骤,我们虽然不能自定义,但也提供了一些可调节的参数让我们可以设置它们在这些关键步骤中执行的逻辑。
几何阶段
几何阶段包括几个重要的步骤,总结如下图:
顶点着色器对开发者是完全可编程的,用于实现顶点的空间变换、逐顶点光照、逐顶点阴影、顶点动画等计算逻辑。
曲面细分着色器是可选的,用于细分图元。
几何着色器也是可选的,它可以被用于执行逐图元的操作,同时也可以用于产生更多的图元。
裁剪阶段是可以被配置的阶段,这个阶段是将那些不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元,其中,剔除阶段可以定义应该剔除正面或是背面。
屏幕映射阶段是不能配置和编程的,这一阶段负责把每个图元映射到屏幕坐标系中。
光栅化阶段
光栅化阶段主要有四个步骤:三角形设置,三角形遍历,片元着色器以及逐片元操作。从上一个阶段输出的信息是屏幕坐标系下的顶点位置以及相关的额外信息,如深度值(z值)、法线方向、视角方向等。光栅化阶段有两个重要的目标:计算每个图元覆盖了哪些像素,以及为这些像素计算它们的颜色。
光栅化的第一个阶段是三角形设置,这一步的输入数据是几何阶段输入的顶点数据。这一步根据顶点坐标,计算三角形每条边的像素坐标,然后输出到下一阶段。
三角形遍历阶段将会检查每个像素是否被一个三角网格所覆盖。针对被覆盖的像素,生成相应的片元,交由片元着色器进行处理。
片元着色器是可以编程着色器阶段,该阶段负责计算片元的颜色并输出。这一阶段可以计算逐片元光照,对深度进行修改进行AlphaTest等一系列对片元的操作。
片元着色器输出的片元,需要经过最后一个逐片元操作的步骤之后,才能决定从片元着色器的片元是否能够输出到屏幕上以及如何输出到屏幕上。这一步骤对逐片元进行模板测试、深度测试,通过两个测试后的片元才能够输出到屏幕上,在输出到屏幕之前,会进行混合操作,混合后的颜色会最终输出到屏幕上。
模板测试
模板测试是一个可以通过配置开启或关闭的功能。除了开关配置,还需要额外配置的参数包括:
- 参考值(Ref),范围(0~255)
- 比较函数(Comp)
- 更新操作
- 读掩码(ReadMask),范围(0~255)
- 写掩码(WriteMask),范围(0~255)
参考值是一个0到255范围的整数。这是由应用层指定的一个数值。
比较函数定义的是如何与模板缓存中的值进行比较,这里可以配置的操作如下:
模板测试中,还可以定义测试结果,对模板缓存的影响,这里的测试结果包括模板测试和深度测试。
读掩码是在比较之前被使用的,读掩码将分别与参考值和模板缓存中对应的值按位与之后,再将按位与的结果进行比较,大多数情况读掩码都设置为255.
写掩码是在写入模板缓存之前,与新的模板值按位与之后再写入模板缓存中。大多数情况也是设置成255.
模板测试的过程如下图所示,其中Ref是应用层指定的参考值,Comp是指定的比较操作。ReadMask是设置的读掩码。虽然在UnityShader中都是写在Shader文件里,熟悉OpenGL和DirectX的朋友会知道,这都是在应用层指定的。
在测试结束后(模板测试和深度测试),根据测试结果和对应的更新操作,计算新的模板值并和写掩码按位与之后再写入到模板缓存中。过程如下图所示。
其中,NewStencil Value可以是指定的参考值,可以是Stencil Buffer中的值,可以是0,具体取决于设置的更新操作。
深度测试
深度测试是将输入片元的Z值与深度缓存中的Z值进行比较,这个步骤是可以配置的。可以配置比较方法,也可以配置深度缓存是否可以写入,如下图所示。
混合
混合操作是在更新颜色缓存之前,使用片元着色器输出的颜色值与目标颜色缓存内的目标颜色值根据一定公式计算后再更新颜色缓存。
更新公式如下: