划水大半年,还没正式开始做项目。简单记录一下最近看的ue4实时渲染(RTR)原理,以后在实际项目中应该能用到。当然,以下内容只是我对虚幻4实时渲染的初步理解,可能会有一些认知上的不足导致理解有误。
实时渲染有两种实现方式,延迟渲染(Deferred Rendering 适用于复杂的应用)和正向渲染(Forward Rendering 适用于简单的应用)。在ue4中默认使用的是延迟渲染,以下内容也是基于延迟渲染的。
一、渲染之前(Before Rendering)
在GPU渲染之前,由CPU先做一些优化。
1.距离剔除(Distance Culling)
当对象尺寸足够小,距离镜头足够远时,可以不进行渲染,以提高性能。在UE4中,可以使用 剔除距离体积(Cull Distance Volume)来设置距离剔除。
2.视锥体剔除(Frustum Culling)
在视锥体之外的对象是默认不会被渲染的,这个不需要我们再做什么了。
3.预计算可见性(Precomputed Visibility)
提前计算出摄像机在各个位置时,actor的可见性,并存储在场景中。在运行时,当摄像机移动到某一区域,可以立即获取到当前状态下各个actor的可见性。适用于中小型、静态对象较多、摄像机移动区域受限的场景,用空间换时间。在UE4中开启 世界场景设置(World Settings) 预计算可视性(Precomputed Visibility)后,使用 预计算可视性体积(Precomputed Visibility Volume)来实现这一功能。
4.遮挡剔除(Occlusion Culling)
一叶障目,不见泰山。计算出被完全遮挡的对象,然后剔除渲染。在 项目设置->Engine->Rendering->Culling->Occlusion Culling 默认开启。
二、几何体渲染
这个环节要注意的就是绘制调用(Drawcall)次数,性能的控制和优化就在于绘制调用的次数。一组相同属性的多边形是一次drawcall,一个材质是一次drawcall,这里能做的就是合并模型减少绘制调用。
但是,合并模型会影响遮挡剔除、光照贴图、碰撞检测使他们更加复杂,还会增加内存开销。因此合并模型遵循以下原则:
1.使用次数多,面数少的网格体。(可以在 **窗口->统计数据** 中筛选)
2.在同一空间内。(不同空间的网格体合并后,会使遮蔽剔除和碰撞检测变得复杂)
3.拥有相同的材质。(不同材质会导致多次绘制调用,合并了也不会有改善)
4.不需要碰撞或者碰撞体积简单。
5.体积非常小或只接受动态光照。
6.距离摄像机很远。(可以使用HLOD来实现)
另外,对于草地一类的需要大量重复绘制的网格体,可以使用实例化网格体组件(InstancedStaticMesh)来实现。
利用顶点着色器可以实现水面、风吹草地的动画效果,在材质编辑器中修改 世界坐标偏移。
三、光栅化和G-Buffer
三维模型最终要渲染到二维的屏幕上,三维到二维的转换就叫做光栅化(Rasterizing)。
模型是由一个个三角形面构成,光栅化就是要将一个个三角形对应显示在一个个像素点上,在这里像素点是以2 * 2矩阵为最小单位进行计算的,因此即使一个矩阵中只有一个像素点对应上了三角形,那其他三个点也会参与一次计算。如果一个矩阵的4个点分别对应着4个三角形,此时就会有2 * 2 * 4次计算,4个像素点执行了16次计算,这就是 过度着色(Overshading)。
在ue4中可以使用 视图模式->优化视图模式->四边形过度绘制 来查看场景中的过度着色。
模型距离摄像机越来越远时,近大远小,他在屏幕上的绘制区域也会变小,三角形的密度就会变大。这就可以使用距离剔除和LOD来进行优化。
光栅化之后,就开始渲染图像了,这些图像就是GBuffer。从这时开始的计算,都不在基于几何体了,而是基于GBuffer。
GBuffer是一组图像,他包括静态光照贴图、法线贴图、金属色组合贴图(R:金属性,G:高光,B:粗糙度)、纹理贴图、深度缓冲等10张图像。如果你的游戏是60fps,那么每秒就会有600张图像被渲染和传输。
四、纹理
纹理在导入ue4时会被自动压缩,在纹理的细节面板中可以修改压缩设置。
如果纹理的宽高都是2的指数幂,那么ue4会在导入纹理时自动生成多级渐进纹理,多级渐进纹理可以有效的改善模型缩小时产生的噪点。
当带宽或纹理池大小不足时,会优先加载多级渐进纹理中的低分辨率纹理。
五、着色器和材质
着色器是一段运行在GPU上的程序,像素着色器用来计算每个像素点的值,广泛应用于渲染的每个步骤。材质系统、光照、雾、反射、后处理、颜色校正等都是有像素着色器实现的。
着色器由着色语言(Shader Language)编写,像OpenGL的GLSL,DirectX的HLSL。在ue4中,使用HLSL编写着色器,再通过ue4的跨平台编译器HLSL cross compiler实现跨平台。
ue4提供了很多HLSL的模板代码,从而支撑起一个可视化材质编辑器。通过在材质编辑器中简单拖拽编辑,然后编译成着色语言。
影响实时渲染速度的主要因素:
1、几何体渲染中的绘制调用次数
2、像素着色器
3、半透明
4、动态阴影
像素着色器的渲染很慢,因为涉及像素着色器的工作内容太多了。分辨率越高,材质越复杂,影响也就越大(消耗 ≈ 像素点数 * 材质复杂度)。使用 视图模式->优化视图模式->着色器复杂度 可以查看材质的复杂度。
六、反射
1、反射采集(Reflection Capture)
特点:预计算,速度快,不精确,只能影响局部区域。
在场景中添加一个 球体反射采集(Sphere Reflection Capture)或者 盒体反射采集(Box Reflection Capture),然后构建反射采集(预计算)。在游戏中用预编译好的纹理,直接叠加到反射材质上就有效果了(速度快)。但是,因为是预编译好的静态文件,只有摄像机和反射采集对象在同一位置时才能看到反射的最佳效果,否则会产生偏移,并且反射的影像不会随着场对象的移动发生变化,因为他是预编译好的静态纹理(不精确)。而且摄像机一旦离开反射采集球体/盒体的范围,就看不到反射效果了(只影响局部区域)。
2、平面反射(Planar Reflection)
特点:精确,实时,影响局部区域,只能用于平滑表面,可能会非常消耗性能。
添加一个 平面反射对象(PlanarReflection)在光滑平面上,这个平面就可以精准的实现反射效果,但是反射到的内容会被渲染两次,导致渲染工作量翻倍。
3、屏幕空间反射(Screen Space Reflection (SSR))
特点:实时,有噪点,性能消耗很大,只能反射出当前可见的几何体(站在你后面的人,不会映在你面前的镜子中(可怕))
SSR在ue4中是默认开启的,可以在 PostProcessVolume 中关闭。
七、静态光照
像反射一样,光照和阴影在实时渲染中也很难计算,需要占用大量硬件性能,并且计算速度慢,因此对于光照也会使用预计算/预渲染,生成光照贴图,在渲染时和模型的底色贴图相乘,这就是静态光照。
特点:
1.预计算,存储在光照贴图中。
2.运行速度快,内存消耗大。
3.预计算消耗的时间很长,当模型发生改变时需要重新预计算。
4.需要生成模型光照贴图UV(模型导入UE4时自动生成)。
5.可以处理辐射和全局光照。
6.阴影效果真实,质量高,可以生成软阴影。
7.静态光照的质量取决于光照贴图分辨率和UV布局,UV布局可能导致光照贴图出现接缝。而光照贴图的分辨率会有上限,导致巨型模型没有足够的空间存储贴图和UV,为了避免浪费,远距离的模型也不应该使用高分辨率光照贴图。
ue4使用LightMass 全局光照重要性体积(Lightmass Importance Volume)来生成光照贴图,在 世界场景设置(World Settings) 中可以设置贴图质量以及查看这些贴图。
但是,这些光照贴图都是基于已经存在于场景中的静态模型渲染的,如果在游戏运行后,走过来一个模型,他是如何对光照做出反应呢?
在引擎的4.18版本之前,应对方法是使用ILC(Indirect Lighting Cache)间接光照缓存。
使用 显示->可视化->体积光照采样 可以查看ILC的存储点,每个点中就存储了光照信息。在动态模型的lightmass设置中可以设置间接光照缓存的质量,从而确定如何使用场景中的ILC
ILC自动分布在 全局光照重要性体积(Lightmass Importance Volume) 中,地面分布的多一些,空中分布的少一些,也可以使用 角色间接细节体积(Lightmass Character Indirect Detail Volume)来控制ILC的分布
从4.18版本开始,体积光照贴图默认取代了间接光照缓存。可以在 世界场景设置(World Settings)-> Lightmass设置(Lightmass Settings)-> 体积光照方法(Volume Lighting Method) 下进行切换。以下是官方给出的详细对比
预计算光照体积/间接光照缓存:
-在CPU上进行开销巨大的插值
-逐个Object插值,在实例化组件上也不例外
-无法影响体积雾
-仅在静态表面上高密度放置,导致低密度样本漏光
-Lightmass重要体积以外为黑色样本
-支持关卡流送
体积光照贴图:
-在GPU上进行高效插值
-逐像素插值
-对体积雾有效
-以高密度放置在所有静态表面周围
-拉伸边界体素以覆盖Lightmass重要体积外部区域
-当前不支持关卡流送
八、动态光照
特点:
1.使用GBuffer实时渲染。
2.灯光可以随时改变。
3.不需要额外的模型准备工作。
4.动态阴影非常消耗性能。
5.渲染动态阴影有多种实现方式,需要了解并权衡最适合的方法或混合方案。
6.因为动态阴影非常消耗性能,通常会降低渲染质量来弥补。
7.动态光照目前无法对大部分内容产生辐射或全局光照。
8.动态光照会比静态光照更有“代入感”。
9.动态阴影不需要光照贴图,因此不用顾虑模型过大。
10.不会生成软阴影。虽然也能实现,但效果远不如静态光照的软阴影。
因为动态阴影消耗过大,通常会关闭一些光源的阴影投射。
动态阴影有4种主要类型,和一些不常见类型。
1.最常用的类似就是 常规动态阴影(Regular Dynamic Shadows) ,设置为 可移动(Movat) 并且打开 投射阴影(Cast Shadows) 的光源,就会生成常规动态阴影,阴影特点是清晰甚至过于锐利。
2.逐对象阴影(Per Object Shadows) 或者叫固定光源阴影---将光源的移动性设置为 固定(Statior)。固定光源阴影会通过静态阴影和动态阴影混合实现阴影效果,效果比常规动态阴影柔和一些。
3.CSM 级联阴影贴图(Cascaded Shadow Maps),只能用于 定向光源(DirectionalLight) ,在定向光源的细节面板中可以进行设置。
4.距离场阴影(Distance Field Shadows) ,级联阴影虽然可以实现很好的过度效果,但是对于处理超远距离的阴影,还是有性能上的浪费,这时就需要距离场阴影。距离场阴影使用距离场信息计算阴影,而不是几何体信息,他的效果不够精确,但是消耗低。需要打开 项目设置>引擎>渲染>Lighting>Generate Mesh Distance Fields 来生成体积纹理。
5.插图阴影(Inset Shadow) 他的本质还是逐对象阴影,在一些动态模型上能提供更高的阴影分辨率,角色通常会默认开启插图阴影,这样即使附近没有适合的光源,也能投射出高分辨率的阴影。
6.接触阴影(Contact Shadow) 适用于细小的物体。
7。胶囊体阴影(Capsule Shadow) 非常简单的阴影,消耗也非常低。
动态阴影的效率非常低,因为计算动态阴影需要获取4种信息。
1.摄像机到几何体的距离(GBuffer中的深度缓冲)。
2.摄像机位置。
3.光源位置。
4.几何体到光源的距离。(这个就是大量消耗性能的地方,需要用光源当作摄像机,渲染出一张只有深度信息的360度全景贴图)
然后用这4个信息合成一张阴影贴图,再用这张阴影贴图去和光照贴图、法线贴图、纹理贴图等去混合,生成最终效果。这些都是像素着色器做的事情。
总结:
1.虽然动态光照本身损耗相对较低,但是动态阴影消耗巨大。
2.消耗源于像素着色器的运算,像素越多,速度越慢。
3.光源距离摄像机越近,受影响的像素就越多。
4.尽量缩小光源的半径。
5.避免动态光源重叠。
6.关闭不必要的阴影。
7.几何体的面数会影响动态阴影质量,在阴影复杂的环境中,需要降低多边形数量。
8.使用距离场阴影进行优化。
9.可以在距离远时关闭光源,或者通过蓝图控制阴影的开关。
10.适当混合使用静态和动态光照。(例如将一个强度弱,范围广的静态光源和一个高强度小范围的可移动光源组合使用,就可以实现近距离动态,远距离静态的效果。技术上来讲,也可以用一个固定光源来代替这两个光源。)
九、雾和透明
ue4有两种距离雾(性能好的 大气雾 Atmospheric Fog ,效果好的 指数高度雾 Exponential Height Fog)和一种局部体积雾。
距离雾会随着距离而消退。通过像素着色器将深度缓冲、雾的基础色、光照和之前渲染的图像混合在一起,就实现了雾的效果。
渲染半透明是延迟渲染的短板。
为了追求效果:
1.用正向渲染来渲染透明,再将结果混合到延迟渲染中。(修改材质细节面板的光照模式为“正向着色”)
为了追求性能:
1.尽量使用材质混合模式的“遮罩”代替“半透明”。
2.半透明的着色模型尽量不要使用“默认光照”,而是用“无光照”并且改变自发光效果,手动去适配周围环境光。
3.半透明的材质应该尽可能的简单。
十、后期处理
在渲染的最后阶段,再次使用GBuffer和像素着色器进行渲染处理,这就是后期处理。在ue4的 后期处理体积 PostProcessVolume 中可以做出很多效果。
常见的后期处理:
-泛光(Light Bloom)
-景深/模糊(Depth of Fidld/Blurring)
-镜头炫光(lens flares)
-光束(Light Shafts)
-虚光(Vignette)
-色调映射/颜色分级(Tonemapping/Color correction)
-曝光(Exposure)
-运动模糊(Motion Blur)
一些尚未学习的重要知识点:
-次级表面渲染
-折射
-位移贴图
-屏幕空间环境光遮蔽
-UI渲染
-贴花
网友评论