前言:在学习的过程中笔记是不可缺少的,匆匆看过一遍不记笔记和实操的话总会忘掉一些知识点或者是记的不牢固,所以我写了本系列文章,一方面是强迫自己认认真真的看完一章课程,另一方面记笔记加深一下自己的记忆以及思考。所以本系列文章是我学习过程中的笔记,思考,实操以及作业然后分享给大家,欢迎大佬们指正。//出处知乎@SakuraWhite
//渲染管线是整个渲染流程的基础,也是学习渲染需要熟知的部分
//单拿出一章来详细的将渲染管线呈现
#渲染管线大致浏览
![](https://i-blog.csdnimg.cn/blog_migrate/057bc5c17806c505e28096a00dd8e907.png)
一、渲染是什么?
![](https://i-blog.csdnimg.cn/blog_migrate/25904c31448a07bf42c6db5a26d83f35.jpeg)
二、流水线是什么
![](https://i-blog.csdnimg.cn/blog_migrate/a25a8134e78605137aa09ec9a03b9918.png)
三、什么是渲染流水线
在Real-Time Rendering 3rd中将渲染流水线分成了三个概念阶段
注意:这里仅仅是概念性阶段, 每个阶段本身通常也是一个流水线系统, 即包含了子流水线 阶段。
![](https://i-blog.csdnimg.cn/blog_migrate/6aaddb54690e5056be0bed4d7ae78095.png)
几个要点:
• 每个阶段本身也可能是一条管线,如图中的几何阶段所示。此外,还可以对有的阶段进行全部或者部 分的并行化处理,如图中的光栅化阶段。应用程序阶段虽然是一个单独的过程,但是依然可以对之进 行管线化或者并行化处理。
• 最慢的管线阶段决定绘制速度,即图像的更新速度,这种速度一般用 FPS 来表示,也就是每秒绘制的 图像数量,或者用 Hz 来表示。
四、CPU渲染管线
以unity默认渲染管线为例,渲染流水线的起点是 CPU, 即应用阶段。
![](https://i-blog.csdnimg.cn/blog_migrate/6016a587e1b684a5fdb4f635b953f924.png)
应用程序阶段的主要任务:在应用程序阶段的末端,将需要在屏幕上(具体形式取决于具体输入设 备)显示出来绘制的几何体(也就是绘制图元,rendering primitives,如点、线、矩形等)输入到绘制 管线的下一个阶段。
应用阶段大致可分为下面3 个阶段
(1) 把数据加载到显存中
所有渲染所需的数据都需要从硬盘 Hard Disk Drive, 印江))中加载到系统内存 Random Access Memory, RAM) 。,网格和纹理等数据又被加载到显卡上的存储空间一显存(Video Random Access Memory, VRAM )中
![](https://i-blog.csdnimg.cn/blog_migrate/264140f279596d0f4183896504ee0433.png)
(2) 设置渲染状态
渲染状态定义了场景中的网格是怎样被渲染的。
(3) 调用 Draw Call (渲染命令)
的发起方是 CPU, 接收方是GPU
![](https://i-blog.csdnimg.cn/blog_migrate/7e692d6ab7419a45ee84478d0e6f3ee2.png)
(4)整个应用期间,进行的实际操作
![](https://i-blog.csdnimg.cn/blog_migrate/5500404d41e33238097d52d837afdf69.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/7fa2d9366bd3d7dfbed0963947df54c4.png)
都是通过调用 Draw Call 进行,最终将模型信息传入到GPU管线中
五、GPU渲染管线
自此进入到GPU渲染管线
整个GPU管线进行的操作
![](https://i-blog.csdnimg.cn/blog_migrate/56371883a66b8854ed54b1d46674dcf5.jpeg)
几何部分
1、顶点着色器
顶点着色器 (Vertex Shader) 是流水线的第 个阶段,它的输入来自于 CPU 。顶点着色器的 处理单位是顶点,也就是说,输入进来的每个顶点都会调用一次顶点着色器。顶点着色器本身不 可以创建或者销毁任何顶点,而且无法得到顶点与顶点之间的关系。是因为这样的相互独立性, GPU 可以利用本身的特性并行化处 理每一个顶点 ,这意味着这一阶段的处理速度会很快。
顶点着色器需要完成的工作主要有:坐标变换和逐顶点光照。
![](https://i-blog.csdnimg.cn/blog_migrate/5d9c4288bb9fee59ece749a03db5dfc9.png)
GPU还需要进行模型转化与相机转换(Model- & Camera transformation),在3D渲染中,我们必须要设置一个摄像机来接收图像,这个摄像机的视野决定了GPU最终会让我们看到什么样的画面,为了方便之后的运算,这里还需要根据视锥体(可以理解为摄像机能够看到的范围)将坐标空间由世界空间映射到摄像机的观察空间。
一个最基本的顶点着色器必须完成的一个工作是,把顶点坐标从模型空间转换到齐次裁剪空间
o . pos = mul(UNITY_MVP,v.position);
MVP过程详见图形学笔记,不过多描述
![](https://i-blog.csdnimg.cn/blog_migrate/9aa6a71eef3bf4955b595ef78b3c549d.jpeg)
还可以进行的操作有:坐标变换(如动画)、逐顶点色彩处理(如光照、纹理采样)。
坐标变换
![](https://i-blog.csdnimg.cn/blog_migrate/64483f257c2c1c648244e6d7c401256b.png)
逐顶点色彩信息处理
![](https://i-blog.csdnimg.cn/blog_migrate/d305b19f9219ded62bb283519c9ed53d.png)
2、曲面细分着色器(Tessellation Stage)
![](https://i-blog.csdnimg.cn/blog_migrate/e25ef8b28add9c49ffe0999af8b49305.png)
如果为这些细分的顶点再准备一些位置信息,那么这些细分的顶点将有助于我们展现一个细节更加丰富的模型。这也是贴图置换(Displacement Mapping)的基本思路。
![](https://i-blog.csdnimg.cn/blog_migrate/8ebf3c289cbf943bd9bdde88774135e1.png)
3、几何着色器(Geometry Shader)
在这个阶段,开发者可以控制GPU对顶点进行增删改操作。几何着色器与顶点着色器都可以对顶点的坐标进行修改,但几何体着色器并行调用硬件困难,并行程度低,效率和顶点着色器有很大的差距;如果不是要做顶点增、删这些仅仅能用几何着色器实现的效果,那么还是用顶点着色器来完成
4、投影(Projection)
MVP中P的过程,从相机空间变换到了裁剪空间
5、裁剪
一个图元相对视体内部的位置,分为三种情况:完全位于内部、完全位于外部、 部分位于内部。所以就要分情况进行处理:
• 当图元完全位于视体内部,那么它可以直接进行下一个阶段。
• 当图元完全位于视体外部,不会进入下一个阶段,可直接丢弃,因为它们无需进行渲染。
• 当图元部分位于视体内部,则需要对那些部分位于视体内的图元进行裁剪处理。
![](https://i-blog.csdnimg.cn/blog_migrate/22a38de45ee65a3b12b458b96559698f.jpeg)
不需要的顶点裁剪掉后,GPU需要把顶点映射到屏幕空间,这是一个从三维空间转换到二维空间的操作,更符合大家对“投影”的理解。对透视裁剪空间来说,GPU需要对裁剪空间中的顶点执行齐次除法(其实就是将齐次坐标系中的w分量除x、y、z分量),得到顶点的归一化的设备坐标(Normalized Device Coordinates, NDC),经过齐次除法后,透视裁剪空间会变成一个x、y、z三个坐标都在[-1,1]区间内的立方体。对于正交裁剪空间就要简单得多,只需要把w分量去掉即可。
此时顶点的x、y坐标就已经很接近于它们在屏幕上所处的位置了,不过还有一个多出来的z分量,不过它也不会被白白丢弃,而是被写入了深度缓冲(z-buffer)中,可以做一些有关于顶点到摄像机距离的计算。
6、屏幕映射
尽管GPU已经得到了顶点的x、y坐标,但他们处于[-1,1]区间中的,GPU还需要进行一定的计算才能把他们映射到我们的1920*1080甚至2560*1440的屏幕。得到的新坐标系称为窗口坐标系,虽然只需要两个坐标把顶点投射到屏幕上,但它仍然是三维的,这个多出来的z值就是在上面算出来的深度。
光栅化——(在图形学笔记05中也有着重描述)
与几何阶段相似,该阶段细分为几个功能阶段:
1、图元组装(Primitive Assembly)
![](https://i-blog.csdnimg.cn/blog_migrate/069c1b2b042c0a7e0c3c12137efbfd4e.png)
2、三角形遍历(Triangle Traversal)阶段
这一阶段牵扯到了抗锯齿(Anti-aliasing)操作,具体操作到图形学笔记06中,有详细的关于反走样问题的描述以及解决方法
图形学05笔记中,有详细的光栅化过程
![](https://i-blog.csdnimg.cn/blog_migrate/7a7b68fb93afd9d215b824c36727113b.png)
3、像素着色(Pixel Shading)阶段 &片元着色器(Fragment Shader)
所有逐像素的着色计算都在像素着色阶段进行,使用插值得来的着色数据作为输入,输出结 果为一种或多种将被传送到下一阶段的颜色信息。纹理贴图操作就是在这阶段进行的。
![](https://i-blog.csdnimg.cn/blog_migrate/5aefb22b641f25a97cfe082ffdc48d1a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0cf00c63b823578444e57bc7e8821055.png)
这里在unity具体的渲染管线中,可以理解为片元shader
在unity中有光照模型和光照组成
光照模型
1、Phong模型
2、Lambert 模型
3、Blinn-Phong 光照模型
光照组成
![](https://i-blog.csdnimg.cn/blog_migrate/8f42c122bd974282884dc3ced086a1b9.png)
逐片元操作(Per-Fragment Operations)&合并
而当图元通过光栅化阶段之后,从相机视点处看到的东西就可以在荧幕上显示出来。为了避 免观察者体验到对图元进行处理并发送到屏幕的过程,图形系统一般使用了双缓冲(double buffering)机制,这意味着屏幕绘制是在一个后置缓冲器(backbuffer)中以离屏的方式进行 的。一旦屏幕已在后置缓冲器中绘制,后置缓冲器中的内容就不断与已经在屏幕上显示过的 前置缓冲器中的内容进行交换。注意,只有当不影响显示的时候,才进行交换。
帧缓冲区
![](https://i-blog.csdnimg.cn/blog_migrate/320a3c4e8408fa46761a15c93ce8e821.jpeg)
裁剪测试(Scissor Test)
![](https://i-blog.csdnimg.cn/blog_migrate/5173dc82f3fbae65ba46251e3991eacd.jpeg)
在裁剪测试中,允许程序员开设一个裁剪框,只有在裁剪框内的片元才会被显示出来,在裁剪框外的片元皆被剔除。
透明度测试(Alpha Test)
![](https://i-blog.csdnimg.cn/blog_migrate/69a34837acf8c116f87bc19f2c4fa638.png)
![](https://i-blog.csdnimg.cn/blog_migrate/127ade3c74a1c3d40dcce9a3e8c4977d.png)
在透明度测试中,允许程序员对片元的透明度值进行检测,仅仅允许透明度值达到设置的阈值后才可以会绘制。在OpenGL3.1后这个API被删除了,但你可以在片元着色器中实现类似的效果。
模板测试(Stencil Test)
模板测试是一个相对复杂的测试。在模板测试中,GPU将读取片元的模板值与模板缓冲区的模板值进行比较,如何比较可以由程序员决定,如果比较不通过,这个片元将被舍弃。
位置关系
![](https://i-blog.csdnimg.cn/blog_migrate/2766eb91695b1d5d009fc63e6b942c9e.jpeg)
模板测试结果
![](https://i-blog.csdnimg.cn/blog_migrate/59cd1fe863a2f3638f13b529ac52513f.jpeg)
譬如图中两个有重叠区域的小球;令左侧小球模板值为3,右侧小球在非重合处模板值为2,重合处模板值为3。现进行模板测试,令左侧小球始终被绘制为红色,令右侧小球满足:模板值与缓冲区相等的绘制为绿色,否则绘制为蓝色。于是我们发现,右侧小球重合处的片元通过了模板测试,被成功绘制为绿色。
深度测试
深度测试是一个十分重要的测试。在深度测试中,GPU将读取片元的深度值(就是我们前面留下来的坐标z分量)与缓冲区的深度值进行比较,比较方式同样是可以配置的。用通俗的说法解释,深度测试允许程序员设置如何渲染物体之间的遮挡关系。
![](https://i-blog.csdnimg.cn/blog_migrate/0d2f66d379dab8226feac88ad4b59c1b.png)
如图,对于摄像机,尽管A小球在B小球的后方,但通过修改深度测试,我们让GPU把A没有被遮挡的部分隐藏了,反而让A被遮挡的部分显示出来的。
大量的被遮挡片元直到深度测试阶段才会被剔除,而在此之前它们同样地被计算,这占用了GPU大量的资源。因此有种优化技术是将深度测试提前(Early-Z)。但这带来了与透明度测试的冲突,例如某个片元甲虽然遮挡了另一个片元乙,但甲却是透明的,GPU应当渲染的是片元乙,这就产生了矛盾,这就是透明度测试会导致性能下降的原因。
如果一个片元通过了上面所有的测试,那它终于可以来到合并环节了。合并有两种主要的方式,一种是直接进行颜色的替换,另一种是根据不透明度进行混合(Blend),而混合操作同样是可配置的,程序员可以设定是把这两种颜色进行相加、相减还是相乘等等,有点像在PS里的操作。
![](https://i-blog.csdnimg.cn/blog_migrate/a9eee9428706a3386b1cda69f8e90e5d.png)
在经过上面的层层测试后,片元颜色就会被送到颜色缓冲区。GPU会使用双重缓冲(Double Buffering)的策略,即屏幕上显示前置缓冲(Front Buffer),而渲染好的颜色先被送入后置缓冲(Back Buffer),再替换前置缓冲,以此避免在屏幕上显示正在光栅化的图元。
//参考 技术美术百人计划,Games101第八课33分,unity shader入门精要,《Real-Time Rendering 3rd》提炼总结-毛星云
BV1Q54y1G7v3
BV1BE411N74b