基础名词解释
-
3D物体:由三角形组成的几何形状构成的物体
-
顶点:具有空间坐标和其他意义(颜色、纹理坐标)的点
-
纹理:映射到3D物体表面的图像,提供物体由某种材质构成的幻觉,物体定点存储的二维向量坐标,用于指定纹理如何映射到给定的表面
-
渲染管线:又叫渲染流水线,将数据从3D场景转换成2D图像,最终在屏幕上显示出来的总过程,其主要分为应用阶段、几何阶段、光栅阶段
-
片元:不是真正意义上的像素,而是很多状态的集合,这些状态用于计算每个像素的最终颜色,例如屏幕坐标、深度信息、几何阶段输出的顶点信息(法线、纹理坐标等)
-
渲染图元(render primitives):渲染所需的几何信息,点、线、面等,这些数据会传递给下一阶段——几何阶段
渲染流水线
渲染流水线分为三个阶段
1.应用阶段(Application Stage)
2.几何阶段(Geometry Stage)
3.光栅化阶段(Rasterizer Stage)
image.png
应用阶段(Application Stage)
三个任务:
1.准备好场景数据:摄像机位置、视锥体参数、场景中包含哪些模型、使用了哪些光源等等
2.粗粒度剔除:把不可见的物体剔除出去
3.设置模型渲染状态:包括材质、纹理、Shader等
输出内容
渲染图元(render primitives):渲染所需的几何信息,点、线、面等,这些数据会传递给下一阶段——几何阶段
由于应用阶段由CPU主导,并且下一阶段几何阶段是在GPU中进行,故而会涉及到CPU与GPU通信的问题,我们可知应用阶段的大致分为以下三步:
1.把数据加载到显存中
2.设置渲染状态
3.调用Draw Call
把数据加载到显存中
硬盘HDD ——>系统内存RAM ——>显存VRAM
所有渲染数据都需要先从磁盘中加载到系统内存,然后经过相关应用系统(Unity),将组织好的数据加载到显存中,主要是因为显卡对于显存的访问速度更快,且大多数显卡对于RAM没有直接的访问权限
设置渲染状态
渲染状态:定义场景中的网格是怎样被渲染,例如使用什么样的着色器、光源属性、材质
调用Draw Call
Draw Call:发起方位CPU接收方位GPU的指令,包含一个需要被渲染的图元列表
当给定了一个Draw Call的时候,GPU就会根据渲染状态和所有输入的顶点数据来进行计算,最终在屏幕上绘制出像素,此过程在GPU中进行,为GPU流水线
GPU流水线
在渲染流水线的后两个阶段,即几何阶段和光栅化阶段,GPU通过实现流水线化,加快了渲染的速度,但这些阶段由GPU来实现,我们无法拥有绝对的掌控权,但是其提供了部分配置性和可编程性
image.png
几何阶段(Geometry Stage)
几何阶段流程:顶点着色器 ——> 曲面细分着色器 ——> 几何着色器 ——> 裁剪 ——> 屏幕映射
接收参数:顶点数据
1.顶点着色器:可完全编程,通常用于顶点的空间变换、顶点颜色着色等功能
2.曲面细分着色器:用于细分图元
3.几何着色器:用于逐图元的着色操作或用于产生更多图元
4.裁剪:可配置,将不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元的面片
5.屏幕映射:把每个图元的坐标转换到屏幕坐标系中
顶点着色器
输入来自CPU,处理单位为顶点,本身不可以创造或销毁顶点,且无法获得顶点之间的互相关系,因此GPU可以利用相互的独立性并行优化每一个顶点,处理速度更快
工作内容:坐标变换和逐顶点光照、输出后续阶段所需数据
坐标转换
改变顶点位置(模拟水面、布料等效果)
把顶点坐标从模型空间转换到齐次裁剪空间,接着通常由硬件做透视除法,得到归一化设备坐标NDC
齐次裁剪空间:用于变换的矩阵叫做裁剪矩阵,也称为投影矩阵。裁剪空间的目标是能够方便地对渲染图源进行裁剪:完全位于这块空间内部的图元将会被保留,完全位于这块空间外部的图元将会被剔除,而与这块空间边界相交的图元就会被裁剪.
裁剪
图元跟摄像机视野的三种关系:
1.完全在视野内:继续传递给下一流水阶段
2.部分在视野内:进行裁剪处理,例如一条线段的一个顶点在视野内,而另一个顶点不在视野内,那么在视野外部的顶点需要用另一个新的定点来代替,这个新的顶点位于这条线段和视野边界的交点处
3.完全在视野外:不会继续向下传递
裁剪是不可编程可配置的(具体如何配置?)
image.png
屏幕映射
输入参数:三维坐标系下的坐标(范围在单位立方体内)
目的:把每个图元的x和y坐标转换到屏幕坐标系(二维坐标系)下
屏幕映射不会对z坐标做任何处理,屏幕坐标系和z坐标一起构成了一个坐标系,叫做窗口坐标系,这些值会一起传递到光栅化阶段,屏幕映射得到的屏幕坐标决定了这个顶点对应屏幕上哪个像素以及距离这个像素由多远
OpenGL:把屏幕左下角当成最小的窗口坐标值
DirectX:把屏幕的左上角作为最小的窗口坐标值
image.png
光栅化阶段(Rasterizer Stage)
输入数据:
1.屏幕坐标系下的定点位置
2.其他额外信息:深度值(z坐标)、法线方向、视角方向等
目的:
1.计算每个图元覆盖了哪些像素
2.为这些像素计算他们的颜色
三角形设置
计算光栅化一个三角形所需要的信息,通过上一阶段得到的顶点信息,得到三角形的表示方式,从而计算每条三角边上的像素坐标,它的输出为下一阶段的三角形遍历做准备
三角形遍历
检查每一个像素是否被一个三角形网格所覆盖,如果是的话,则生成一个片元(fragment),该阶段会使用三角网格3个顶点的顶点信息对整个覆盖区域进行插值
输出:一个片元序列
片元着色器(Fragment Shader)
可编程着色器阶段
输入:上一阶段对顶点信息插值的结果,根据从顶点着色器中输出的数据插值得到
输出:一个或者多个颜色值
这一阶段完成很多重要的渲染技术,其中之一有纹理采样,所需的纹理坐标,是在顶点着色器中输出每个顶点对应的纹理坐标,然后在光栅化阶段对三角网络的3个顶点对应的纹理坐标插值后,就可以得到其覆盖的片元的纹理坐标
局限性:仅可以影响单个片元,不可以将其执行的结果发送给其他片元
逐片元操作
渲染流水线的最后一步,高度可配置
目的:合并
主要任务:
1.决定每个片元的可见性,涉及深度测试、模板测试等
2.通过测试决定可以显示后,需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行合并,或混合
可见性判断的简单流程:
片元 ——> 模板测试 ——> 深度测试 ——> 混合 ——> 颜色缓冲区
测试的实际过程相较来说更为复杂,且不同图形接口(OpenGL和DirectX)的实现细节不尽相同
模板测试流程(高度配置)
开启模板测试后,GPU会首先读取(使用读取码)模板缓冲区中该片元的模板值,然后将该值和读取(使用读取码)到的参考值进行比较,比较函数是又开发者指定的,如果这个片元没有通过这个测试则会被舍弃,不管一个片元有没有通过模板测试,都会根据模板测试和深度测试的结果来修改模板缓冲区的,这个修改操作也是开发者指定的,例如,在失败时模板缓冲区不变,通过时将模板缓冲区对应位置的值加1等
应用:限制渲染区域、渲染阴影、轮廓渲染(如何应用的?)
深度测试流程(高度配置)
通过了模板测试之后才可以进入深度测试,开启深度配置后,GPU会把该片元的深度值和已经存在与深度缓冲区中的深度值进行比较,比较方法又开发者配置,通常比较函数式小于等于,即如果这个片元的深度值大于等于当前深度缓冲区的值,就舍弃。如果这个片元没有通过深度测试,就无法修改深度缓冲区的值,如果通过则可以通过开发者指定是否覆盖掉原有深度缓冲区中的深度值,通过开启/关闭深度写入来达到这个目的
合并/混合(高度配置)
如果一个片元通过了模板测试和深度测试就可以进入合并功能
需要合并/混合的原因
渲染过程是一个物体接一个物体画到屏幕上的过程,每个像素的颜色信息存储在颜色缓冲区,在执行某次渲染的时候,颜色缓冲区中有了上次渲染的结果,我们使用当前渲染得到的颜色完全覆盖掉之前的结果,还是其他处理,需要得到解决
不透明物体:通过关闭混合操作,让片元着色器计算得到的颜色值直接覆盖掉颜色缓冲区的像素值
半透明物体:使用混合操作来使这个物体看起来是透明的,GPU取出源颜色(片元着色器得到的颜色)和目标颜色(颜色缓冲区中的颜色),将两种颜色进行混合,混合方式是根据开发者配置的混合函数
Early-Z技术:将深度测试提前到片元着色器之前的技术,好处在于尽早地知道了哪些片元是被舍弃的,对于这些片元可以不需要再使用片元着色器进行计算,大大提高了GPU的渲染性能
网友评论