The Two Tracks: Desktop Forward & Mobile Forward双轨制:桌面前向与移动前向

Contrary to a common assumption, Unreal Engine 5.6 does not have a dedicated ForwardShadingRenderer.cpp. Forward shading exists on two separate tracks, each serving very different hardware targets:

与常见认知不同,UE 5.6 并没有独立的 ForwardShadingRenderer.cpp。 前向渲染以两条独立轨道运行,分别服务于截然不同的硬件目标:

Desktop Forward桌面前向

  • Controlled by IsForwardShadingEnabled(ShaderPlatform)
  • IsForwardShadingEnabled(ShaderPlatform) 控制
  • Lives inside FDeferredShadingSceneRenderer
  • 存在于 FDeferredShadingSceneRenderer 内部
  • Skips GBuffer writes; lighting computed in base pass shader using Clustered Light Grid
  • 跳过 GBuffer 写入;在 Base Pass 着色器中使用 Clustered Light Grid 计算光照
  • Targets PC/Console VR where deferred shading is problematic with MSAA
  • 针对 PC/主机 VR 场景,延迟着色在此场景下与 MSAA 不兼容

Mobile Forward (Primary focus)移动端前向(重点)

  • Owned by FMobileSceneRenderer
  • FMobileSceneRenderer 管理
  • Fully separate renderer class — not a branch of FDeferredShadingSceneRenderer
  • 完全独立的渲染器类,不是 FDeferredShadingSceneRenderer 的分支
  • Designed to exploit TBDR (Tile-Based Deferred Rendering) hardware on iOS/Android
  • 专为利用 iOS/Android 上的 TBDR(基于瓦片的延迟渲染)硬件而设计
  • Can optionally switch to Mobile Deferred via IsMobileDeferredShadingEnabled()
  • 可通过 IsMobileDeferredShadingEnabled() 可选地切换到移动端延迟着色

Desktop Forward: A Branch Inside the Deferred Renderer桌面前向:延迟渲染器内的分支

When r.ForwardShading=1 is set on PC, FDeferredShadingSceneRenderer skips the GBuffer entirely and computes lighting per-pixel inside the base pass using GetForwardDirectLightingSplit() from ForwardLightingCommon.ush. The key conditional that drives most of the pipeline differences is:

在 PC 上设置 r.ForwardShading=1 时,FDeferredShadingSceneRenderer 完全跳过 GBuffer, 在 Base Pass 内使用 ForwardLightingCommon.ush 中的 GetForwardDirectLightingSplit() 逐像素计算光照。驱动大多数管线差异的关键条件判断是:

// DeferredShadingRenderer.cpp — multiple call sites
if (IsForwardShadingEnabled(ShaderPlatform))
{
    // Forward-specific early paths:
    RenderShadowDepthMaps(...);          // Shadows BEFORE base pass
    RenderForwardShadowProjections(...); // Pre-render shadow mask texture
    ComputeVolumetricFog(...);           // Fog also before base pass
}
else
{
    // Deferred path: shadows run AFTER base pass
}

The most important structural difference: in desktop forward, shadow maps are rendered before the base pass, and their results are written into a screen-space ForwardScreenSpaceShadowMask texture. The base pass pixel shader then samples this texture at GetForwardDynamicShadowFactors(ScreenUV) to apply pre-computed shadowing. This avoids the need for per-light shadow projection passes after lighting.

最重要的结构性差异:桌面前向中阴影贴图在 Base Pass 之前渲染,结果写入屏幕空间的 ForwardScreenSpaceShadowMask 纹理。Base Pass 像素着色器通过 GetForwardDynamicShadowFactors(ScreenUV) 采样此纹理以应用预计算阴影, 避免了光照后逐光源投影 Pass 的需要。

FMobileSceneRenderer — Constructor & Key FlagsFMobileSceneRenderer — 构造函数与关键标志

All mobile pipeline decisions are made in the constructor (MobileShadingRenderer.cpp:303), evaluated once per frame before any rendering work starts. Understanding these flags is the key to understanding every branch in the mobile renderer.

所有移动端管线决策都在构造函数(MobileShadingRenderer.cpp:303)中完成, 在任何渲染工作开始前每帧计算一次。理解这些标志是理解移动端渲染器每个分支的关键。

FMobileSceneRenderer::FMobileSceneRenderer(...)
    : FSceneRenderer(InViewFamily, HitProxyConsumer)
    , bGammaSpace(!IsMobileHDR())                               // LDR = no tonemap pass needed
    , bDeferredShading(IsMobileDeferredShadingEnabled(ShaderPlatform))  // Mobile Deferred toggle
    , bRequiresDBufferDecals(IsUsingDBuffers(ShaderPlatform))
    , bEnableClusteredLocalLights(MobileForwardEnableLocalLights(ShaderPlatform))
    , bEnableClusteredReflections(MobileForwardEnableClusteredReflections(ShaderPlatform))
{
    bIsFullDepthPrepassEnabled = (Scene->EarlyZPassMode == DDM_AllOpaque
                                || Scene->EarlyZPassMode == DDM_AllOpaqueNoVelocity);
    bTonemapSubpass   = IsMobileTonemapSubpassEnabled(...);   // Inline tonemap in base pass (Vulkan)
    bTonemapSubpassInline = ...;
    bRequiresSceneDepthAux = MobileRequiresSceneDepthAux(...); // iOS needs SceneDepthAux RT
}
Flag标志 Condition条件 Effect效果
bDeferredShading IsMobileDeferredShadingEnabled() Routes to RenderDeferredSinglePass/MultiPass instead of RenderForward路由到 RenderDeferredSinglePass/MultiPass 而非 RenderForward
bIsFullDepthPrepassEnabled DDM_AllOpaque / DDM_AllOpaqueNoVelocity Enables full Z-prepass; depth is loaded (not cleared) in main pass — critical for TBDR depth fetch启用完整深度预通道;主 Pass 加载而非清除深度——对 TBDR 深度读取至关重要
bEnableClusteredLocalLights MobileForwardEnableLocalLights() Builds a clustered light grid; enables per-pixel local light loops in the pixel shader构建聚簇光源网格;在像素着色器中启用逐像素局部光源循环
bTonemapSubpass Vulkan + r.Mobile.TonemapSubpass Tonemapping runs as a subpass inside the same render pass — eliminates one full frame buffer write色调映射作为同一渲染通道内的子通道运行——消除一次完整帧缓冲写入
bRequiresMultiPass Set in InitViews()InitViews() 中设置 Translucency or scene depth access forces a second render pass半透明或场景深度访问强制第二个渲染通道

Mobile Render() Main Loop移动端 Render() 主循环

Unlike the deferred renderer, the mobile Render() loop is significantly more compact (under 600 lines of active logic vs. 2300+). The most critical structural difference from deferred rendering is that shadows are rendered before the base pass, not after. This is essential because forward shading needs shadow data available during material evaluation.

与延迟渲染器不同,移动端 Render() 循环更为紧凑(活跃逻辑不足 600 行,而延迟端超过 2300 行)。 与延迟渲染最关键的结构性差异是:阴影在 Base Pass 之前渲染,而非之后。 这是必须的,因为前向着色需要在材质评估期间就能获取阴影数据。

00Visibility & Setup可见性与初始化

CPU Same as Deferred与延迟一致

01Lights & Shadows — Before Base Pass灯光与阴影 — Base Pass 之前

GPU Key Difference vs Deferred与延迟的关键区别

Why shadows before base pass? In forward shading, every pixel's material shader must evaluate lighting including shadows in a single pass. There is no separate lighting stage that can reference shadow data after geometry is drawn. Shadow maps therefore must be ready before any triangle reaches the pixel stage. 为什么阴影在 Base Pass 之前? 前向着色中,每个像素的材质着色器必须在单个 Pass 中同时评估光照和阴影。没有单独的光照阶段可以在几何体绘制后引用阴影数据,因此阴影贴图必须在任何三角形到达像素阶段之前就已就绪。

02Full Depth Prepass (Conditional)完整深度预通道(条件执行)

GPU TBDR CriticalTBDR 关键

When bIsFullDepthPrepassEnabled is true, RenderFullDepthPrepass() writes all opaque depth. This enables two important optimizations downstream:

bIsFullDepthPrepassEnabled 为 true 时,RenderFullDepthPrepass() 写入所有不透明深度。 这为后续开启两个重要优化:

03RenderForward — The Single-Pass DesignRenderForward — 单 Pass 设计

GPU Core of Mobile Forward移动前向核心

RenderForward() dispatches to either RenderForwardSinglePass() or RenderForwardMultiPass() depending on bRequiresMultiPass. The single-pass variant is the default and the architecturally important one:

RenderForward() 根据 bRequiresMultiPass 分发到 RenderForwardSinglePass()RenderForwardMultiPass()。 单 Pass 变体是默认路径,也是架构上最重要的:

// MobileShadingRenderer.cpp:1782 — the entire opaque+translucency pipeline is ONE RDG pass
GraphBuilder.AddPass(
    RDG_EVENT_NAME("SceneColorRendering"),
    PassParameters,
    ERDGPassFlags::Raster | ERDGPassFlags::NeverMerge, // NeverMerge: per-view passes must stay separate
    [this, ...](FRHICommandList& RHICmdList)
{
    RenderMaskedPrePass(RHICmdList, View, ...);  // Depth write for masked materials
    RenderMobileBasePass(RHICmdList, View, ...); // Opaque + sky: material eval + lighting + fog

    RHICmdList.NextSubpass();                    // TBDR: depth now readable as framebuffer fetch

    RenderDecals(RHICmdList, View, ...);
    RenderModulatedShadowProjections(...);
    RenderFog(RHICmdList, View);                 // Pixel fog — uses subpass depth fetch
    RenderTranslucency(RHICmdList, View, ...);   // Translucency also in same render pass

    PreTonemapMSAA(RHICmdList, SceneTextures);
    // Optional: inline tonemap subpass (Vulkan)
});

The RHICmdList.NextSubpass() call is the hardware key. On TBDR GPUs this signals the driver that subsequent draw calls may read the depth buffer from the same render pass as a framebuffer fetch — without any round-trip to DRAM. This is how the fog pass reads per-pixel depth without sampling a depth texture.

RHICmdList.NextSubpass() 调用是硬件关键点。在 TBDR GPU 上,这向驱动程序发出信号: 后续绘制调用可以通过帧缓冲读取从同一渲染通道读取深度缓冲区, 无需任何 DRAM 往返。这就是雾效 Pass 在不采样深度纹理的情况下读取逐像素深度的方式。

04Post Processing后处理

GPU

Light Data Architecture光照数据架构

Mobile forward uses three distinct mechanisms to deliver light data to shaders, reflecting the tension between shader simplicity and supporting more lights:

移动端前向使用三种不同机制向着色器传递光照数据,反映了着色器简洁性与支持更多光源之间的权衡:

1. Directional Light — Uniform Buffer1. 方向光 — Uniform Buffer

The primary directional light is always passed via MobileDirectionalLight uniform buffer (FMobileDirectionalLightShaderParameters). The pixel shader evaluates it first with a direct MobileDirectionalLight.DirectionalLightDirectionAndShadowTransition access at line 1044 of MobileBasePassPixelShader.usf. No light loop required — it's hardcoded as the primary light source.

主方向光始终通过 MobileDirectionalLight uniform buffer(FMobileDirectionalLightShaderParameters)传递。 像素着色器首先在 MobileBasePassPixelShader.usf 第 1044 行通过直接访问 MobileDirectionalLight.DirectionalLightDirectionAndShadowTransition 对其进行评估。 不需要光源循环——它被硬编码为主光源。

2. Local Lights — Three Modes2. 局部光源 — 三种模式

Local (point/spot) lights are handled by one of three exclusive modes, selected at shader compile time via MERGED_LOCAL_LIGHTS_MOBILE and ENABLE_CLUSTERED_LIGHTS defines:

局部(点光源/聚光灯)光源通过三种互斥模式之一处理,在着色器编译时通过 MERGED_LOCAL_LIGHTS_MOBILEENABLE_CLUSTERED_LIGHTS 定义来选择:

Mode Define宏定义 Mechanism机制 Trade-off权衡
Prepass Buffer预通道缓冲 MERGED_LOCAL_LIGHTS_MOBILE == 1 Reads from LocalLightTextureA/B written by RenderMobileLocalLightsBuffer(). All local lights merged into single direction + color per pixel.RenderMobileLocalLightsBuffer() 写入的 LocalLightTextureA/B 中读取。所有局部光源每像素合并为单一方向和颜色。 Lowest ALU cost in base pass; requires full depth prepassBase Pass ALU 开销最低;需要完整深度预通道
Clustered Merge聚簇合并 MERGED_LOCAL_LIGHTS_MOBILE == 2 Iterates clustered light grid per pixel, merges contributions via MergeLocalLights() into one synthetic light direction每像素遍历聚簇光源网格,通过 MergeLocalLights() 将贡献合并为一个合成光照方向 Better quality than mode 1; heavier per-pixel ALU质量优于模式 1;每像素 ALU 开销更重
Full Clustered完整聚簇 ENABLE_CLUSTERED_LIGHTS Full per-pixel light loop via AccumulateLightGridLocalLighting() — similar to desktop deferred clustered, but runs inside the forward base pass通过 AccumulateLightGridLocalLighting() 进行完整的逐像素光源循环——类似桌面延迟聚簇,但在前向 Base Pass 内运行 Highest quality; most expensive per-pixel; benefits from clustered culling最高质量;每像素开销最大;受益于聚簇剔除

MobileBasePassPixelShader.usf — The All-in-One ShaderMobileBasePassPixelShader.usf — 全合一着色器

This is arguably the most important file in the mobile renderer. In forward mode, a single invocation of Main() (line 302) performs what the deferred renderer spreads across three stages: material evaluation, direct lighting, and reflection/indirect lighting.

这可以说是移动端渲染器中最重要的文件。在前向模式下,Main()(第 302 行)的单次调用 完成了延迟渲染器分布在三个阶段的工作:材质评估、直接光照以及反射/间接光照。

// MobileBasePassPixelShader.usf:302 — the combined forward shader
void Main(FVertexFactoryInterpolantsVSToPS Interpolants, ...,
    out HALF4_TYPE OutColor : SV_Target0)   // Forward: direct color output
// If MOBILE_USE_GBUFFER: also outputs OutGBufferA/B/C/D (Mobile Deferred path)
{
    // 1. Evaluate material expressions → GBuffer struct (even in forward mode)
    FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(...);
    FGBufferData GBuffer = GetGBufferData(...);

    // 2. Sample pre-computed indirect lighting (lightmaps / SH cache)
    GetPrecomputedIndirectLightingAndSkyLight(..., DiffuseIndirectLighting, IndirectIrradiance);

    // 3. Direct lighting — directional light (hardcoded)
    AccumulateDirectionalLighting(GBuffer, ..., DirectLighting);

    // 4. Local lights — one of three modes (compile-time select)
    // MERGED_LOCAL_LIGHTS_MOBILE==1: read LocalLightTexture
    // MERGED_LOCAL_LIGHTS_MOBILE==2: MergeLocalLights() from grid
    // ENABLE_CLUSTERED_LIGHTS: AccumulateLightGridLocalLighting()

    // 5. Reflection IBL
    AccumulateReflection(GBuffer, ..., GridIndex, DirectLighting);

    // 6. Emissive + fog (vertex fog via interpolant — NOT a separate pass!)
    Color += GetMaterialEmissive(PixelMaterialInputs);
    half4 VertexFog = BasePassInterpolants.VertexFog; // Fog packed as VS→PS interpolant
    Color = Color * VertexFog.a + VertexFog.rgb;     // Apply fog in-shader
}
Fog is free in forward. Notice that fog is applied as a vertex-interpolated value at the end of the pixel shader — no separate fog pass, no extra render target, no extra bandwidth. The vertex shader pre-computes fog density and passes it to the pixel stage as BasePassInterpolants.VertexFog. This is one of the bandwidth advantages of forward shading that is impossible in deferred (where geometry data is unavailable at the lighting stage). 前向渲染中雾效是免费的。注意雾效是作为顶点插值值在像素着色器末尾应用的——没有单独的雾效 Pass,没有额外的渲染目标,没有额外的带宽开销。顶点着色器预先计算雾密度,通过 BasePassInterpolants.VertexFog 传递到像素阶段。这是前向着色带宽优势之一,在延迟渲染中(光照阶段无法获取几何数据)是不可能实现的。

ForwardLightingCommon.ush — Desktop Forward Light LoopForwardLightingCommon.ush — 桌面前向光照循环

This header is used by the PC forward shading path (and by translucency in the deferred renderer). It implements the full per-pixel light loop that the deferred GBuffer path avoids by separating geometry from lighting. The key function is GetForwardDirectLightingSplit():

此头文件用于 PC 前向着色路径(以及延迟渲染器中的半透明)。 它实现了完整的逐像素光照循环——延迟 GBuffer 路径通过将几何与光照分离而避免了这一点。 关键函数是 GetForwardDirectLightingSplit()

// ForwardLightingCommon.ush:137 — called from base pass pixel shader on desktop forward
FDeferredLightingSplit GetForwardDirectLightingSplit(
    uint2 PixelPos, uint GridIndex, float3 WorldPosition, ...)
{
    // 1. Directional light — with pre-sampled shadow mask
    float4 DynamicShadowFactors = GetForwardDynamicShadowFactors(ScreenUV);
    // ForwardScreenSpaceShadowMaskTexture was written before the base pass

    // 2. Optional VSM shadows for directional light
    #if VIRTUAL_SHADOW_MAP
    VirtualShadowMapSample = TraceDirectional(ForwardLightStruct.DirectionalLightVSM, ...);
    DynamicShadowFactor *= VirtualShadowMapSample.ShadowFactor;
    #endif

    // 3. Local lights loop — reads from clustered light grid
    FCulledLightsGridHeader Header = GetCulledLightsGridHeader(GridIndex);
    uint NumLights = min(Header.NumLights, GetMaxLightsPerCell()); // MaxCulledLightsPerCell

    LOOP for (uint i = 0; i < NumLights; i++)
    {
        FLocalLightData LocalLight = GetLocalLightDataFromGrid(Header.DataStartIndex + i, EyeIndex);
        // Optional per-light VSM shadow
        if (LocalLight.Internal.VirtualShadowMapId != INDEX_NONE)
            DynamicShadowFactor *= SampleVirtualShadowMapLocal(...);

        DirectLighting += GetDynamicLightingSplit(..., LightData, LightAttenuation, ...);
    }
    return DirectLighting;
}

The critical insight here: GetMaxLightsPerCell() returns ForwardLightStruct.MaxCulledLightsPerCell, which is set at CPU-side light grid construction. This is not a hard limit of 4 — the number of local lights per froxel cell is dynamically determined by the light grid. VR/forward mode typically caps this lower for performance, but the architecture itself supports arbitrary counts up to the grid budget.

关键洞察:GetMaxLightsPerCell() 返回 ForwardLightStruct.MaxCulledLightsPerCell, 该值在 CPU 端光源网格构建时设置。这并非固定的 4 个光源限制——每个 Froxel 单元的局部光源数量由光源网格动态决定。 VR/前向模式出于性能通常将其限制得更低,但架构本身支持不超过网格预算的任意数量。

TBDR Hardware Analysis — Why Forward Wins on MobileTBDR 硬件博弈分析 — 前向渲染为何赢在移动端

Mobile GPUs (Apple A-series, Qualcomm Adreno, ARM Mali) use Tile-Based Deferred Rendering (TBDR) architecture, fundamentally different from desktop's Immediate Mode Rendering (IMR). Understanding this hardware difference is the entire reason UE's mobile renderer exists as a separate class.

移动 GPU(苹果 A 系列、高通 Adreno、ARM Mali)使用基于瓦片的延迟渲染(TBDR)架构, 与桌面端的即时模式渲染(IMR)有根本性差异。理解这种硬件差异,正是 UE 移动端渲染器作为独立类存在的全部原因。

IMR (Desktop GPU)IMR(桌面 GPU)

  • Processes triangles immediately as they arrive
  • 三角形到来后立即处理
  • Framebuffer lives in VRAM (~500 GB/s bandwidth)
  • 帧缓冲存储在 VRAM(~500 GB/s 带宽)
  • GBuffer bandwidth: ~300 MB/frame at 1080p (4 targets × 8 bytes × 1920×1080)
  • GBuffer 带宽:1080p 下约 300 MB/帧(4 目标 × 8 字节 × 1920×1080)
  • Cheap; VRAM bandwidth is massive
  • 开销低;VRAM 带宽极大

TBDR (Mobile GPU)TBDR(移动 GPU)

  • Divides screen into tiles (~16×16 or 32×32 pixels)
  • 将屏幕分割为瓦片(约 16×16 或 32×32 像素)
  • All geometry for a tile is buffered, then the entire tile is shaded in on-chip SRAM (~100–400 GB/s)
  • 一个瓦片的所有几何体被缓冲,然后整个瓦片在片上 SRAM(~100–400 GB/s)中着色
  • Tile result is written to DRAM only once at end — massive bandwidth saving
  • 瓦片结果仅在末尾写入 DRAM 一次——节省大量带宽
  • Reading back from framebuffer (GBuffer pass → lighting pass) forces a tile flush to DRAM — fatal for performance
  • 从帧缓冲读回(GBuffer 通道 → 光照通道)迫使瓦片刷新到 DRAM——对性能致命

A deferred G-Buffer pass at 1080p on mobile writes approximately:

移动端 1080p 下延迟 GBuffer 通道大约写入:

// Approximate G-Buffer bandwidth at 1080p
GBufferA (Normal + ShadingModelID)  = 1920 × 1080 × 4 bytes = ~8 MB  (R8G8B8A8)
GBufferB (Metallic/Spec/Rough)      = 1920 × 1080 × 4 bytes = ~8 MB  (R8G8B8A8)
GBufferC (BaseColor + AO)           = 1920 × 1080 × 4 bytes = ~8 MB  (R8G8B8A8)
SceneDepth (read in lighting pass)  = 1920 × 1080 × 4 bytes = ~8 MB  (R32)
// Write + read back = ~64 MB per frame through off-chip DRAM
// At 60 FPS: ~3.8 GB/s of GBuffer traffic alone
// Mobile LPDDR5 typical bandwidth: ~50 GB/s total — GBuffer consumes ~8% just for base data
The real cost is the round-trip. It's not just writing the GBuffer — it's that the lighting pass must read it back from DRAM tile-by-tile. Each tile-flush-and-refill through DRAM costs power and time. On mobile where thermal and battery budgets are tight, this is a dealbreaker. UE's mobile forward renderer avoids this entirely by keeping all per-pixel data — normal, depth, material properties, light accumulation — in on-chip registers within a single render pass. 真正的开销在于往返。问题不只是写入 GBuffer——而是光照通道必须逐瓦片从 DRAM 读回。每次瓦片刷新和重新填充 DRAM 都消耗功耗和时间。在热量和电池预算紧张的移动端,这是致命的。UE 移动端前向渲染器通过在单个渲染通道内将所有逐像素数据——法线、深度、材质属性、光照累积——保持在片上寄存器中,完全避免了这一问题。

The ESubpassHint::DepthReadSubpass set on BasePassRenderTargets and RHICmdList.NextSubpass() are the API-level signals that enable the driver to keep framebuffer data on-chip across the depth-write subpass and the depth-read subpass, without ever touching DRAM in between.

BasePassRenderTargets 上设置的 ESubpassHint::DepthReadSubpass 以及 RHICmdList.NextSubpass() 是让驱动程序能够在深度写子通道和深度读子通道之间 将帧缓冲数据保持在片上的 API 级信号,期间完全不接触 DRAM。

Key Console Variables关键控制台变量

CVar Default默认值 Effect效果
r.ForwardShading0Enables desktop forward shading path in FDeferredShadingSceneRenderer启用 FDeferredShadingSceneRenderer 中的桌面前向路径
r.Mobile.ForceDepthResolve0Forces a depth resolve by drawing with the depth texture — workaround for some devices强制通过绘制深度纹理来解析深度——某些设备的变通方案
r.Mobile.AdrenoOcclusionMode0Mode 1: defer occlusion queries to after translucency + flush — helps Adreno in GL mode模式 1:将遮挡查询延迟到半透明和刷新之后——有助于 Adreno GL 模式
r.Mobile.CustomDepthForTranslucency1Whether to render CustomDepth/Stencil if any translucency uses it如果半透明材质使用 CustomDepth/Stencil,是否渲染它
r.SceneDepthHZBAsyncCompute00=sync, 1=async as soon as possible, 2=async after light grid compact pass0=同步,1=尽早异步,2=在光源网格紧凑 Pass 后异步
r.AdrenoOcclusionUseFDM0Use FDM (Fragment Density Map) with Adreno occlusion mode — reduces overdraw on VR在 Adreno 遮挡模式下使用 FDM(片段密度贴图)——减少 VR 中的过度绘制
r.shadow.ShadowMapsRenderEarly0Force early shadow rendering even on desktop deferred path (not compatible with VSM)在桌面延迟路径上强制早期阴影渲染(不兼容 VSM)

Source Navigation源码导航

All paths relative to Engine/Source/Runtime/Renderer/Private/ or Engine/Shaders/Private/.

所有路径相对于 Engine/Source/Runtime/Renderer/Private/Engine/Shaders/Private/

Unreal Engine 5.6 · Engine/Source/Runtime/Renderer/Private/
Source analysis · For learning purposes