其他分享
首页 > 其他分享> > UE4学习笔记:实时渲染原理

UE4学习笔记:实时渲染原理

作者:互联网

本随笔介绍UE4引擎在渲染及其性能影响方面的工具和概念。该随笔整理自官方教程视频:实时渲染原理

本随笔作者还在学习阶段,对于UE4引擎的使用和理解还不是非常透彻,难免出现书写上和技术上的问题,若出现了类似的问题欢迎在评论区或私信与我讨论。

目录

实时渲染(Real—Time Redndering,RTR)

渲染之前发生的过程

执行时刻(30FPS) CPU 绘制线程 GPU
0毫秒 A帧    
33毫秒 B帧 A帧  
66毫秒 C帧 B帧 A帧

执行时刻0毫秒开始,第A帧开始处理对象逻辑及位置信息:

执行时刻33毫秒开始,第A帧画面中对象逻辑及位置信息计算完毕,开始计算哪些对象需要被绘制:

执行时刻66毫秒开始,第A帧画面中对象位置及需要渲染哪些对象的需求已经计算完毕,开始进行如下所述的渲染工作。

渲染中发生的过程

几何体渲染

  1. 首先需要通过预通道(Prepass)或前期深度通道(Early Z Pass)来做一个深度检测。
    • 我们知道对象的位置以及需要渲染的对象,但是不清楚这些对象的渲染顺序,通过深度检测,当一个小型模型(如一个茶壶)“遮挡”到一个大型模型(如一面超大的墙,但是因为小型模型没有完全遮挡大型模型所以不会执行遮挡剔除操作)时,我们可以确立墙的模型只需要渲染除茶壶模型遮挡的部分以外的像素即可,就像一面墙上有一个茶壶的剪影,渲染部分就是除“剪影”以外的像素。如果像这样排列的模型很多的话,就会一个个计算每个模型在墙面上的“剪影”并叠加起来,最终确立的“剪影”就是不需要渲染的像素。
  2. 接下来就是实际的几何体渲染。
    • 几何体渲染会根据绘制调用逐个渲染。
    • 不能说一个模型算做一个绘制调用,一个模型上的一个材质也算是一个绘制调用。绘制调用就是一组拥有相同属性的多边形,很明显一个模型用到两种材质,用到两种材质的部分肯定不属于拥有相同属性,因此会算作两次绘制调用(因此不能说一个模型算作一个绘制调用,模型上不同材质的部分会被当成两个对象来渲染)。
    • 使用引擎命令“Stat RHI(Rendering Hardware Interface,渲染硬件接口)”可以显示绘制调用相关的统计信息。
    • 由于UE4引擎非常复杂,在渲染对象的背后有许多看不到的过程,因此就算你创建一个空白关卡,也会有大概100次左右的绘制调用(这里的绘制调用主要指“DrawPrimitive Calls”而不是“Mesh Draw Calls”,后者仅仅指模型的绘制调用,前者则指场景里全部对象的绘制调用,包括模型、阴影、粒子等)的基本损耗。
    • 2000到3000次左右的绘制调用是正常的,也是大部分游戏的标准,对于移动端和VR来说,这个标准值通常为几百或1000左右。5000次左右绘制调用高于硬件标准,但是仍能运行。10000次绘制调用则有可能会导致运行出现问题。
    • 对于UE4和现今的硬件来说,绘制调用及其他因素对性能产生的影响已经比多边形面数大得多
    • 渲染器(GPU等)每次渲染完一个绘制调用,都会开始请求下一次绘制调用,然后继续渲染,这样就会形成“渲染->请求->渲染”这样的停顿,该“请求停顿”正是绘制调用次数过高之后对性能造成影响的主要原因。需要注意的是组件也会导致绘制调用,组件也是逐个进行遮挡和渲染的,和单个的Actor没有区别。
    • 使用少量多面数的大模型而不是大量少面数的小模型可以降低绘制调用的次数,但是因此会造成大模型更不容易被遮挡、会加大光照贴图纹理所占用的空间、碰撞检测变得复杂(因为一个物体碰上小模型的立柱的话,只会去检测该立柱的碰撞网格体,但是如果碰上了大模型的立柱的话,会检测立柱、屋顶、地板等整个大模型组成的碰撞网格体)和加大内存的占用。
    • 发挥最好性能的方法就是衡量何时使用单个大模型何时使用多个小模型即何时合并模型:
      • 合并常用面数很低的模型。可以通过“窗口(Window)->统计(Statistics)”的统计窗口来快速获取每个模型的面数以及使用次数。这会减少绘制调用,且只增加了很少的面数。
      • 只合并同一个区域内的模型。如果你将第一个房间内的东西和第六个房间内的东西合并为一个模型的话,这对遮挡没有任何帮助,还会增加碰撞的复杂度。
      • 只合并使用同一种材质的模型。如果你的每个小模型使用的都是不同的材质,那么合并之后的大模型就会有多少种材质,这对减少绘制调用没有任何帮助。
      • 合并没有或简单碰撞的模型。这在能减少绘制调用的同时也让合并后的模型不会有太复杂的碰撞网格体。
      • 合并较小的或接收动态光照的模型,因为这类模型合并了之后其光照贴图也并不会很大。
      • 合并远距离的网格体。就算合并后的网格体体积很大,但是在远距离看起来也会很小,遮挡剔除的功能可以很好的起作用。
      • 在非常低端的硬件设备上(如移动端、VR和移动端VR),你可能需要合并所有对象。
    • UE4默认会在内存中实例化模型,即一个大小为400kb的模型,无论你在场景里面放置多少个该模型,该模型所占用的内存只有400kb,但是在渲染过程中并不会实例化,有多少个该模型的对象就需要有多少次渲染,因此可以使用实例化静态网格体(Instanced Static Mesh)来让该模型在渲染过程也进行实例化。实例化静态网格体是一种类似合并、且能有效降低绘制调用的方法,该方法是通过将模型添加到实例化静态网格体组件(Instanced Static Mesh Component),然后通过组件来生成模型的实例化,用这种方法生成四个实例化的模型的话,该四个实例化模型会被当成一个模型来渲染,只是大小有原来模型的四倍大,就像合并一样,但是每个该组件只支持实例化一个模型,不像合并模型的功能一样能将不同的模型都合并进来。实例化静态网格体的功能没有默认启用是因为,如果启用了的话,每次渲染一个新模型的时候,渲染器都要去询问引擎其他所有模型是否和该模型一致,这种询问会导致更加严重的性能损耗产生。实际上实例化静态网格体的功能就相当于是给引擎一张简表,简表上面记录了场景中哪些模型是完全一致的,这样就省去了渲染器询问引擎的过程,而直接在渲染过程上实现了实例化。
    • 使用LOD(Level Of Detail,细节层次)可以在摄像机距离模型较远的时候降低面数,这里有几个需要掌握的规则:
      • 确保每一个LOD面数是前一个LOD面数的50%或更低。至少需要确保我们优化出来的性能必须抵消因为使用LOD而造成的性能消耗。
      • 常规LOD已经足够好用,但是其存在一个问题,如果一个雕像及其下三个底座都设置了LOD,虽然这四个模型能够在特定的摄像机距离上切换LOD,但是这四个模型同时也产生了四次绘制调用。HLOD(Hierarchical Level Of Detail,分层细节层次)解决了这个问题,它会在远距离观察这四个模型的时候,将其合并为一个模型,近距离观察的时候又变成了原来的四个模型,这样当我们在远距离观察模型的时候,可以很好地消减绘制调用次数。只需要勾选“世界设置(World Settings)->LOD系统(LODSystem)->启用分层LOD系统(Enable Hierarchical LODSystem)”,然后点击“窗口(Window)->分层LOD大纲(Hierarchical LOD Outliner)”即可打开创建HLOD的界面。
      • 请在室外环境使用LOD和HLOD,否则在室内环境里很少会遇到摄像机能在LOD设置的距离外观察到对象物体的情况,且使用LOD和HLOD还产生了额外的消耗,变成得不偿失的情况。
      • 顶点着色器开始处理顶点着色也是在这个阶段进行的。
      • 骨骼网格体动画和顶点动画的取舍可以通过以下规则来进行判断:
      • 动画效果越复杂,效率就会越低下,如果涉及到性能损耗特别高的动画(例如大草原里有很大数量的草被风吹的动画),可以使用顶点动画来提高效率。
      • 面数特别多(也就代表顶点特别多)的模型,可以使用顶点动画来提高效率(因为不需要CPU来计算每个顶点的位置,而是通过GPU的顶点着色器来计算,将CPU的负担交给了GPU)。
      • 对于一些远距离的动画对象,可以通过禁用世界坐标偏移来提升效率。

顶点着色器(Vertex Shader)

渲染结束后,将图像呈现到屏幕上的过程

光栅化过程(Resterization Process)

G缓存(G-Buffer)

纹理(Texture)

材质(Material)

像素着色器(Pixel Shader)

反射(Reflection)

光照和阴影(Lighting And Shadows)

混合使用静态光照和动态光照

雾和半透明

雾(Fog)

透明度(Transparency)

后期处理(Post Processing)

泛光(Bloom)

景深(Depth Of Field)

颜色校正(Color Correction)

着色器(Shader)概念

G缓冲区(GBuffer)概念

混合预计算渲染和实时计算渲染的解决方案。

渲染质量的可延展性(Scalability)

渲染相关的命令

渲染的两种方法:“延迟渲染(Deferred Rendering)”和“前向渲染(Forward Rendering)”

延迟渲染

前向渲染

渲染指标:FPS、Game、Draw和GPU

四种常见的性能问题

  1. 半透明材质:屏幕当中半透明材质每叠加一层,就要多渲染一次半透明效果,就需要重新计算像素,因为像素是叠加在其他半透明层的上面。半透明像素在屏幕中的占比越多(例如距离半透明材质的物体越近),需要计算的像素越多,性能损耗越大。
  2. 像素着色器:越是复杂的材质,对性能的损耗越高,判断材质是否复杂可通过判断该材质所包含的指令(Instructions)数量来确定,指令数量越大的材质越复杂。像素在屏幕中的占比也会影响性能(因为占比越大的话,需要计算的像素的数量变多,计算量也会变大)。
  3. 绘制调用(Draw Call,简称DC):引擎渲染场景的时候是逐对象渲染,每渲染一个对象就是一次绘制调用,对于模型里拥有多个材质的单例模型来说,每个材质也是一次绘制调用。引擎是以绘制调用为单位进行渲染的。
  4. 动态阴影:动态阴影会随着场景中多边形的数量增加而产生更多的开销。一般来说,如果你打算在场景中使用大量动态阴影,那么就应该使用低面数的模型,如果要使用高面数的模型,那么就应该使用静态光照或不生成动态阴影。

标签:渲染,模型,笔记,阴影,纹理,UE4,像素,光照
来源: https://www.cnblogs.com/u-n-owen/p/16306664.html