UE4 渲染基础概念

本文总结于ue4 4.9官方文档

转载请注明文章出处,作者信息,原文链接

场景基本对象

  • UWorld : 世界,相当于一个包含了所有角色和组件的容器,世界中的关卡可以动态载入载出,游戏中的多个世界可以被设置为活动的
  • ULevel : 关卡,世界的子集,用于包含和管理角色和组件的载入载出,关卡被存储于一个独立的 map 文件中
  • USceneComponent : 能够被加载到场景中的对象的基类,如灯光,网格,雾等
  • UPrimitiveComponent: 所有能够被渲染的或可与物理交互的对象的基类,同时也扮演了最小粒度的可视化裁剪和保存渲染属性的角色(如投射阴影)与所有 UObjects 一样,游戏线程拥有所有变量和状态,渲染线程不应直接对其进行访问
  • ULightComponent : 代表光源。渲染器负责计算和添加其对场景的贡献
  • FScene : UWorld 的渲染器版本。对象仅在其被添加到 FScene(注册组件时调用)后才会存在于渲染器中。渲染线程拥有 FScene 中的所有状态,游戏线程无法直接对其进行修改
  • FPrimitiveSceneProxy : UPrimitiveComponent 的渲染器版本,为渲染线程映射 UPrimitiveComponent 状态。存在于引擎模块中,用于划分为子类以支持不同类型的基元(骨架、刚体、BSP 等)。实现某些非常重要的函数,如 GetViewRelevance、DrawDynamicElements 等
  • FPrimitiveSceneInfo : 内部渲染器状态(FRendererModule 实现专有),对应于 UPrimitiveComponent 和 FPrimitiveSceneProxy。存在于渲染器模块中,因此引擎看不到它
  • FSceneView : 单个视图到一个 FScene 的引擎代表。视图可以通过对 FSceneRenderer::Render 的不同调用的不同视图来渲染(多编辑器视口)或通过对 FSceneRenderer::Render 的同一调用中的多个视图来渲染(分屏游戏)。为每个帧构建新视图
  • FViewInfo : 渲染线程中描述View信息的对象,可以看成是view在渲染线程中的副本
  • FSceneViewState : ViewState 存储有关在多个帧中需要的某个视图的私有渲染信息。在游戏中,每个 ULocalPlayer 都有一个视图状态
  • FSceneRenderer : 每个帧都会被创建的类,用于封装跨帧的临时对象

材质基本对象

  • FMaterial : 材质渲染接口,用于访问材质属性(如混合模式),及着色器容器,可根据具体平台和设置选择具体着色器版本
  • UMaterial: 材质资源类,计算着色的材质属性,和设置混合模式等功能
  • FMaterialResource : UMaterialFMaterial 接口实现
  • FMaterialRenderProxy : 材质对象在渲染线程的实现,用于渲染线程访问材质的各种属性,如各标量值,向量值,贴图参数等
  • UMaterialInterface : 虚类,材质对象在游戏线程的接口,用于访问渲染线程的FMaterialRenderProxy对象,和资源对象 UMaterial
  • UMaterialInstanceConstant : 仅用于编辑器访问的UMaterialInterface实现,可以提供标量、向量、纹理和静态开关参数
  • UMaterialInstanceDynamic : 运行时可被更改的UMaterialInterface实现,可以提供标量、向量、纹理参数,无法提供静态开关参数,且无法成为另一 UMaterialInstance 的父项

场景渲染基本概念

  • Primitive components and proxies
    基本组件是可视性和相关性确定的基本单位。例如,遮蔽和视锥剔除都是按基元进行的。因此在设计系统时,考虑组件的大小十分重要。每个组件都有一个边界,用于多种操作,如剔除、阴影投射和光照影响确定。
    组件只有在注册之后才会对场景(以及渲染器)可见。更改组件属性的游戏线程代码必须调用组件上的 MarkRenderStateDirty(),将更改传播到渲染线程。
  • FPrimitiveSceneProxy and FPrimitiveSceneInfo
    FPrimitiveSceneProxyUPrimitiveComponent的渲染器版本,主要用于根据不同渲染目标类型将渲染目标归类,它存在与引擎模块中,在渲染通道中其某些函数会被调用,FPrimitiveSceneInfo保存了每个UPrimitiveComponent在渲染器内部的状态,为渲染器模块私有

下面按其所在的模块(引擎模块,渲染模块)列出了各种主类

Engine ModuleRenderer Module
UWorldFScene
UPrimitiveComponent / FPrimitiveSceneProxyFPrimitiveSceneInfo
FSceneViewFViewInfo
ULocalPlayerFSceneViewState
ULightComponent / FLightSceneProxyFLightSceneInfo

下面按其所在的线程(游戏线程,渲染线程)列出了各种主类

Engine ModuleRenderer Module
UWorldFScene
UPrimitiveComponentFPrimitiveSceneProxy / FPrimitiveSceneInfo
FSceneView / FViewInfo
ULocalPlayerFSceneViewState
ULightComponentFLightSceneProxy / FLightSceneInfo

FPrimitiveSceneProxy 重要的渲染函数

  • GetViewRelevance : 每帧开始的时候会在InitViews中被调用, 并返回一个初始化好的FPrimitiveViewRelevance对象, FPrimitiveViewRelevance对象保存了渲染一些渲染标志信息如,改primitive是否支持光照,是否已经被绘制过了 等,一个Primitive有多个element, 每个element 都关联了自己的FPrimitiveViewRelevance对象,这表明,一个Primitive 可能既包含不透明的element,有包含透明的element,或者既包含静态的element又包含动态的element,它们并非相互排斥
  • GetDynamicMeshElements : 该函数会收集所有的动态物体并在随后渲染
  • DrawStaticElements : 绘制 primitive 的静态物体,静态图元仅当想关联的FPrimitiveViewRelevance对象声明为静态时被绘制

场景渲染顺序(Scene rendering order)

渲染器按照其希望将数据整合给渲染目标(render target)的顺序处理场景。例如,only Depth 的通道会比 Base 通道先渲染,先填充 Heirarchical Z (HiZ),从而降低基础通道(base pass)中的着色消耗。此顺序是按通道函数在 C++ 中调用的顺序静态决定的

渲染策略(Drawing Policies)

渲染策略(drawing policy)包含了通过通道特定的着色器渲染网格体(meshes)的逻辑, 它通过特定的FVertexFactory接口来抽象网格(mesh)通过FMaterial接口来抽象具体材质(material),在底层,一个渲染策略(drawing policy)拥有一系列的材质着色器,和一个 FVertexFactory对象,通过绑定FVertexFactory的顶点buffer和顶点着色器(mesh material shaders)到渲染硬件接口(Rendering Hardware Interface(RHI))并设置适当的着色参数,然后执行 RHI 绘制调用(RHI draw call)

绘制规则方法(Drawing Policy methods)

  • Constructor : 从给定的顶点工厂(vertex factory)和材质着色器列表(material shader map)找出适当的着色器,并存储他们的引用。
  • CreateBoundShaderState : 为绘制规则(Drawing Policy)创建绑定到RHI的着色器状态。
  • Matches/Compare : 提供了排序该drawing policy和渲染列表(draw lists)中其他drawing policy的函数,Matches 必须比较 DrawShared 依赖的所有因素。
  • DrawShared : 设置在从 Matches 返回 True 的绘制规则之间一致的 RHI 状态。例如,大多数绘制规则会为材质和顶点工厂排序,因此着色器参数只依赖可以设置的材质,并且可以绑定特定于该顶点工厂的顶点缓冲区。应尽可能在此处设置状态,而非在 SetMeshRenderState 设置,因为 DrawShared 在静态渲染路径中调用较少。
  • SetMeshRenderState : 设置特定于此网格体的 RHI 状态,或 DrawShared 中未设置的状态。这比 DrawShared 调用的次数多得多,因此此处性能非常重要。
  • DrawMesh : 实际发出 RHI 绘制调用。

渲染路径(Rendering paths)

UE4有静态渲染和动态渲染两个渲染路径,动态渲染路径可以被更灵活的控制但速度较慢,静态渲染路径(缓存尽可能靠近 RHI 级别的场景转换)速度较快,两条渲染路径的差异基本是在上层实现中,因为它们在底层都使用(Drawing polices)。 每个渲染批次(rendering pass | drawing policy)应该确保能够处理这两种渲染路径如果需要的话

动态渲染路径(Dynamic rendering path)

待查

静态渲染路径(Dynamic rendering path)

静态渲染路径通过静态渲染列表(static draw list)来实现, 所有场景中的网格(mesh)物体都会被添加到静态渲染列表中,在加入渲染列表时,渲染代理的DrawStaticElements对象会收集FStaticMeshElements对象,然后一个绘制策略(drawing policy)会被创建和保存,根据CrateBoundShaderState的结果,这个新创建的渲染策略会被CompareMatches函数进行排序并插入到渲染列表中的适当位置(参见 TStaticMeshDrawList::AddMesh),在InitViews函数中,一个包含了静态渲染列表中物体可视性的位数组(BitArray)会被初始化给TStaticMeshDrawList::DrawVisible函数在具体绘制渲染列表的时候使用,DrawShared只会在所有相同类型的渲染策略(Drawing Policy)被调用一次, 而 SetMeshRenderStateDrawMesh会在每次绘制FStaticMeshElement(参见TStaticMeshDrawList::DrawElement)的时候被调用
  静态渲染路径会将大部分工作移动到链接阶段(attach time)来做,这对于加速场景转换(scene traversal)和缩短渲染时间很有意义,这样的话渲染线程渲染静态物体的时间可以提高3倍的效率,这样便可以放更多的静态物体到场景中,因为静态渲染列表会在链接阶段(attach time)将数据缓存,但也仅仅能够缓存与视口无关的状态(view independent stat),很少重新连接但经常需要渲染的单元(primitive)非常适合静态绘制列表
  静态渲染路径可能会暴露出 bug,因为它每个状态桶只调用 DrawShared 一次。这些 bug 可能会很难发现,因为它们取决于场景中网格体的渲染顺序和连接顺序。特别的视图模式(如仅光照、无光照等)会强制所有基元使用动态路径,因此如果在强制动态渲染路径时 bug 消失,则其很可能是由于某绘制规则的 DrawShared 和/或 Matches 函数的错误实现而出现的。

总体渲染路径(High level Rendering order)

下面将说明从 FDeferredShadingSceneRenderer::Render 开始渲染一个帧时的简略控制流程

  • GSceneRenderTargets.Allocate : 按需要重新分配全局场景渲染目标,使其对当前视图足够大。
  • InitViews : 通过多种剔除方法为视图初始化基元可见性,设立此帧可见的动态阴影、按需要交叉阴影视锥与世界场景(对整个场景的阴影或预阴影)。
  • PrePass / Depth only pass: RenderPrePass / FDepthDrawingPolicy 。渲染遮挡物,对景深缓冲区仅输出景深。该通道可以在多种模式下工作:禁用、仅遮蔽,或完全景深,具体取决于活动状态的功能的需要。该通道通常的用途是初始化 Hierarchical Z 以降低 Base 通道的着色消耗(Base 通道的像素着色器消耗非常大)。
  • Base pass : RenderBasePass / TBasePassDrawingPolicy。渲染不透明和遮盖的材质,向 GBuffer 输出材质属性。光照图贡献和天空光照也会在此计算并加入场景颜色。
  • Issue Occlusion Queries / BeginOcclusionTests : 提出将用于下一帧的 InitViews 的延迟遮蔽查询。这会通过渲染所查询物体周围的相邻的框、有时还会将相邻的框组合在一起以减少绘制调用来完成。
  • Lighting : 阴影图将对各个光照渲染,光照贡献会累加到场景颜色,并使用标准延迟和平铺延迟着色。光照也会在透明光照体积中累加。
  • Fog : 雾和大气在延迟通道中对不透明表面进行逐个像素计算。
  • Translucency : 透明度累加到屏外渲染目标,在其中它应用了逐个顶点的雾化,因而可以整合到场景中。光照透明度在一个通道中计算最终光照以正确融合。
  • Post Processing : 多种后期处理效果均通过 GBuffers 应用。透明度将合成到场景中。

着色器等级(shander feature level)

  • SM5 : 通常对应于 D3D11 Shader Model 5,但由于 OpenGL 4.3 限制,仅有 16 种纹理可以使用。支持曲面细分、计算着色器和立方体贴图阵列。支持延迟着色路径。
  • SM4 : 对应 D3D11 Shader Model 4,这与 SM5 基本相同,但没有曲面细分、计算着色器和立方体贴图阵列。支持延迟着色路径。不支持眼适应,因为其使用计算着色器。
  • ES2 : 对应大多数 OpenGL ES2 移动设备支持的功能。使用削减向前着色路径。
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐