Unity Shader之渲染流水线
前言
- 为什么要增加这个栏目?
由于个人目前主要学习Unity引擎,在引擎Shader的学习中,学了很多,但是不解的和不会的反而越来越多,借此机会自我总结一下,也为后续学习理清思路,该栏目将会随时间迭代更新。
- 该栏目有哪些内容?
我个人目前的想法是从我的学习路线入手。Unity Shader入门精要 -> Catlike Coding -> 案例综合
- 环境
- 软件: Unity
- 着色器语言: ShaderLab (CG/HLSL)
Unity Shader之渲染流水线
什么是渲染流水线?
由CPU、GPU共同完成,使用三维数据(顶点,法线等)将三维物体渲染成屏幕上的二维图像
流水线的概念早已学过,那么渲染流水线又是什么。首先要区别于GPU(硬件)流水线。
分为三个概念上的阶段,每个阶段又有子流水线。
应用阶段(application):开发者支配的阶段,输入场景、贴图、摄像机、光源等等;输出点、线、三角面等渲染图元。CPU上。
几何阶段(geometry): 这个阶段主要决定渲染哪些图元,以及将这些图元映射到二维平面上,最后输出包括屏幕上的着色,深度值,坐标等等到光栅器。
光栅化阶段(rasterization): 这个阶段同上一个阶段都是在GPU上进行,利用上一阶段的数据绘制出像素。先是逐顶点处理(对坐标,颜色逐顶点插值),然后是像素处理。
CPU和GPU之间的交互流程是怎样的?(Application)
之前提到渲染流水线是在CPU和GPU上进行。我们知道应用阶段在CPU上进行,下面是应用阶段CPU与GPU的交互流程。
CPU控制数据从HDD(硬盘)中加载到显卡的显存(VRAM)中,数据包括位置、顶点着色、法线方向等。
通过CPU指定渲染状态,指导GPU使用哪个顶点/片元着色器,使用哪个光源属性、材质等。
准备好后CPU发起Draw Call命令,GPU根据渲染状态开始渲染。
上面三个阶段为应用阶段整个CPU和GPU交互流程。
什么是Draw Call
有一个命令缓冲区,用于CPU和GPU的并行处理。
Draw Call是其中的一道命令,用于发送渲染命令。下图中黄色框就是渲染命令Draw Call,红色是改变状态的命令。
尽量减少Draw Call
提交一次Draw Call需要提交数据,状态,命令等,如果一次能够绘制完,就不要分多次Draw Call.
类似于复制10MB的文件很快,但是复制10000个1KB的文件却很慢。
如何减少Draw Call是游戏优化中的一个重要命题。后续会以“优化”为主题详细讨论。
GPU流水线(Geometry&Rasterization)
已知几何与光栅化阶段在GPU上进行。以下为总览图。
几何阶段
- 顶点着色器(Vertex Shader)
负责顶点
几何变换
和顶点着色
曲面细分(Tessellation Shader)、几何着色器(Geometry Shader)均为可选着色器,前者用来细分图元,后者用来执行图元着色操作和产生图元(增加)。
裁剪
位置分为完全在相机内,部分在相机内,完全不在相机内
裁剪用来处理部分在相机内的部分,将与相机交点替代相机外的部分。
需要注意的是顶点着色器是可编程的,但裁剪却是不可编程的,是硬件上的固定操作。
- 屏幕映射
输入三维坐标,映射到二维屏幕空间,z不做处理
光栅化阶段
- 三角形设置
已知顶点着色器不会记录点与点之间的关系
该过程用来得到三角形边界表达方式,计算像素覆盖情况。
- 三角形遍历(Triangle Traversal)
找到覆盖的像素,生成片元。
一个片元并不是真正意义上的像素,而是包含了很多状态的集合 这些状态用于计算每个像素的最终颜色
对三个顶点进行插值,然后三角形内部颜色由三点渐变(gradient)混合而成。
- 片元着色器(Fragment Shader)
DX中又称为像素着色器(Pixel Shader),不过片元不是真正意义上的像素,可编程
纹理采样,对三个顶点插值后,就可以得到片元的纹理坐标,仅可以影响单个片元,无法传递给邻居
- 逐片元操作(Per Fragment Operation)
主要进行修改颜色,深度缓冲,混合操作
为OpenGL的说法,DX具体较复杂
片元着色器执行后,进行模板测试(Stencil Test),再开始深度测试(Depth Test)
模板测试是根据比对模板值和reference value来判断(可设置)是否舍弃片元。
深度测试则是比对深度,保留深度小的片元。
混合(Blend)操作,新渲染得到的颜色与颜色缓冲中的已有值做取舍。没有开启混合,计算得到什么,片元得到的就会是什么,也即不透明物体。如果开启了混合模式,会和原有颜色进行插值混合,表现为半透明。
现代GPU硬件特性,深度测试可以在片元着色器之前,但是不能同时。
补充
【显卡中除了图形处理单元GPU,还有显存(VRAM)】。
光栅化是一个过程,需要时间,为了得到连续图像,采样双缓冲机制(Double Buffering),Back Buffer计算新场景,Front Buffer显示当前场景,交换循环。
OpenGL、DX是CPU和GPU之间的桥梁,图形接口(api) (严格来说OpenGL不是图形api?,但是可以这么理解)
更详细地说
OpenGL没有提供着色器编译器,显卡驱动自己完成编译工作,GLSL的跨平台性在于编译结果依赖于硬件本身,而非局限于操作系统。不同显卡供应商的实现有所不同。
HLSL是针对微软产品的。不依赖于显卡驱动。
CG是跨平台的,根据不同平台翻译成中间语言,由于合作关系,和HLSL很像。但是可能无法发挥OpenGL新特性。
vv
提到了OpenGL和DX,就不得不提到Unity本身的跨平台性,两者坐标系不同,但是Unity自身提供翻转(开了抗锯齿+渲染到纹理就失效)
最后提一嘴,固定渲染管线由于对开发者极弱的开放性,渐渐退出历史舞台。