目录
定义
图形渲染管线(graphics rendering pipeline)的核心功能就是利⽤给定的虚拟相机、三维物体、光源等信息,来⽣成或者渲染(render)⼀张⼆维图像。
它分为四个阶段:应用阶段、几何处理阶段、光栅化阶段、像素处理阶段。其中每一阶段也由若干个子阶段构成。
【渲染速度的评价指标】
FPS:每秒的帧数
HZ:硬件刷新率 ,多用于硬件如显示器
应用阶段
将用户操作产生的等数据变成可以进行图形渲染的数据。这种数据称为渲染图元(rendering primitives),包括点、线、三角形。
应用阶段运行在CPU上,由于CPU的多核,也可以并行。有一些应用阶段的任务也可以通过使用计算着色器(compute shader)来让GPU执行。
该阶段进行:碰撞检测、某些剔除算法(视锥体剔除和遮挡剔除)和渲染管线剩余部分⽆法处理的⼀切问题
关于各类剔除算法与发生的阶段:不同几种剔除(Culling)在渲染流程中的使用总结
应用阶段之后的阶段均发生在GPU上,数据从应用阶段(CPU)进入后续阶段(GPU)开始渲染的过程,称为Draw Call。
几何处理阶段
可划分为:顶点着色、投影、裁剪和屏幕映射。
顶点着色
可编程阶段,体现为顶点着色器(vertex shader)。
该阶段的任务:
1.计算顶点的位置
2.计算那些开发⼈员想要作为顶点数据进行输出的任何参数,例如法线和纹理坐标等
对于任务1计算顶点的位置,需要将顶点进行Model-View变换;对于任务2,实际上是在为后续的像素着色阶段做准备,因为顶点着色的结果(可能是颜色、向量、纹理坐标或者其他类型的着色数据)会被发送到光栅化阶段中进行插值,并在像素处理阶段中用于计算表面的着色。
可选的顶点处理
当完成顶点处理之后,还有几个可以在 GPU 上执行的可选操作,它们的执行顺序如下:曲面细分、几何着色和流式输出。这些功能相互独立,看硬件功能和程序员的意愿按需使用。
曲面细分:将当前的顶点集合转换成更大的顶点集合,从而创造出更多的三角形
几何着色:也是产生新的顶点,常用来生成粒子,做烟花效果等
流式输出:可以把进行到此处的数据传回CPU输出,不再走之后的流程。
位于顶点着色阶段后,裁剪阶段之前。
投影
分为透视投影和正交投影,一般和model-view变换合称model-view-projection变换。
裁剪
对那些一部分位于可视空间内,一部分位于可视空间外的图元进行裁剪,保留其位于可视空间内的部分,丢弃之外的部分。
屏幕映射
将标准立方体(NDC)放缩到和屏幕尺寸所匹配,顶点坐标对应改变。改变后的坐标将传入后续阶段。
光栅化阶段
目标:找到位于待渲染图元中的所有像素值。这一过程称为光栅化。
光栅化可以被认为是⼀个几何处理阶段和像素处理阶段之间的同步点,因为光栅化阶段的三⻆形,是由几何处理阶段输出的顶点组成的,并且最终会输出到像素处理阶段中。
分为两个阶段:三角形设置(图元装配)和三角形遍历。
三角形设置
由固定功能的硬件实现,把顶点数据收集并组装为简单的基本体,计算三角形的微分、边界方程等可以用于三角形遍历中的数据。
三角形遍历
对每个被三角形覆盖的像素进行检查,并生成一个对应的片元(fragment)。并且会对三角形三个顶点上的属性进行插值,来获得每个三角形片元的属性。
这一阶段牵扯到了抗锯齿(Anti-aliasing)操作。因为不管用什么划分片元的方法,三角形边缘部分总会显得很锐利。当然,程序员们也想出了各种各样的抗锯齿方法来解决这个问题,譬如多重采样抗锯齿(MultiSampling Anti-Aliasing,MSAA),这种抗锯齿方法对中心点不在三角形内的边缘处采用不同程度的浓度进行计算。
像素着色阶段
在图元内部进行逐像素(或逐样本)的计算。
分为两个阶段:像素着色和合并。
像素着色
可编程阶段,对应像素着色器(或称片元着色器)。
可进行一切的逐像素着色计算,包括纹理贴图等。
合并
负责解决可见性问题。
主要的测试有:裁剪测试、透明度测试、模板测试以及深度测试。这个阶段是高度可配置的。
裁剪测试:在裁剪测试中,允许程序员开设一个裁剪框,只有在裁剪框内的片元才会被显示出来,在裁剪框外的片元皆被剔除。
透明度测试:在透明度测试中,允许程序员对片元的透明度值进行检测,仅仅允许透明度值达到设置的阈值后才可以会绘制。
模板测试:模板测试是一个相对复杂的测试。在模板测试中,GPU将读取片元的模板值与模板缓冲区的模板值进行比较,如何比较可以由程序员决定,如果比较不通过,这个片元将被舍弃。
深度测试:深度测试是一个十分重要的测试。在深度测试中,GPU将读取片元的深度值与缓冲区的深度值进行比较,比较方式同样是可以配置的。用通俗的说法解释,深度测试允许程序员设置如何渲染物体之间的遮挡关系。
问答
1.顶点在做完MVP变换后,变换到NDC的过程
顶点在做完MVP变换后,视锥体已经被压成了一个长方体,这称为裁剪空间。这是一个齐次空间,也就是(wx,wy,wz,w)。在这个空间中,进行裁剪,将图元中不在视锥体内的部分抛弃。然后进行透视除法,也就是都除以w分量,视锥体也就从长方体变成了标准正方体,就从裁剪空间转换到NDC了。
2.Z-buffer与Early-z的区别?各自的作用
z-buffer和early-z都是消隐算法,但它们发生的阶段不同。首先介绍z-buffer。
NDC中顶点的z值经过光栅化阶段的插值后,每个片元都拥有了一个z值。z-buffer是一个二维数组,它的大小与渲染窗口大小一致,存储了每个像素所对应的片元中目前最小的z值。一开始,z-buffer初始为最大值。然后遍历所有片元,如果当前片元的z值比z-buffer中该像素的z值小,就更新z-buffer和颜色缓存,表示该片元可以被看见;如果当前片元的z值更大,就不更新z-buffer和颜色缓存,表示该片元被之前的片元遮挡,看不见。z-buffer的时间复杂度为O(n),n为需要被渲染的片元数。这一过程发生在片元着色后的合并阶段。
由于z-buffer发生在渲染管线末尾,被遮挡的片元仍然在片元着色器中进行了着色。为了避免这种浪费,许多GPU会在光栅化后、像素着色前进行early-z,使得在像素着色前就去除了不可见的片元。early-z的内容与z-buffer(为了与early-z区别,合并阶段中的这个也被称为late-z)完全一致,但是存在着缺点:一旦进行了手动写入深度值、开启alpha test或者丢弃像素等操作,那么gpu就会关闭early-z直到下次clear z-buffer后才会重新开启,这是因为上述那些操作可能会在片元阶段与late-z阶段之间修改深度缓存中的深度值,导致提前的early-z的结果并不正确。当然,也可以强制打开early-z。
Alpha Test为什么会和Early Z冲突:一个片元可能通过了Early Z并更新了深度阈值,但又在Alpha Test中被视作全透明,这就导致一个透明的片元会挡住后面不透明的片元。