[ue4] 几何体绘制管线
Posted ZJU_fish1996
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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。
以上是关于[ue4] 几何体绘制管线的主要内容,如果未能解决你的问题,请参考以下文章
图形基础篇04 # GPU与渲染管线:如何用WebGL绘制最简单的几何图形?