1. 概述
UE4 的渲染相关模块分布在 Engine, Renderer 和 RenderCore 。
Game Thread 和 Render Thread 一般不触碰对方的数据。Game Thread 里的类对应到 Render Thread 里基本遵循:UXX 在 Game Thread 里,对应的 FXX 在 Render Thread 里。 e.g. 代表光源的 ULightComponent & FLightSceneProxy 。
2. 工具推荐
- RenderDoc :看各种渲染命令、数据。谁用谁舒服 👍 x N。
- HLSL Tool for Visual Studio : ush / usf 文件高亮(需要设置)。
3. SceneRenderer
移动渲染器 MobileShadingRenderer 和延迟渲染器 DeferredShadingRenderer 都公有继承自 FSceneRenderer :
父类 FSceneRenderer 里有一个纯虚函数 Render ,即子类必须实现的渲染入口:
virtual void Render(FRHICommandListImmediate& RHICmdList) = 0;
Debug 引擎源码时,在编辑器上点击 Play Preview ,选择 Mobile Preview 就会进到 MobileShadingRenderer::Render 里,而选择 Windows Preview 则进到 DeferredShadingRenderer::Render 里。代码在 Engine/Source/Runtime/Renderer/Private
下。
3.1 进到哪个 SceneRenderer ?
// SceneRendering.cpp
// FRendererModule::BeginRenderingViewFamily() => CreateSceneRenderer()
FSceneRenderer* FSceneRenderer::CreateSceneRenderer(const FSceneViewFamily* InViewFamily, FHitProxyConsumer* HitProxyConsumer)
{
// ...
if (ShadingPath == EShadingPath::Deferred)
{
SceneRenderer = new FDeferredShadingSceneRenderer(InViewFamily, HitProxyConsumer);
}
else
{
check(ShadingPath == EShadingPath::Mobile);
SceneRenderer = new FMobileSceneRenderer(InViewFamily, HitProxyConsumer);
}
return SceneRenderer;
}
4. DeferredShadingRenderer
4.1 相关文件:
DeferredShadingRenderer.h class DeferredShadingRenderer
DeferredShadingRenderer.cpp Render()
BasePassRendering.cpp RenderBasePass(), class FBasePassMeshProcessor
LightRendering.cpp RenderLights()
BasePassVertexShader.usf Main()
BasePassPixelShader.usf FPixelShaderInOut_MainPS()
DeferredShadingCommon.ush EncodeGBuffer()
DeferredLightPixelShaders.usf DeferredLightPixelMain()
DeferredLightingCommon.ush GetDynamicLighting()
4.2 前向渲染 vs 延迟渲染:
嗯,说白了就是原本 1 个 Pass 画完,现在分成两个 Pass —— 第 1 个 Pass 先把 Material 里那些东西画到 GBuffers 里,第二个 Pass 再从 GBuffers 里读出这些数据做 Lighting 。
那么 GBuffers 里放什么东西?一般来说, 引用 [2] :
A common case example is a 5 texture GBuffer, A through E.
GBufferA.rgb = World Normal
, withPerObjectGBufferData
filling the alpha channel.GBufferB.rgba = Metallic, Specular, Roughness, ShadingModelID
.GBufferC.rgb
is theBaseColor
withGBufferAO
filling the alpha channel.GBufferD
is dedicated to custom data andGBufferE
is for precomputed shadow factors.
好了,RenderDoc 要出场了。
整个 DeferredRenderer 的渲染流程如上图所示。其中, BasePass 就是延迟渲染的 Pass 1 (GBuffer Pass),下面一些的 Lights 就是 Pass 2 (Lighting Pass)。BasePass 之前的 BeginRenderingGBuffer 把 RenderTargets 设置成了 GBuffers ,然后 Clear。
接下来的 BasePass 就绘制到 GBuffers 上了。然后到 Lighting 里,RenderDoc 显示出的 DX11 绘制命令,那几个连续的 PSSetShaderResources
就是在设置 GBuffers 作为参数,获得 BasePass 画好的 GBuffers 。
4.3 延迟渲染的主要步骤
4.4 Shader side
UE4 里的 Shader 是 HLSL 代码和 material graph 的结合。创建一个 material 会编译出很多的 shader permutation 。产生 shader permutation 的大量的条件编译语句是 usf / ush 难读的原因之一(而使得 UE4 的 C++ 代码难读的原因之一是大量的宏技巧)。
- BasePass VS
- BasePass PS
- DeferredLight VS
- DeferredLight PS
5. MobileShadingRenderer
5.1 相关文件:
SceneRendering.h class FMobileSceneRenderer
MobileShadingRenderer.cpp Render()
MobileBasePassRendering.cpp RenderMobileBasePass()
MobileBasePassVertexShaders.usf Main()
MobileBasePassPixelShaders.usf Main()
5.2 移动渲染的主要步骤
移动渲染器单纯就是前向渲染。
5.3 Shader side
注:因为这些 shader 的 permutaion 过多,把 RenderDoc 里最后确定的那个版本拷下来看的,并做了一些可读性处理。
5.3.1 MobileBasePassPixelShader.usf
MobileBasePassPixelShaders.usf
Main()
参数如下:
// ?
FVertexFactoryInterpolantsVSToPS Interpolants
// ?
FSharedMobileBasePassInterpolants BasePassInterpolants
// 当前 pixel 的 screen coord
in float4 SvPosition : SV_Position
// is front face?
in bool bIsFrontFace : SV_IsFrontFace
// 当前 pixel 输出的 color
out half4 OutColor : SV_Target0
干了啥事?按照顺序:
/*
1. ResolvedView = ResolveView();
2. ShadingModelContext
- FMaterialPixelParameters = GetMaterialPixelParameters(Interpolants, SvPosition);
- CalcMaterialParametersEx() 和 GetMaterialCoverageAndClipping() => PixelMaterialInputs
- PixelMaterialInputs => 设置 ShadingModelContext.XXX
- InitShadingModelContext(ShadingModelContext, MaterialParameters)
3. Diffuse
- ShadingModelContext.DiffuseColor
- DiffuseGI = max(half3(0, 0, 0), DotSH3(PointIndirectLighting, DiffuseTransferSH));
- View_IndirectLightingColorScale
4. MaterialAO
- MaterialAO = GetMaterialAmbientOcclusion(PixelMaterialInputs);
5. MobileDirectionalLight
- Shadow = GetPrimaryPrecomputedShadowMask(Interpolants).r;
- Shadow = Shadow * MobileDirectionalLightCSM(ScreenPosition.xy, SceneDepth);
- MobileDirectionalLight_DirectionalLightColor.rgb
- FMobileDirectLighting Lighting = MobileIntegrateBxDF(ShadingModelContext, NoL, RoL, MaterialParameters.CameraVector, MaterialParameters.WorldNormal, H, NoH);
6. Specular
- SpecularIBL = GetImageBasedReflectionLighting(MaterialParameters, ShadingModelContext.Roughness, IndirectIrradiance);
- ShadingModelContext.SpecularColor
7. DynamicLights
- for (int i = 0; i < NumDynamicPointLights; i++) { ... }
8. Emissive
- GetMaterialEmissive(PixelMaterialInputs);
9. lerp(Color, ShadingModelContext.DiffuseColor + ShadingModelContext.SpecularColor, ResolvedView.UnlitViewmodeMask);
10. VertexFog
11. ResolvedView.PreExposure
*/
Constant Buffers
-
MobileDirectionalLight
在 RenderMobileBassPass 里的 UpdateDirectionalLightUniformBuffers() 设置给 View.MobileDirectionalLightUniformBuffers 。本质上是 Scene.MobileDirectionalLights[] 里面的方向光源。
-
IndirectLightingCache
InitViews() 里会用 Scene->IndirectLightingCache 更新 View ,然后在 UpdatePrimitiveIndirectLightingCacheBuffers() 里更新 PrimitiveSceneInfo->UpdateIndirectLightingCacheBuffer();
-
$Globals
里面有心心念念的 NumDynamicPointLights 和 LightPositionAndInvRadius
MobileBasePassRendering.cpp 里的 FMobileBasePassMovableLightInfo 设置了这些参数
FPrimitiveSceneProxy->GetPrimitiveSceneInfo()->LightList->GetLight()->Proxy->GetLightShaderParameters() 里面得到光源信息,然后赋给 LightPositionAndInvRadius
MobileBasePass.cpp 里的 GetShaderBindings() Set dynamic point lights
MobileBasePass.h 里有 NumDynamicPointLightsParamete
5.3.2 MobileBasePassPixelShader.usf (4.26)
切换 RT 变成 GBuffer 了
// Store the results in local variables and reuse instead of calling the functions multiple times.
FGBufferData GBuffer = (FGBufferData)0;
GBuffer.WorldNormal = MaterialParameters.WorldNormal;
GBuffer.BaseColor = GetMaterialBaseColor(PixelMaterialInputs);
GBuffer.Metallic = GetMaterialMetallic(PixelMaterialInputs);
GBuffer.Specular = GetMaterialSpecular(PixelMaterialInputs);
GBuffer.Roughness = GetMaterialRoughness(PixelMaterialInputs);
GBuffer.ShadingModelID = GetMaterialShadingModel(PixelMaterialInputs);
half MaterialAO = GetMaterialAmbientOcclusion(PixelMaterialInputs);
// ------------------------------------------------------------------
#if DEFERRED_SHADING_PATH
float4 OutGBufferD;
float4 OutGBufferE;
float4 OutGBufferF;
float4 OutGBufferVelocity = 0;
GBuffer.IndirectIrradiance = IndirectIrradiance;
GBuffer.PrecomputedShadowFactors.r = GetPrimaryPrecomputedShadowMask(Interpolants).r;
EncodeGBuffer(GBuffer, OutGBufferA, OutGBufferB, OutGBufferC, OutGBufferD, OutGBufferE, OutGBufferF, OutGBufferVelocity);
OutSceneDepthAux = SvPosition.z;
5.3.3 MobileDeferredShading.usf
对方向光:
void MobileDeferredShadingPS(
noperspective float4 UVAndScreenPos : TEXCOORD0,
float4 SvPosition : SV_POSITION,
out half4 OutColor : SV_Target0)
{
}
对其他光:
void MobileRadialLightPS(
float4 InScreenPosition : TEXCOORD0,
float4 SVPos : SV_POSITION,
out half4 OutColor : SV_Target0
)
/*
1. 从 MobileSceneTextures FetchGBuffer()
2. FGBufferData = DecodeGBufferMobile()
3. 从 DeferredLightUniforms 里面得到 LightData
4. 填充 ShadingModelContext
5. GetDirectLighting
*/
6. MobileDeferredShadingPass (4.26)
MobileDeferredShadingPass.cpp
MobileDeferredShadingPass
MobileDeferredShadingPass
MobileDeferredShadingPass(RHICmdList, Scene, View, SortedLightSet)
- (?) 似乎没卵用:创建 MobileSceneTexture 然后绑成全局的 (
SCOPED_UNIFORM_BUFFER_GLOBAL_BINDINGS
) - SetViewport()
- RenderDirectLight()
- 挨个对 SortedLightSet 里的光源调用 RenderLocalLight()
RenderDirectLight
RenderDirectLight(RHICmdList, RHICmdList, Scene, View, LightSceneInfo)
PS 对应 FMobileDeferredShadingPS:
RenderLocalLight
RenderLocalLight(RHICmdList, RHICmdList, Scene, View, LightSceneInfo)
PS 对应 FMobileRadialLightPS :
-
不是点光或者聚光灯就 return
-
SetLocalLightRasterizerAndDepthState
-
填充 FMobileRadialLightPS 的 PermutationVector,绑定 VS 和 PS
-
SetGraphicsPipelineState
-
填充 FMobileRadialLightPS 的 PassParameters (ViewUB 和 DeferredLightUniforms)
-
SetShaderParameters
-
VertexShader->SetParameters
-
StencilingGeometry::DrawXXX(RHICmdList)
有用链接
[0] UE4调试源码正确方式
[1] 关于 UE4 更详细点的概述 Unreal Source Explained ,GDC Europe 2014 : Unreal Engine 4 for Programmers
[2] 介绍 UE4.22 之前版本的渲染的系列文章 Unreal Engine 4 Rendering ,之后的版本 FXXXDrawingPolicy 被 FXXXMeshProcessor 代替掉了,见 Mesh Drawing Pipeline Conversion Guide for Unreal Engine 4.22
[3] UE4 材质系统的概念 Intro to Materials: Overview