渲染流水线的工作任务在于,由一个三维场景出发,生成(或叫渲染)一张二维图像。换句话说,计算机需要从一系列的顶点数据、纹理信息出发,把这些信息最终转换成一张人眼可见的图像。
这个过程通常是由CPU和GPU共同完成的。
1. 三个概念阶段
2. 应用阶段
在CPU中进行
渲染流水线的作用简单用一个字概括就是“画”,应用阶段作为渲染流水线的起始阶段,其主要作用就是准备好“画”的材料并通知“开始画”。
因此,应用阶段的主要工作可以概括为以下三个方面:
- 准备要画的内容——主要针对Mesh,准备顶点信息,顶点位置、法线、uv坐标等,并上传ObjectBuffer
- 准备画的材料和方法——主要针对Material,设置渲染状态,设置shader、设置光源属性、设置纹理、上传MaterialBuffer等
- 通知开始画——调用DrawCall,由CPU调用图形绘制接口,如glDrawElements
2. 几何阶段
在GPU中进行
几何阶段操作的目标是应用阶段提交的顶点信息,其最重要的作用包括四个:
- 逐顶点计算(如顶点光照)
- 将顶点坐标转换到齐次空间
- 裁剪无效顶点
- 将顶点坐标映射到屏幕空间
几何阶段细分
几何阶段中最重要的三个细分阶段分别为顶点着色器、裁剪、屏幕映射,而曲面细分着色器和几何着色器均为可选阶段。
顶点着色器
- 可编程
- 对每个由CPU提交的顶点进行并行处理
- 不可以增减顶点,也没有顶点间的关系数据
- 可以进行顶点变换、顶点光照等
- 最重要的作用是将顶点坐标从模型空间转换到投影空间(齐次剪裁空间)
裁剪
- 不可编程,但可以配置
- 依据顶点的齐次坐标裁剪掉摄像机范围以外的顶点
屏幕映射
- 将顶点的齐次坐标映射到二维的屏幕空间中
- 经过屏幕映射,我们可以得到每个顶点对应在屏幕上的像素坐标,以及该顶点与屏幕的距离(深度)
- 注意OpenGL的屏幕空间是左下到右上,即左下角为(0,0)右上角为(maxX,
maxY),而DX的屏幕空间是左上到右下,即左上角为(0,0)右下角为(maxX, maxY)
3. 光栅化阶段
在GPU中进行
经过几何阶段的处理,顶点坐标已经转化为屏幕上的像素坐标,这样就得到了一系列像素坐标以及这些像素坐标对应的光照、法线、深度等信息,我们可以将其称为“片元”。
然而我们从几何阶段获得的片元只是模型三角面顶点对应的片元,为了能够正确完整地将模型绘制在屏幕上,还需要找出这些三角面内部覆盖到的像素坐标以及这些像素坐标对应的光照、法线等等绘制需要的信息,也就是需要生成三角面内部的片元。
光栅化阶段就是从顶点对应的片元信息出发,生成三角面内部片元并最终生成每个片元的颜色信息的阶段。
光栅化阶段细分
三角形设置
- 根据三角形的顶点确定三角形的边界
- 最终确定三角形覆盖范围的表示
- 是一个构建三角形的过程
三角形遍历
- 计算上一步构建出的三角形网格覆盖到了哪些像素
- 用三角形顶点的各种信息进行线性插值,得到其中覆盖的像素对应的各种信息
- 是一个通过顶点片元插值得到三角面内每个片元的过程
片元着色器
- 可编程(对应shader里的#pragma fragment XXX)
- 对每个被三角形覆盖的片元进行并行处理
- 没有片元之间的关系数据
- 通常会在片元着色器进行各种纹理采样(有时候根据需要也可以直接在顶点着色器采样)
- 输出片元的颜色值用于更新对应的渲染目标(颜色缓冲区或者Render Texture)
输出合并
- 逐片元操作
- 不可编程,但可配置
- 经过模板测试、深度测试等对片元的可见性进行测试 舍弃不可见的片元
- 按照配置的混合模式对可见片元进行颜色混合
4. 一些补充信息
深度测试
- 针对指定片元对应的像素坐标读取深度缓冲区中记录的深度值
- 对片元本身的深度值和从深度缓冲区中读取的深度值进行比较
- 比较的方式可以在shader中进行配置
- 不满足比较结果的片元视为未通过深度测试,舍弃该片元,满足比较结果的片元视为通过深度测试,进入后续流程
- 未通过深度测试的片元不会更新深度缓冲区中的深度值
- 通过深度测试的片元要看shader中是否开启了深度写入,只有开启了深度写入的情况下才会更新深度缓冲区中的值
Early-Z
上面提到深度测试在片元着色器之后进行,但是对于那些没有通过深度测试而舍弃的片元来说,其在片元着色器中的计算完全是没有必要的,因此为了节省这部分性能,可以把深度测试提前,在经过三角形遍历之后,先进行深度测试,只有通过了深度测试的片元才会进入片元着色器进行计算,从而提高效率
但是如果开启了透明度测试,可能出现某一片元先通过了深度测试将其深度写入了深度缓冲区,但该片元可能因为没有通过后续的透明度测试而被舍弃,最终导致深度缓冲区中记录的深度值发生错误
因此,在开启透明度测试的时候不能进行Early-Z,这也是透明度测试会影响效率的原因
透明度测试会在后面介绍
双缓冲机制
我们在屏幕上看到的图像,反映的是颜色缓冲区中的颜色值
在没有中间渲染目标的情况下( 比如Render Texture),光栅化阶段的输出结果会直接写入颜色缓冲区中
此时如果屏幕将颜色缓冲区中的颜色实时显示出来,就会由于渲染流水线中间状态的颜色更新导致画面错误
双缓冲机制通过设置前置缓冲区和后置缓冲区两个颜色缓冲区来解决这个问题
屏幕上显示的颜色始终是前置缓冲区中的颜色
在渲染流水线过程中的颜色更新只写入后置缓冲区,只有当渲染完全结束之后,才会将前置缓冲区和后置缓冲区进行对换,将流水线渲染的结果显示到屏幕上