跳转到主要内容

Unity Graphics-图形渲染管线(Rendering Pipeline)

demi 提交于

<font color="#9a9a9a">本文章首发于https://pokhoofscut.github.io/</font&gt;

想要在屏幕上渲染出效果惊人的画面,那就必须了解将一个3D场景渲染到2D屏幕的过程,而这个过程就是 渲染管线。可以将它类比成一个工厂的流水线,在工厂的流水线中,原材料经过各个环节的处理最终成为一个产品。而在渲染管线中,原材料就是3D场景的坐标和一些其他数据,然后经过一系列处理变成屏幕上的有色2D像素。

<font size="4" style="line-height: 45px;" color="#c200ff"><strong>概览</strong></font>

渲染管线主要可以分为以下三个阶段:
<ul><li>应用阶段(Application Stage)</li>
<li>几何阶段(Geometry Stage)</li>
<li>光栅化阶段(Rasterization Stage)</li></ul>

<center><img width="600" src="http://imgtec.eetrend.com/files/2021-03/%E5%8D%9A%E5%AE%A2/100063011-12…; alt=""></center><br>

<font size="4" style="line-height: 45px;" color="#c200ff"><strong>应用阶段</strong></font>

应用阶段主要由应用程序主导,与CPU打交道。这个阶段需要完成的任务主要有:
① 数据从硬盘加载到内存并由CPU进行相应计算,将计算好的数据加载到显存
② 设置渲染状态
③ 调用Draw Call

<strong>数据流向</strong>

<center><img width="600" src="http://imgtec.eetrend.com/files/2021-03/%E5%8D%9A%E5%AE%A2/100063011-12…; alt=""></center><br>

数据首先从硬盘加载到内存,再由内存加载到显存。因为渲染工作需要由GPU完成,显卡对于VRAM的访问速度更快,而且一些显卡并不具备直接访问RAM的权利。

一般来说,在数据从RAM被加载到VRAM中之后,RAM中的数据就可以移除了。但是对于一些后续计算仍然需要访问的数据,则需要保留。

<strong>设置渲染状态</strong>

渲染状态是什么?简单来说渲染状态定义了一个网格在应该怎样被渲染,如:使用什么着色器、材质属性等。我们如果使用不同的顶点着色器,那么网格形状可能就会不同;如果我们使用不同的纹理,那么网格的颜色就会不同。

<strong>调用Draw Call</strong>

我们可以将Draw Call看作是CPU给GPU发出的一个指令,因为我们数据准备好了,渲染状态也设置好了,GPU可以开始渲染工作了。

<center><img width="600" src="http://imgtec.eetrend.com/files/2021-03/%E5%8D%9A%E5%AE%A2/100063011-12…; alt=""></center><br>

<font size="4" style="line-height: 45px;" color="#c200ff"><strong>几何阶段</strong></font>

几何阶段在GPU中完成,在接受到 顶点数据输入以后,主要经历以下几个过程:
<ul><li>顶点着色器(Vertex Shader)</li>
<li>曲面细分着色器(Tesselation Shader)</li>
<li>几何着色器(Geometry Shader)</li>
<li>裁剪(Clipping)</li>
<li>屏幕映射(Screen Mapping)</li></ul>

<strong>顶点着色器</strong>

顶点着色器是可编程的,它用于顶点的空间变换和顶点着色。

在顶点着色器中实现的着色被称为Gourand着色,LearnOpenGL中有相关介绍可供参考。

顶点着色器会对输入的顶点进行逐顶点操作,也是就每个顶点都需要调用一次顶点着色器。坐标的空间变换是顶点着色必须要完成的一项工作。整个过程实际上就是一系列的矩阵计算:

<center><img width="600" src="http://imgtec.eetrend.com/files/2021-03/%E5%8D%9A%E5%AE%A2/100063011-12…; alt=""></center><br>

值得注意的一点是,在裁剪之后往往还需要进行 透视除法将坐标映射到 标准化设备坐标(Normalized Device Coordinates, NDC,再通过 屏幕映射 映射的屏幕坐标。至于顶点着色功能,这里不详细介绍,因为这是一个可选项,并不是顶点着色器必须要做的。

<strong>曲面细分着色器和几何着色器</strong>

基于理解渲染管线的基本原理,这里也不具体介绍了,有兴趣可以自行了解。

<strong>裁剪</strong>

在刚刚提到的坐标空间变换中,你应该已经注意到了这一点。用摄像机模拟人眼观察的时候,视野往往无法覆盖整个场景的内容,此时可以将视野之外的顶点裁剪掉,以提升性能。这一步是不可编程的。

<strong>屏幕映射</strong>

这部分也在坐标空间变换中提到了,就是将NDC中的坐标的x,y映射到屏幕坐标。到这里,细心的朋友可能就会发现,我们在将裁剪坐标映射到NDC时只做了透视除法,也就是NDC依旧是一个三维坐标,而屏幕只能处理二位坐标,那NDC中的z坐标去哪了?实际上,z坐标会和处理之后的x,y坐标一起被传入到光栅化阶段,用来进行深度的相关计算。

<center><img width="600" src="http://imgtec.eetrend.com/files/2021-03/%E5%8D%9A%E5%AE%A2/100063011-12…; alt=""></center><br>

<font size="4" style="line-height: 45px;" color="#c200ff"><strong>光栅化阶段</strong></font>

光栅化主要经历以下阶段:
三角形设置(Triangle Setup)
三角形遍历(Triangle Traversal)
片元着色器(Fragment Shader)

<strong>三角形设置</strong>

上一个阶段输出的信息只包含顶点信息,对于一个三角形面片来说,就是三个端点的信息。这个阶段主要实现利用三角形三个顶点的信息计算它每条边上的像素坐标。

<strong>三角形遍历</strong>

有了三角形每条边上像素坐标之后,检查每个像素时候被一个三角形网格覆盖。如果被覆盖,就生成一个 片元(Fragment)。这个过程也被成为扫描变换(Scan Conversion)。

片元是一系列带有属性的图像元素,这些属性包括了屏幕坐标、深度值、法线坐标等,用于计算一个像素的最终颜色。所以片元可以理解为一个像素的原型。

<strong>片元着色器</strong>

对三角形三个顶点的信息进行插值,得到其覆盖片元的信息。

<center><img width="600" src="http://imgtec.eetrend.com/files/2021-03/%E5%8D%9A%E5%AE%A2/100063011-12…; alt=""></center><br>

<font style="line-height: 40px;"><strong>总结</strong></font>

至此,关于渲染管线基本信息已经介绍完毕了。学习了解以下渲染管线对于之后更进一步的学习是非常有帮助的,所以又复习了一遍并将其记录了下来,又高兴能在这里跟大家分享,可能文章还存在很多的不足,欢迎批评和讨论。

<strong>引用和参考</strong>
<font color="#9a9a9a">[1] LearnOpenGL:https://learnopengl-cn.github.io/</font&gt;
<font color="#9a9a9a">[2] 冯乐乐,Unity Shader入门精要</font>

<font color="#9a9a9a">版权声明:本文为CSDN博主「ShowhoopStudio」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。</font>
<font color="#9a9a9a">原文链接:https://blog.csdn.net/qq_37389722/article/details/89057521</font&gt;