找回密码
 立即注册
查看: 317|回复: 1

UE4 Rendering Part 3: Drawing Policies

[复制链接]
发表于 2022-1-19 18:10 | 显示全部楼层 |阅读模式
原文地址:https://medium.com/@lordned/unreal-engine-4-rendering-part-3-drawing-policies-89bb1a3c641b
Introduction

在这篇文章,我们将介绍drawing policy和draw policy factory和它们如何与系统所有其它部分进行互动的。另外,我们还介绍什么让Unreal画Mesh的。


Drawing Policies

Drawing Policy在UE4中不仅仅是一个类,因为他们不是继承于同一个类。理论上,drawing policy决定哪个shader变体用于绘制东西,但是它不选择绘制什么活着什么时候绘制。我们将看看两种drawing policy,一个是Unreal depth pre-pass,另外一个是更加复杂的Unreal base pass。


FdepthDrawingPolicy

Depth drawing policy是一个简单的drawing policy。在它的构造函数中,它让material找到一个指定vertex factory的shader。
VertexShader = InMaterial.GetShader<TDepthOnlyVS<false>>(VertexFactory->GetType());
TDepthOnlyVS是FMeshMaterialShader一个实现。Unreal尝试编译所有material/shader/vertex factory可能组合,因此对应的shader能够被找到。如果你有ShouldCache函数构建错误,引擎会抛出一个assert让你来解决这个问题。
接着,让我们来看下应该被渲染的material来判断material是否启动了tesselation。如果启动了,depth drawingpolicy找到hull和domain shader:
HullShader = InMaterial.GetShader<FDepthOnlyHS>(VertexFactory->GetType());
DomainShader = InMaterial.GetShader<FDepthOnlyDS>(VertexFactory->GetType());
Drawing Policies通过SetSharedState和SetMeshRenderState函数来设置shader参数,尽管他们就是把这些参数传给当前的bound shaders.
FbasePassDrawingPolicy

这个是Unreal开始变得tricky的地方。让我们思考下在deferred rendering中的basepass。你有很多使用不同硬件特性(例如tessellation)和不同vertexfactory的不同material,你需要light-specific变体。这里有大量的组合,Unreal使用几种宏来实现它。如果你不太理解也没有关系,这个地方你大概知道怎么回事就可以了。
首先要做的事情就是做一个FBasePassDrawingPolicy模板,叫做template<typename LightMapPolicyType> class TBasePassDrawingPolicy: public FBasePassDrawingPolicy,以及构造函数简单调用另外一个模板函数。
现在它们知道它们尝试获取shader的lighting policy是什么,它们使用与Depth Drawing Policy相同的InMaterial.GetShader函数,但是它们获取到的是一个模板的shader class。
VertexShader = InMaterial.GetShader<TBasePassVS<TUniformLightMapPolicy<Policy>, false> >(VertexFactoryType);
当然你可以顺着模板链走下去,但是重要的是Unreal如何知道所有可能的实现。答案就是一大堆嵌套宏。在BasePassRendering.cpp上面,我们从上往下看。
第一个宏就是IMPLEMENT_BASEPASS_VERTEXSHADER_TYPE,它为给定的LightMapPolicyType注册好了vertex, hull和domain material shaders。LightMapPolicyName创建新的typedefs。所以现在我们知道调用IMPLEMENT_BASEPASS_VERTEXSHADER_TYPE注册vertex shader。第二个宏实IMPLEMENT_BASEPASS_LIGHTMAPPED_SHADER_TYPE使用LightMapPolicyType和LightMapPolicyName和调用IMPLEMENT_BASEPASS_VERTEXSHADER_TYPE和IMPLEMENT_BASEPASS_PIXELSHADER_TYPE。这个宏所以让我们为任何给定LightMap创建完整的shader chain。最后,Unreal这个宏16次,在不同LightMapPolicyTypes和LightMapPolicyNames组合进行传输。
在早些调用InMaterial.GetShader<..>某个时间点,一个函数有个很大switch语句将返回正确的LightMapPolicyType。所以,我们知道Unreal声明所有变体,GetShader能够把正确的返回。
Drawing Policy Factory

我们已经知道drawing policy为给定material和vertex factory寻找合适的shader。这让Unreal能够创建“获取depth only shaders”或者“获取point lights所有相关的shaders”。但是,Draw Policy是如何被创建的?怎么知道哪个被创建呢?它怎么知道如何绘制?这也就是Drawing Policy Factory的功能。他们检查material或者vertex factory状态,然后创建正确的drawing policy.
FdepthDrawingPolicyFactory

我们将使用FDepthDrawingPolicy作为一个简单的例子。这里只有三个函数:AddStaticMesh, DrawDynamicMesh和DrawStaticMesh。当AddStaticMesh被调用,Policy Factory根据material设置以及将要绘制的asset来决定创建合适的Drawing Policy。接着Unreal把该drawing policy压入到将要绘制的FScene里面。
举例来说,FDepthDrawingPolicyFactory查看material,是否它修改mesh位置。如果修改了位置,它创建FDepthDrawingPolicy和将它添加到FScene中的“MaskedDepthDrawList”。如果material没有修改位置,则创建FPositionOnlydepthDrawingPolicy,并添加到FScene中“different list”里面去。



FDepthDrawingPolicyFactory也可以绘制mesh batch。Mesh batch也会根据设置,创建drawing policy。但是,它不会将drawing policy添加到list里面去,而且通过RHI layer建立GPU的状态,而后调用另外drawing policy将mesh绘制出来。
Telling the Drawing Policy Factory to Draw

最后,我们学习一下Drawing Policy Factory相关内容是如何一起工作的。记住,没有Drawing Policies基类或者Drawing Policy Factory基类会怎么样。
FStaticMesh::AddToDrawLists

我们的FDepthDrawPolicyFactory有个函数叫做AddStaticMesh,所以不用惊讶这个类跟Static mesh有关系。当AddToDrawList被调用时,它会检查assets和project设置来决定如何处理。它做的第一件事情就是调用FHitProxyDrawingPolicyFactory::AddStaticMesh,接着是FShadowDepthDrawingPolicyFactory::AddStaticMesh,接着FDepthDrawingPolicyFactory::AddStaticMesh和FVelocityDrawingPolicyFactory:AddStaticMesh。
所以我们当FStaticMesh被添加到draw list时,它创建一系列Drawing Policy Factory。这个函数具体怎么被调用不是很重要,但是我们知道有人告诉在base pass之前使用depth pass来绘制。
输入FDeferredShadingRenderer,一个巨大class让所有东西按照正确顺序绘制。FDeferredShadingRenderer::Render开始整个流程和控制渲染操作的顺序。我们来看下base pass drawing policy factory。Render函数调用FDeferredShadingSceneRenderer::RenderBasePass,接着调用FDeferredShadingSceneRenderer::RenderBasePassView,接着调用FDeferredShadingSceneRenderer::RenderBasePassDynamicData,接着在一个循环中调用FBasePassOpaqueDrawingPolicyFactory::DrawDynamicMesh,每次传送一个不同的Mesh。
Review

Drawing Policies根据给定material/vertex factory/shader,寻找正确shader组合。开发者指定shader类型完成依赖该policy的不同事情。Drawing Policy Factory创建Drawing Policies和添加他们到合适的list。最后,通过一大串FDeferredShadingRender::Render结束不同list循环和调用他们的绘制函数。
Next Post

我们将介绍大量信息,让你知道这些片段是如何连接一起工作的。我了解到Scene是如何创建drawable data(UPrimitiveComponent)和这些数据如何到render thread(FPrimitiveSceneProxy),以及render thread如何将数据以正确格式(FVertexFactory)传到GPU上去的。接着,我们介绍根据开发者意图,Drawing Policy是如何找到合适的shader,Drawing Policy Factory从多种Drawing Policy抽象出来,接着我们了解这些Drawing Policy如何被调用的。


下一篇文章,我们将移到GPU那边。我们将了解下shader构架,大部分在Deferred shading renderer。我们将什么shader文件和函数修改预期结果的那一部分,以及shading是如何计算的。我们不会研究Deferred shading pipeline具体细节。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-1-19 18:13 | 显示全部楼层
热泪盈眶,感谢作者分享总结如此用心的深度好文
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 21:29 , Processed in 0.241894 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表