渲染是什么?
渲染是CPU将数据交给GPU计算的过程。
数据有哪些?
数据类型:
- 固定类型数据。顶点float3,uv float2(4),等。
- 固定用途数据。PBR Albedo(固有色)等。
- 自定义数据。
- 模型参数:顶点,三角面,法线,切线,变换矩阵,UV,顶点色
- 材质参数:(颜色,纹理,金属的,粗糙度等)参数是动态的,可以是无限的。
- 引擎内置参数:框架底层为每个shader声明的参数,框架底层为参数赋值(例如,深度缓冲,阴影缓冲)[通过变体等多种方式,可以理解为
固定参数
] - 全局参数:[
固定参数
]
- shader: 计算公式。
我们可以这样理解: GPU 通过 shader
计算公式,带入参数(模型参数
=> 固定参数,上文列出,无论shader是否使用,都会传送到GPU
,材质参数
=> 动态参数,任意个数和类型
),计算得出一张图片(RenderTarget)。
所以,只要shader和参数匹配,理论上我们可以通过添加无限的材质参数,来实现任意效果。
我们所担忧的地方?
因为性能关系,我们希望shader尽可能复用:
只要材质参数
相同,即参数个数相同,类型相同(因为材质参数和shader必须匹配,所以shader相同,个数和类型必定相同),并且参数的值相同
,这样我们就能进行批处理,与模型参数无关
(这是由GPU流水线,DX OPENGL,或者硬件架构决定的,具体由什么决定我不清楚),大大提升性能。
一个比喻性理解,可能不正确:一次渲染batch,就是用shader和材质参数
一起搭建起一个渲染管道,shader是水管,材质参数
就是连接头,搭建好以后,调用drawcall,模型参数
顶点面片就是像水流流进管道,输出结果。如果多个模型shader和材质参数
相同,那么不用重新搭建管道,可以一起输入多个模型,这就是批处理。
但这导致一个问题,多个模型使用相同的参数,那么看起来都一样(一样的金属光泽,一样的粗造度),太过单一。
那能不能即保留多样性,同时又进行批处理呢?
- 如果想进行批处理:
材质参数
必须相同。 - 如果想每个模型表现不同:shader 必须获得不同参数值。
我们由多种方法来达到这种效果(^_^):
- 将每个实例的参数封装到
模型参数
:模型参数支持8个UV,每个都是float4类型(uv012通常已经被使用),顶点色float4,通过这个方法将参数传递到shader。例如:使用顶点色R通道表示顶点受风力影响的强度。 - 使用额外的缓冲区,可以是一张全局Texture,也可以是已经渲染完成Pass的结果,这样后续Pass就可以直接使用。
- 例如:
- 深度贴图 实现阴影
- 通过定义一个全局贴图来实现战争迷雾
- 一个额外的深度贴图,实现半透明彩色阴影
- 全局参数:可以实多维数组使用模型坐标做Key设置好参数,shader中通过模型坐标取即可。
任何不通过材质参数
将参数传递到shader的方法都可以。
从这个角度理解PBR?
纹理贴图是动态参数,材质上还有各种各样的参数和贴图计算,得到最终结果。
简单理解为rt = tx * p1 + tx * p2 + tx * pn
贴图和参数都是任意的,所以完全无非控制最终结果,只能靠美术慢慢调。
所以PBR就是将问题 由动态参数,转变 为半固定参数。
贴图不能想顶点,法线一样固定数据大小和格式,因为贴图可以用分辨率控制效果精度。
所以从贴图的数据属性来定义了一组标准贴图,就是PBR。
属性:
- Albedo(固有色):物体在无光照情况下材质原本具有的颜色
- Metalness(金属性/金属度):使用黑白灰颜色来定义材质类似金属的程度(白色为完全金属,黑色为非金属)
- Roughness(粗糙度):使用黑白颜色来定义材质的粗糙度(白色为完全粗糙,黑色为光滑)
其实这些属性也可以向顶点色一样设计为每顶点数据。变成固定参数。“mesh.Albedo,mesh.Metalness”,将这些都写进mesh里去。但是后来随着性能提高和对效果的要求,顶点色进化成了纹理采样。技术没有倒着走的道理。 这里只是想表明。将数据传递到渲染流水线的方式多种多样。
通过定义标准,我们就有了有规则的参数。艺术家产出素材时,不必为每个特殊效果额外制作贴图。通过增加shader参数,通过不同算法,就可以得到不同效果。
有了固定属性的贴图,所以产生了符合现实世界的通用型standard材质shader。
有了通用性的ToonShader。
相同的模型和贴图。通过更换shader就可以转换风格。
当然,如果要求的效果越特殊,最终还是会回到手绘贴图的老路上。