[ue4] 几何体绘制管线

        几何体绘制管线,通俗来说,就是通知GPU绘制物体信息的一套完整流程;ue4封装了一套较为复杂的框架来维护整个流程,本文将对整个流程做一些简单的介绍。

引入

        ue4的大部分核心渲染代码入口都在FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)这一函数中(以及对应的Mobile版本),在这里可以比较详细地看到整个渲染流程中先后做了哪些事情。其中,在渲染前期的时候,一个比较重要的函数是InitViews:

        在InitViews中,做了如下两件重要的事情:

        (1) 计算可见性

        (2) 构建可见的图元绘制指令

        场景中的所有图元存储在FScene中,我们首先需要对所有图元做一个筛选,确定哪些图元是我们需要在当前帧绘制的(剔除不在视锥体内的,剔除被完全遮挡的,剔除远距离的小物件等)。

        确定了待绘制的图元后,我们最终的目标就是将这些图元的绘制提交给GPU(drawcall)。为了实现这一点,我们需要构造MeshCommand结构,它包含了物体的渲染信息,如顶点缓冲、索引缓冲、着色器绑定等。

       为了实现构建可见的图元绘制指令,ue4实现了一套机制,简单来说,就是为不同类型的图元生成一个中间数据结构,不同渲染通道加载对应所需的中间数据结构,并设置渲染状态,构造MeshCommand,将渲染指令加入到DrawList。整体的流程图如下:

生成MeshBatch

        前文已经提到了我们会生成一个中间临时的数据结构,这一结构就是FMeshBatch,它包含了通道确定最终着色器绑定和渲染状态所需的所有内容。

        Proxy

        ue4中有着各种不同类型的UPrimitiveComponent,如StaticMesh,Sprite,Landscape,Particle,ISM,HISM等。为了针对不同类型做生成时的特殊处理,ue4针对每个UPrimitiveComponent设计了对应的FPrimitiveSceneProxy,不同类型的Proxy将生成统一中间格式的MeshBatch。

       对于Proxy而言,它类似一个代工的工厂,只需关心如何生成MeshBatch,而无需关心这个MeshBatch将会在何时何处如何使用。如下图所示,从FPrimitiveSceneProxy派生了多种类型,它们分别有各自对应的UPrimitiveComponent。

        静态和动态生成

       对于有些物体而言,它的渲染状态大部分情况下不会改变,在这种情况下,生成的MeshBatch是确定的,因此可以缓存下来,仅在有必要的时候重新生成。我们将这类生成MeshBatch的途径称为静态路径。

       类似的,对于另一部分物体而言,它们的渲染状态可能会频繁发生改变,因此需要每帧生成MeshBatch,我们将其称为动态路径。

       为了确定Proxy从静态或是动态路径生成MeshBatch,可以通过调用GetViewRelevance()函数,获取bDynamicRelevance/bStaticRelevance属性。

       对于静态路径而言,将调用Proxy中的DrawStaticElements构造对应MeshBatch;而对于动态路径而言,将调用Proxy中的GetDynamicMeshElements来构造对应的MeshBatch。

       在InitViews中的ComputeViewVisibility函数中,我们能够先后看到两个调用,分别用于收集静态MeshBatch和动态MeshBatch:

静态路径

 

动态路径

        静态生成

        对于静态生成的图元绘制指令而言,当我们把图元添加到场景中时(AddToScene),同时创建MeshBatch,并将MeshBatch相关的信息绑定到场景中。我们调用Proxy的DrawStaticElements来构造静态物体的MeshBatch:

        必要时,我们生成缓存的MeshCommand:

 

        对于每个图元而言,收集MeshBatch主要包含ComputeRelevance()和MarkRelevant()两个过程。

        其中,ComputeRelevance主要是收集所有静态物体;而MarkRelevance主要负责获取MeshDrawCommand,并将MeshBatch和相关的RenderPass关联起来。

        缓存的MeshBatch信息记录在PrimitiveSceneInfo中:

       StaticMeshBatch对应的MeshDrawCommand也有缓存机制,如果使用缓存,我们查找对应的Command并收集;如果不使用,则直接收集MeshBatch,等到后续再处理相关逻辑。

      缓存和非缓存的MeshBatch数据分别记录在ViewCommands.MeshCommands和ViewCommands.DynamicMeshCommandBuildRequests中。

       动态生成

       动态物体每帧都会重新生成对应的MeshBatch,它的整体逻辑要相对简单一些。

       首先会通过Proxy的GetDynamicMeshElements函数构造对应的MeshBatch,之后,在ComputeDynamicMeshRelevance函数中,和相关的RenderPass关联起来。

       动态物体的MeshBatch通过Collector来收集,记录在View.DynamicMeshElements中。

生成MeshBatchCommand 

        ue4包含了多个绘制pass,如DepthPass, BasePass, ShadowPass等,这是通过继承FMeshPassProcessor实现的,最终的绘制发生在这里。

        在ComputeViewVisibility中,当我们收集完所有静态/动态MeshBatch后,紧接着就要将提交将MeshBatch转换为对应的MeshDrawCommand的请求,整个过程发生在SetupMeshPass中:

        在前面我们已经提到,无论是静态或是动态的MeshBatch,都会与特定的RenderPass关联。这里的RenderPass包含DepthPass, BasePass, ShadowPass等,不同的Pass是通过继承FMeshPassProcessor实现的,每个FMeshPassProcessor都会包含一个AddMeshBatch函数。

        而在SetupMeshPass中,我们将会遍历所有的pass,并获取对应的MeshBatch,调用AddMeshBatch,如下图,所标注的两处AddMeshBatch分别处理动态和静态(无cache command)的MeshBatch。

        AddMeshBatch由每个pass单独实现,它主要包含以下两个部分:一个是根据MeshBatch的信息获取相关渲染信息,如绑定的着色器,渲染状态(混合模式,深度读写模式等);另一个就是调用通用的BuildMeshDrawCommands函数,从MeshBatch和前一步计算好的渲染状态中生成MeshDrawCommands。

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值