参考书籍:UNITY SHADER入门精要
一、认识流水线
将一道工序,分解成多道工序同时运作。
辅助理解:一个人生产一个玩具,需要经过 “部件制作->部件组装->包装” 三步流程。假设每道工序需要耗时1个小时,则完成一个玩具需要4个小时。流水线是将三道工序分别交由三个人各负责一道工序进行,每个人需要完成各自的流程工作即可。如下图所示:
假设我们需要完成3件玩具的制作:
- 流水线工序时间:需要 5小时(3+1+1)
- 传统工序时间:需要12小时(4+4+4)
下图为流水线工序,时间计算从第一件商品开始至最后一件商品完成。
注意点:流水线中,三个流程是同时进行的。
所以,流水线使得整道工序的效率更高。
二、认识渲染流水线
2.1 什么是渲染流水线?
计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可见的图像。这项工作由CPU与GPU共同完成。
2.2 渲染流程
- 应用阶段
由CPU负责实现,主要准备场景数据,如摄像机位置、视椎体、场景模型数据、光源灯。为提高渲染性能,需求准备粗粒度剔除(Culling)工作,用于剔除无法看见的物体。以及设置每个模型的渲染状态。
简言:此阶段需求渲染图元(渲染所需的几何信息) - 几何阶段
由GPU负责进行,主要对各 渲染图元 进行逐顶点、逐多边形的操作。
简言:将顶点坐标变换成屏幕空间的二维坐标 - 光栅化阶段
由GPU运行,将上一阶段传递的数据产生为屏幕的像素,来渲染最终图像。
简言:决定各渲染图元中哪些像素应被绘制到屏幕上
2.3 渲染状态
解释:定义场景中的网格以怎样方式被渲染。
如图解释:
三、GPU流水线
- GPU渲染流水线的输入:顶点数据
- 顶点着色器:实现顶点的空间变换、顶点着色等功能
- 曲面细分着色器:(可选着色器)细分图元
- 几何着色器:(可选着色器)执行逐图元的着色操作,或被用于产生更多图元
- 裁剪:剔除不在摄像机视野内的顶点,剔除某些三角图元的面片
- 屏幕映射:将各图元坐标转换为屏幕坐标【不可配置和编程】
—————阶段分割线————— - 三角形设置:固定函数
- 三角形遍历:同上
- 片元着色器:实现逐片元的着色操作
- 逐元着色器:修改颜色、深度缓冲、混合等。【不可编程,可配置】
3.1 顶点着色器
主要任务:坐标变换、逐顶点光照,准备后续阶段的需求数据。
- 坐标变换:改变顶点位置模拟水面、布料等。
前提必须完成的工作:将顶点坐标从模型空间转换到齐次裁剪空间。
代码中以o.pos = mul(UNITY_MVP, v.positioni);
出现。(坐标限制[-1, 1])
3.2 裁剪
主要任务:剔除摄像机无法看见的面、顶点,减缓后阶段GPU的工作压力,提高渲染性能。
- 剔除(Culling):代码中以Culling Font/Back/Off
出现,分别表示剔除 正面/背面/不剔除。
值得注意的是,这类代码仅为配置,无法通过编程控制裁剪过程。
3.3 屏幕映射
主要任务:将各图元的坐标转换成屏幕坐标系(二维,与显示器分辨率有关)。
- 三维坐标的Z坐标不会被处理
- OpenGL 与 DirectX 之间的坐标存在差异,如下图所示:
3.4 三角形设置
主要任务:计算边界的像素信息,获得三角形边界
辅助理解:计算几何阶段传入的信息,查找三角网格的像素为止
3.5 三角形遍历(又称"扫描变换")
主要任务:查找哪些像素被三角形网格覆盖
辅助理解:计算三角网格中各网格的颜色深度
3.6 片元着色器(又称"像素着色器")
实际上,此时的片元≠真正的像素
主要任务:实现多渲染技术(如:纹理采样)
局限性:仅影响单个片元,即执行片元着色器时,不可将自身任何结果发送给其领周围片元。
例外:片元着色器可访问到导数信息
3.7 逐片元操作(又称"输出合并阶段")
主要任务:
- 决定各片元的可见性(例如:深度测试、模板测试)
- 将通过所有测试的片元的颜色值与存储在颜色缓冲区的颜色进行合并(混合)
流程图:
3.7.0 模板测试与深度测试(流程图)
3.7.1 模板测试
原理:GPU读取模板缓冲区该片元位置的模板值
目的:限制渲染区域(指定比较函数,>、<、≤、≥指定值时舍弃该片元)
用法:渲染阴影、轮廓渲染
3.7.2 深度测试
前提:通过了模板测试
原理:GPU将深度值与已存在于深度缓冲区的深度值比较(通过值可覆盖原有深度值)
用法:透明效果
3.8 混合(Blend)
适用范围:透明或半透明物体。
- 不透明物体可关闭Blend操作——片元着色器计算的颜色值直接覆盖颜色缓冲区的像素值
- 半透明物体需要混合操作
流程图:
混合功能:可选择 开启/关闭
四、Draw Call
4.1 什么是Draw Call?
答:CPU调用图像编程接口。
4.2 造成性能问题的元凶是谁?
答:CPU。
解释:CPU添加命令至 “命令缓冲区” ,GPU依次读取命令进行缓冲。值得注意的是当GPU完成一次渲染工作后,才会取出下一命令继续执行。
4.3 为什么Draw Call多了会影响帧率?
答:CPU的完成需要很多准备工作,但GPU的渲染执行能力却快于CPU,造成CPU耗时长的提交渲染信息与命令。出现CPU过载,GPU闲置工作的情况。
4.4 如何减少Draw Call
答:批处理。
适用范围:
- 不会移动的地面、岩石等(参照 Unity的静态批处理)
- 飞行的禽兽,鱼等(对动态的批处理)。
注意:由于这些物体时刻运动,需每帧重新合并后发送至GPU,对空间和时间均有一定程度影响。
其他注意事项:
- 避免使用大量很小的网格。(出现不可避免情况,需考虑是否可以合并)
- 避免使用过多的材质。(不同网格之间尽可能公用同一材质)