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