UE4渲染模块学习笔记
注:转载自知乎文章:https://zhuanlan.zhihu.com/p/57561144
本文仅为个人阅读笔记,归纳整理。
优化的方法论
图形工程师一直在追求渲染质量、性能、功能这三者的平衡。一个空场景性能必然是最佳的,但这没有意义,另一方面若场景都用最高精度的图片或是堆砌各种功能点,游戏卡得根本玩不了,也是不行的。
在图形优化的路上有很多具体的实现,但都可以归纳成以下六点方法论:
1)流水线:并发多线程,使硬件的利用率达到最高;
2)预计算:能事先计算的就尽量事先做,降低Runtime的计算成本,比如静态光的lightmap;
3)局部化:只在可见的局部区域里做事情,比如屏幕空间反射的计算(SSR);
4)先粗后精:先用消耗低的方法缩小计算范围,再用消耗大的方法执行精确计算,比如八叉树遮挡查询;
5)时空互换:用时间换空间或是空间换时间,比如lightmap就是空间换时间的例子;
6)负担转移:使用stat unit查看如果是CPU的瓶颈,又不可进一步优化了,可以考虑将CPU的计算移至GPU计算,如骨骼动画计算可以移至GPU做。
DrawCall Example
所谓drawcall,是指CPU向GPU发出的一次渲染指令,让GPU绘制指定的几何体。
CPU会提交六次drawcall,一次天空,一次地面,左侧两个柱子各一次(因为是三个独立的模型),最右侧柱子上下两部分采用了不同的材质,所以需要提交两次渲染信息,这样就多一次drawcall,共计6次drawcall。这张图展示了渲染的顺序,蓝色的是地面,因为事先做了prez,所以为地图上原来柱子的区域留空了没去绘制。
从左到右依次是:渲染地面,渲染右侧柱子的下半截,渲染中间柱子,渲染左侧柱子,渲染右侧柱子的上半截,渲染天空。
渲染顺序也就侧面反映了提交drawcall的次序,其实对于图形工程师而言,不必特别注意drawcall提交次序,只要最终结果是对的就行。不过这里也可以发现一个问题,那就是为啥没有完整地渲染完右侧的柱子,再去渲染中间的呢?那是因为渲染器会把相同材质的对象放到相邻drawcall中处理,这样可以节省让硬件切换渲染状态的耗时。可以注意到这里右侧柱子下半截的材质与其他柱子材质是一样的,所以会放到连续的drawcall中处理,而右侧柱子上半截材质不同,被放到了所有相同材质柱子渲染完成后才进行绘制。
材质应用流程
.usf模板文件中提供了基础的材质变量和函数,这些不能改变,但同时也把可以变化的代码段留了空,这时开发同学就可以利用材质编辑器,自动将图形化的连线转成HLSL代码插入到.usf文件里(print到.usf中的%s处):
反射
- 反射捕获(Reflection Capture)
- 平面捕获(Planar Reflection)
- 屏幕空间反射(SSR)
屏幕空间反射(SSR)
待补充
光照
静态光照
- lightmap存储静态光的直接光分量
- 采样点存储间接光分量
动态光照
动态阴影
- 常规动态阴影(Regular Dynamic Shadows)