eltable渲染大量数据_基于UE4的 Mobile Skeletal Instance——Unreal渲染流程基本架构

本文详细介绍了Unreal 4.23后的渲染架构,重点讨论了Skeletal Mesh Dynamic Instance的实现难点,包括渲染流程、数据传输、MeshDrawCommand及FPrimitiveSceneInfo等内容。通过理解渲染架构,有助于动态实例化的集成。
摘要由CSDN通过智能技术生成

接下来的4个章节,将主要讲解Unreal4.23后,最新的渲染架构,如果不了解整体流程,对实现Skeletal Mesh Dynamic Instance 将会十分困难。

下面4个章节内容分布为:

1.Unreal渲染流程基本架构

2.MeshDrawCommand

3.Uniform Buffer、FVertexFactory、FVertexFactoryType

4.FVisibleMeshDrawCommand到RHICommandList

5.Game Thread与Render Thread数据传输

按照惯例,先自淫一下,宣传下自己的书。

bcc41097c25f2e5e18cd9c5b4c14e3b4.png

从此篇开始,进入了集成Dynamic Instance最复杂的部分,我也是看了很久的代码

并多次修改才把Skeletal Mesh Dynamic Instance功能放到了最合适的地方。

从4.23版本,你必须要重新认识整个渲染流程,最后给我的印象就是:架构很清晰,细节很复杂。这里的细节我不想多谈,真的太复杂了,很难通过文档来说清楚,但架构还是值得一说,有了对渲染架构整体了解,集成Dynamic Instance会有很大帮助。

发送Primitive加入渲染线程命令

我从FScene::AddPrimitive这个函数说起,一旦Mesh走进这个函数,多线程所需要的渲染数据都会被构建出来。

aa3d8870d001a36e50095b1f714c26be.png

在Game Thread中,Component注册后会通过AddPrimitive添加到Render Thread。FPrimitiveSceneInfo和FPrimitiveSceneProxy包含渲染数据,包括Transform、Bound Box、是否投影等等吧。相关代码如下:

void FScene::AddPrimitive(UPrimitiveComponent* Primitive)
{
   …
   // Create the primitive's scene proxy.
   FPrimitiveSceneProxy* PrimitiveSceneProxy = Primitive->CreateSceneProxy();
   Primitive->SceneProxy = PrimitiveSceneProxy;
 
   // Create the primitive scene info.
   FPrimitiveSceneInfo* PrimitiveSceneInfo = new FPrimitiveSceneInfo(Primitive, this);
   PrimitiveSceneProxy->PrimitiveSceneInfo = PrimitiveSceneInfo;
 
   // Cache the primitive's initial transform.
   FMatrix RenderMatrix = Primitive->GetRenderMatrix();
   FVector AttachmentRootPosition(0);
 
   AActor* AttachmentRoot = Primitive->GetAttachmentRootActor();
   if (AttachmentRoot)
   {
     AttachmentRootPosition = AttachmentRoot->GetActorLocation();
   }
 
   struct FCreateRenderThreadParameters
   {
     FPrimitiveSceneProxy* PrimitiveSceneProxy;
     FMatrix RenderMatrix;
     FBoxSphereBounds WorldBounds;
     FVector AttachmentRootPosition;
     FBoxSphereBounds LocalBounds;
   };
   FCreateRenderThreadParameters Params =
   {
     PrimitiveSceneProxy,
     RenderMatrix,
     Primitive->Bounds,
     AttachmentRootPosition,
     Primitive->CalcBounds(FTransform::Identity)
   };
 
   // Create any RenderThreadResources required and send a command to the rendering thread to add the primitive to the scene.
   FScene* Scene = this;
 
   // If this primitive has a simulated previous transform, ensure that the velocity data for the scene representation is correct
  TOptional<FTransform> PreviousTransform = 
  FMotionVectorSimulation::Get().GetPreviousTransform(Primitive);
 
   ENQUEUE_RENDER_COMMAND(AddPrimitiveCommand)(
   [Params, Scene, PrimitiveSceneInfo, PreviousTransform](FRHICommandListImmediate& RHICmdList)
   {
     FPrimitiveSceneProxy* SceneProxy = Params.PrimitiveSceneProxy;
     FScopeCycleCounter Context(SceneProxy->GetStatId());
     SceneProxy->SetTransform(Params.RenderMatrix, Params.WorldBounds, 
     Params.LocalBounds, Params.AttachmentRootPosition);
     // Create any RenderThreadResources required.
     SceneProxy->CreateRenderThreadResources();
     Scene->AddPrimitiveSceneInfo_RenderThread(PrimitiveSceneInfo, PreviousTransform);
   });
}

这里删除一些无关代码,可以看见为了解除Game Thread和Render Thread的数据耦合, FPrimitiveSceneInfo和FPrimitiveSceneProxy都new出来的,算是一份Component的数据拷贝。

渲染线程中更新Primitive

ea37984b014bfd9fce89c73cd99f97bd.png

简单说明下流程最后的4个关键点:

1. 当初花大量时间来考虑是否也用GPU Scene实现Skeletal Mesh Dynamic Instance,更新GPU Scene需要消耗大量CPU时间和并Compute Shader写入信息,最后还是放弃了。

2. 生成的Primitives数据都记录在下面的变量里面:

 /** Packed array of primitives in the scene. */
 TArray<FPrimitiveSceneInfo*> Primitives;
 /** Packed array of all transforms in the scene. */
 TArray<FMatrix> PrimitiveTransforms;
 /** Packed array of primitive scene proxies in the scene. */
 TArray<FPrimitiveSceneProxy*> PrimitiveSceneProxies;
 /** Packed array of primitive bounds. */
 TArray<FPrimitiveBounds> PrimitiveBounds;
 /** Packed array of primitive flags. */
 TArray<FPrimitiveFlagsCompact> PrimitiveFlagsCompact;
 /** Packed array of precomputed primitive visibility IDs. */
 TArray<FPrimitiveVisibilityId> PrimitiveVisibilityIds;
 /** Packed array of primitive occlusion flags. See EOcclusionFlags. */
 TArray<uint8> PrimitiveOcclusionFlags;
 /** Packed array of primitive occlusion bounds. */
 TArray<FBoxSphereBounds> PrimitiveOcclusionBounds;
 /** Packed array of primitive components associated with the primitive. */
 TArray<FPrimitiveComponentId> PrimitiveComponentIds;
 /** Packed array of runtime virtual texture flags. */
 TArray<FPrimitiveVirtualTextureFlags> PrimitiveVirtualTextureFlags;
 /** Packed array of runtime virtual texture lod info. */
 TArray<FPrimitiveVirtualTextureLodInfo> PrimitiveVirtualTextureLod;

为了实现Skeletal Mesh Dynamic Instance,你需要解读这里面所有有关渲染线程使用的变量,来找到最终最合适的集成方法,也时花了大量时间和实践,排除了这些变量相关性。

3. Static Mesh所有属性基本是不变的,它的任何渲染状态和Shader都是不变的,所以会生成MeshDrawCommand并Cache,为渲染做使用。如果存在特殊情况发生改变,它们会从FScene中移除从新添加进来。

4. 找到和这个Mesh有关系的光源,这部分和集成Skeletal Mesh Dynamic Instance有相关性的,具体后面会讲解。

Cache Static Mesh 的 Mesh Draw Command

e499bb754c67258daa33d7947bcee541.png

FPrimitiveSceneProxy::DrawStaticElements会得到StaticMeshes,是FMeshBatch类型,这里包含所有LOD级以及Sub Mesh。FMeshBatch类在之前的Unreal版本就有。

然后会遍历所有的StaticMeshes,看是否符合Cache的条件,则生成MeshDrawCommand。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值