Unity自习室-渲染流水线
木子雪夜
2020年11月24日 00:08
收录于文集
共25篇

一、渲染流水线

·应用阶段(Application Stage):

任务:

1. 准备场景数据(摄像机、模型、光源等)。

2. 粗粒度剔除(剔除不可见物体)。

3. 设置模型的渲染状态(材质、纹理、shader)。

输出:

渲染图元(rendering primitives):点、线、三角面等(包含渲染所需的几何信息)

理解:

准备原材料、画布、颜料。

 

·几何阶段(Geometry Stage)

任务:

处理和绘制有关的所有事务,对渲染图元进行逐顶点处理。

输出:

屏幕空间的二维顶点坐标、每个顶点的相关属性(深度值、颜色等)

理解:

各项顶点数据由独立的模型空间转换到和屏幕像素建立关系。

·光栅化阶段(Rasterizer Stage)

任务:

产生屏幕上的像素,决定图元包含像素的显示与否,对顶点属性进行插值,逐像素处理。

输出:

屏幕上的最终图像。

理解:

通过顶点和像素的关系进行对顶点的混合,产生每个像素的最终结果。

 

二、三个阶段的细分

·应用阶段

运作载体:CPU

步骤1:把渲染数据加载到显存中

路径:硬盘 -> RAM -> VRAM

 

步骤2:设置渲染状态

概念:定义网格是怎么渲染的。

 

步骤3:调用Draw Call

性质:一个命令

命令对象:图元列表

过程:CPU -> GPU

概念:CPU告诉GPU要渲染那些图元。

·几何阶段&光栅化阶段

运作载体:GPU

步骤1:顶点着色器(Vertex Shader)

处理对象:模型空间下的顶点。

 

基本任务:把顶点从模型空间转换到齐次裁剪空间。

 

可拓展任务:

·逐顶点光照时的顶点颜色计算。

·制作顶点动画,模拟水面、布料等。

 

步骤2:裁剪(Clipping)

基本任务:将位于齐次裁剪空间外的图元舍弃,位于空间边缘的会形成新的顶点。

步骤3:屏幕映射(Screen Mapping)

基本任务:将图元的x和y坐标转换到屏幕坐标空间(二维坐标系)。而z坐标不会变。

顶点与屏幕的关系:顶点对应了屏幕的哪个像素以及距离这个像素有多远(z的值,也就是之后要用到的深度值)。

自此几何阶段结束,主要的任务是完成了顶点从【模型空间】到【裁剪空间】再到【屏幕空间】的变换 ,最终让顶点和屏幕的像素建立了基本的对应关系。

 

而光栅化最重要的两个目标为:

(1)计算每个图元覆盖了哪些像素。

(2)计算被图元覆盖像素的颜色。

 

步骤4:三角形设置(Triangle Setup)

基本任务:通过三角形网格的顶点计算出每条边上的像素坐标,从而得出边界像素的坐标信息。

输出:整个三角网格表示的数据信息。(此阶段之前只知道顶点的表示信息)

 

步骤5:三角形遍历(Triangle Traversal)

基本任务:检查每个像素是否被三角形网格覆盖,若覆盖,则生成一个片元(fragment)。

计算原理:非顶点位置的像素的信息是根据3个顶点来插值得到的。

输出:片元序列。(不等于像素,而是包含用于计算像素最终颜色的各个状态)

 

步骤6:片元着色器(Fragment Shader)

基本任务:纹理采样(输入的来源通常是顶点着色器的顶点纹理坐标)

其它任务:完成众多渲染技术。

输出:每个片元的颜色。

 

步骤7:逐片元操作(Per-Fragment Operation)

基本任务:合并片元。包含如下内容:

(1)决定片元可见性。(深度、模板测试)

(2)将通过测试的片元的颜色和现存的颜色缓冲区的颜色进行混合,输出最终结果。

自此光栅化阶段结束,可以看到这个阶段不再有坐标变换,而是在处理三角形图元与像素的关系,它们由片元产生联系,而后针对片元进行插值、颜色的计算等,并输出最终结果。

三、两种常见的测试和最后的混合

1. 模板测试(Stencil Test)

简述:GPU会比较当前片元的读取掩码与模板缓冲区的对应片元位置的模板值进行比较。而后根据大小关系来决定片元是否要舍弃。

缓冲区数值是否可更改:是。

常见应用:限制渲染的区域。

高级应用:渲染阴影,轮廓渲染等。

 

2. 深度测试(Depth Test)

简述:GPU会比较当前片元的深度值与深度缓冲区的深度值进行比较。一般是片元的深度值大于深度缓冲区值的时候会舍弃,因为这说明片元相对于摄像机更远,是会被更近的物体挡住的。

缓冲区数值是否可更改:没有通过时更改不了,通过时可以(可以选择将片元的深度值写入缓冲区,也可以关闭写入)。

常见应用:透明效果。

 

3. 混合(Blend)

简述:当片元通过了所有测试,就进入混合阶段。它所解决的问题就是当前要渲染在屏幕上的片元颜色如何和之前已经渲染在屏幕上的颜色之间的关系(例如是否是覆盖)。

常见应用:

(1)处理不透明物体时关闭混合,此时后渲染的片元颜色会直接覆盖之前的。

(2)处理透明物体时开启混合,将源颜色和片元颜色按照一定的计算过程进行混合。例如用透明通道进行加减乘除。(和PS里的混合模式很像,展示的是该层和下层之间的混合结果)

 

写在最后:

实际上这些测试会在片元着色器之前就先进行了,目的是为了减少计算成本。即越在渲染的早期剔除不需要的片元就越节约计算成本,不然等都计算完了再测试和舍弃就会白花不少工夫。

同样的,提前测试也可能会导致片元着色器的操作冲突。因此GPU也会根据是否产生冲突而决定是否实用提前测试。

GPU如何让渲染的图像看起来是连续的?答案是有两个缓冲区,一个呈现在屏幕上时,另一个就在屏幕后进行渲染,两者交替出现。

关于Draw Call:

老生常谈的问题是Draw Call多了会影响CPU的性能。

(1)CPU和GPU的并行工作

方法:使用命令缓冲区(command buffer),形成流水线。

命令缓冲区:包含一个命令队列,CPU写命令,GPU读命令。读写相互独立。Draw Call是这些命令的一种。

(2)Draw Call影响帧率的原因

举个栗子,复制粘贴10240个1KB的文件和复制粘贴1个10MB的文件,后者会更快。因为前者需要10240个命令,而后者只需要1个命令。

同理,Draw Call多了会让CPU花费更多的时间去写命令。

(3)如何减少Draw Call

方法有很多,最常用的是批处理(Batching)。思路是将众多小的Draw Call合为1个大的Draw Call。例如将所有静态物体(不会再做改变)合并成一次Draw Call来处理。

(4)如何在美术阶段减少Draw Call开销

①避免使用大量的小网格,或者试着合并它们。

②避免使用过多的材