什么是BasePass

BasePass是位于延迟管线的PrePass之后的一个Pass,Deferred 下不透明 BasePass 以写 GBuffer + 雾效 + 部分间接/自发光为主;半透明等另有路径

为什么需要BasePass

这是一个老生常谈的问题,这里简单提一下,主要作用就是优化光照复杂度,避免无用的光照计算

UE5 BasePass

CPU端

设定GBuffer及Format

  • 位置:GBufferInfo.cpp->FetchLegacyGBufferInfo()

  • 实现

    Info.Targets[0].Init(GBT_Unorm_11_11_10,  TEXT("Lighting"), false,  true,  true,  true);
    Info.Targets[1].Init(NormalGBufferFormatTarget,TEXT("GBufferA"), false,  true,  true,  true);
    Info.Targets[2].Init(DiffuseAndSpecularGBufferFormat,   TEXT("GBufferB"), false,  true,  true,  true);
    
    const bool bLegacyAlbedoSrgb = true;
    Info.Targets[3].Init(DiffuseAndSpecularGBufferFormat,  TEXT("GBufferC"), bLegacyAlbedoSrgb && !bHighPrecisionGBuffers,  true,  true,  true);
    
    // This code should match TBasePassPS
    if (Params.bHasVelocity == 0 && Params.bHasTangent == 0)
    {
      TargetGBufferD = 4;
      Info.Targets[4].Init(GBT_Unorm_8_8_8_8,  TEXT("GBufferD"), false,  true,  true,  true);
    
      if (Params.bHasPrecShadowFactor)
      {
          TargetGBufferE = 5;
          Info.Targets[5].Init(GBT_Unorm_8_8_8_8, TEXT("GBufferE"), false, true, true, true);
      }
    }
    else if (Params.bHasVelocity)
    {
      TargetVelocity = 4;
      TargetGBufferD = 5;
    
      // note the false for use extra flags for velocity, not quite sure of all the ramifications, but this keeps it consistent with previous usage
      Info.Targets[4].Init(Params.bUsesVelocityDepth ? GBT_Unorm_16_16_16_16 : (IsAndroidOpenGLESPlatform(Params.ShaderPlatform) ? GBT_Float_16_16 : GBT_Unorm_16_16), TEXT("Velocity"), false, true, true, false);
      Info.Targets[5].Init(GBT_Unorm_8_8_8_8, TEXT("GBufferD"), false, true, true, true);
    
      if (Params.bHasPrecShadowFactor)
      {
          TargetGBufferE = 6;
          Info.Targets[6].Init(GBT_Unorm_8_8_8_8, TEXT("GBufferE"), false, true, true, false);
      }
    }
    else if (Params.bHasTangent)
    {
      TargetGBufferF = 4;
      TargetGBufferD = 5;
      Info.Targets[4].Init(GBT_Unorm_8_8_8_8,  TEXT("GBufferF"), false,  true,  true,  true);
      Info.Targets[5].Init(GBT_Unorm_8_8_8_8, TEXT("GBufferD"), false, true, true, true);
      if (Params.bHasPrecShadowFactor)
      {
          TargetGBufferE = 6;
          Info.Targets[6].Init(GBT_Unorm_8_8_8_8, TEXT("GBufferE"), false, true, true, true);
      }
    }
    else
    {
      // should never hit this path
      check(0);
    }
    

清空GBuffer

static const auto ClearMethodCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearSceneMethod"));

bool bRequiresRHIClear = true;
bool bRequiresFarZQuadClear = false;

if (ClearMethodCVar)
{
    int32 ClearMethod = ClearMethodCVar->GetValueOnRenderThread();

    if (ClearMethod == 0 && !ViewFamily.EngineShowFlags.Game)
    {
        // Do not clear the scene only if the view family is in game mode.
        ClearMethod = 1;
    }

    switch (ClearMethod)
    {
    case 0: // No clear
        bRequiresRHIClear = false;
        bRequiresFarZQuadClear = false;
        break;

    case 1: // RHICmdList.Clear
        bRequiresRHIClear = true;
        bRequiresFarZQuadClear = false;
        break;

    case 2: // Clear using far-z quad
        bRequiresFarZQuadClear = true;
        bRequiresRHIClear = false;
        break;
    }
}

清空GBuffer的方式由控制台指令显示指定,清空方式有三种,默认RHIClear

  1. NoClear
    • 目的:不清除Clear
    • 优点:省带宽
    • 缺点:可能残留垃圾数据
  2. RHIClear
    • 目的:整屏硬件 Clear
    • 优点:实现简单、行为稳定
    • 缺点:整屏带宽
  3. QuadAtMaxZ
    • 目的:在Clear的情况下,节省带宽
    • 优点:使用Prepass深度做深度测试
      • 画一个全屏 Quad,深度设在 MaxZ
      • • 深度测试 CF_GreaterEqual(Reversed-Z)
      • • 只有仍为 MaxZ 的像素通过测试并被写上背景色
      • • 已有几何的像素深度更近 → 不会被覆盖
if (ViewFamily.EngineShowFlags.Wireframe || ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap)
{
    bRequiresRHIClear = true;
    bRequiresFarZQuadClear = false;
}

这里依然在判断如何Clear RT,判断View视口目前是不是线框模式、shader复杂度模式、静止光交错测试模式,是则启用RHIClear,禁用QuadAtMaxZ,也就是说这三种模式不能走prepass深度测试优化

获取GBuffer RenderTargets

TStaticArray<FTextureRenderTargetBinding, MaxSimultaneousRenderTargets> BasePassTextures;
uint32 BasePassTextureCount = SceneTextures.GetGBufferRenderTargets(BasePassTextures);

先获取Base Pass用到的RenderTargets

TArrayView<FTextureRenderTargetBinding> BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);

由于BasePassTextures初始化一次性分配固定大小的数组,RenderTargets可能不会完全占满,这意味着会剩下空位,不能把带有空位的BasePassTextures传给其他对象,因此MakeArrayView执行了去空

FRDGTextureRef BasePassDepthTexture = SceneTextures.Depth.Target;
FLinearColor SceneColorClearValue = FLinearColor::Black;

获取Depth Texture,以及Clear Color

Clear GBuffer Render Targets

  • 决定Clear Color
    if (ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap)
    {
      SceneColorClearValue = FLinearColor(0, 0, 0, kSceneColorClearAlpha);
    }
    else
    {
      SceneColorClearValue = FLinearColor(InViews[0].BackgroundColor.R, InViews[0].BackgroundColor.G, InViews[0].BackgroundColor.B, kSceneColorClearAlpha);
    }
    

    Debug模式Clear Color为黑,正常情况Clear Color为背景色

  • 决定clear方式

    ERenderTargetLoadAction ColorLoadAction = ERenderTargetLoadAction::ELoad;
    
    if (SceneTextures.Color.Target->Desc.ClearValue.GetClearColor() == SceneColorClearValue)
    {
      ColorLoadAction = ERenderTargetLoadAction::EClear;
    }
    else
    {
      ColorLoadAction = ERenderTargetLoadAction::ENoAction;
    }
    
    • EClear:GPU硬件 Clear,clear成ClearColor
    • ENoAction:不 Load、也不 Clear
    • ELoad:保留已有内容
  • Pass Parameter绑定RenderTargets
    auto* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
    PassParameters->RenderTargets = GetRenderTargetBindings(ColorLoadAction, BasePassTexturesView);
    
  • 处理GBufferD
    const FGBufferBindings& GBufferBindings = SceneTextures.Config.GBufferBindings[GBL_Default];
    if (!CVarClearGBufferDBeforeBasePass.GetValueOnRenderThread() && GBufferBindings.GBufferD.Index > 0 && GBufferBindings.GBufferD.Index < (int32)BasePassTextureCount)
    {
      PassParameters->RenderTargets[GBufferBindings.GBufferD.Index].SetLoadAction(ERenderTargetLoadAction::ENoAction);
    }
    

    若明确指出Base Pass执行前,不clear GBufferD,GBufferD设置不clear 不load

    GBufferD 常存 PerObject Data / Custom Data,有时 Base Pass 会完整覆盖,提前清是浪费

  • 当不Clear,也不Load

    GraphBuilder.AddPass(RDG_EVENT_NAME("GBufferClear"), PassParameters, ERDGPassFlags::Raster,
    [PassParameters, ColorLoadAction, SceneColorClearValue](FRDGAsyncTask, FRHICommandList& RHICmdList)
    {
    const FRenderTargetBindingSlots& RenderTargets = PassParameters->RenderTargets;
      FLinearColor ClearColors[MaxSimultaneousRenderTargets];
      FRHITexture* Textures[MaxSimultaneousRenderTargets];
      int32 TextureIndex = 0;
    
      RenderTargets.Enumerate([&](const FRenderTargetBinding& RenderTarget)
          {
              FRHITexture* TextureRHI = RenderTarget.GetTexture()->GetRHI();
              ClearColors[TextureIndex] = TextureIndex == 0 ? SceneColorClearValue : TextureRHI->GetClearColor();
              Textures[TextureIndex] = TextureRHI;
              ++TextureIndex;
          });
    
      // Clear color only; depth-stencil is fast cleared.
      DrawClearQuadMRT(RHICmdList, true, TextureIndex, ClearColors, false, 0, false, 0);
    });
    

    当硬件不做任何处理时,才执行Shader Clear

    Scene Color Clear 为SceneColorClearValue,其他的Clear为各自纹理注册的 GetClearColor()

RenderBasePass

Load GBuffer

FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);

BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);

const bool bAllowReadOnlyDepthBasePass = bIsEarlyDepthComplete
    && !ViewFamily.EngineShowFlags.ShaderComplexity
    && !ViewFamily.UseDebugViewPS()
    && !ViewFamily.EngineShowFlags.Wireframe
    && !ViewFamily.EngineShowFlags.LightMapDensity;

const FExclusiveDepthStencil::Type BasePassDepthStencilAccess =
    bAllowReadOnlyDepthBasePass
    ? FExclusiveDepthStencil::DepthRead_StencilWrite
    : FExclusiveDepthStencil::DepthWrite_StencilWrite;

只有是非Debug模式且prepass执行了,才走DepthRead_StencilWrite,否则DepthWrite_StencilWrite

RenderNaniteBasePass

auto RenderNaniteBasePass = [&](FViewInfo& View, int32 ViewIndex)
{
    Nanite::FRasterResults& RasterResults = NaniteRasterResults[ViewIndex];

    Nanite::DispatchBasePass(
        GraphBuilder,
        NaniteBasePassShadingCommands,
        Renderer,
        SceneTextures,
        BasePassRenderTargets,
        DBufferTextures,
        *Scene,
        View,
        uint32(ViewIndex),
        RasterResults
    );
}

由于Nanite还未接触,这里就不班门弄斧了,只需知道RenderNaniteBasePass渲染的是Nanite物体的GBuffer

Render Base Pass

依然遍历View渲染Base Pass

  • 判断Lumen是否启用
    const bool bLumenGIEnabled = Renderer.GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen;
    
计算渲染状态
FMeshPassProcessorRenderState DrawRenderState;

SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState);
void SetupBasePassState(FExclusiveDepthStencil::Type BasePassDepthStencilAccess, const bool bShaderComplexity, FMeshPassProcessorRenderState& DrawRenderState)
{
    DrawRenderState.SetDepthStencilAccess(BasePassDepthStencilAccess);

    if (bShaderComplexity)
    {
        // Additive blending when shader complexity viewmode is enabled.
        DrawRenderState.SetBlendState(TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One>::GetRHI());
        // Disable depth writes as we have a full depth prepass.
        DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
    }
    else
    {
        // Opaque blending for all G buffer targets, depth tests and writes.
        static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BasePassOutputsVelocityDebug"));
        if (CVar && CVar->GetValueOnRenderThread() == 2)
        {
            DrawRenderState.SetBlendState(TStaticBlendStateWriteMask<CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA, CW_NONE>::GetRHI());
        }
        else
        {
            DrawRenderState.SetBlendState(TStaticBlendStateWriteMask<CW_RGBA, CW_RGBA, CW_RGBA, CW_RGBA>::GetRHI());
        }

        if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite)
        {
            DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());
        }
        else
        {
            DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI());
        }
    }
}
  • DrawRenderState设置深度模板测试
  • 判断是否是ShaderComplexity Debug模式
    • 是:设置适合ShaderComplexity的深度模板测试(不写深度)、Blend测试(Additive)
    • 否:设置opaque blend
    • 判断是否Debug Velocity
      • 是:GBuffer 前6个RT的Color 四通道都写,第七个RT不写Color,第八个默认Color 四通道都写
      • 否:GBuffer 8个RT的Color 四通道都写
    • 判断是否Depth Write
      • 是:启用深度写入,深度测试为深度值小于等于
      • 否:禁用深度写入,深度测试为深度值小于等于
绑定Pass Parameter
BEGIN_SHADER_PARAMETER_STRUCT(FOpaqueBasePassParameters, )
    SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
    SHADER_PARAMETER_STRUCT_REF(FReflectionCaptureShaderData, ReflectionCapture)
    SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FOpaqueBasePassUniformParameters, BasePass)
    SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
    RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()

BEGIN_SHADER_PARAMETER_STRUCT(FViewShaderParameters, )
    SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
    SHADER_PARAMETER_STRUCT_REF(FInstancedViewUniformShaderParameters, InstancedView)
END_SHADER_PARAMETER_STRUCT()

BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FReflectionCaptureShaderData,)
    SHADER_PARAMETER_ARRAY(FVector4f,PositionHighAndRadius,[GMaxNumReflectionCaptures])
    // W is unused
    SHADER_PARAMETER_ARRAY(FVector4f,PositionLow,[GMaxNumReflectionCaptures])
    // R is brightness, G is array index, B is shape
    SHADER_PARAMETER_ARRAY(FVector4f,CaptureProperties,[GMaxNumReflectionCaptures])
    SHADER_PARAMETER_ARRAY(FVector4f,CaptureOffsetAndAverageBrightness,[GMaxNumReflectionCaptures])
    // Stores the box transform for a box shape, other data is packed for other shapes
    SHADER_PARAMETER_ARRAY(FMatrix44f,BoxTransform,[GMaxNumReflectionCaptures])
    SHADER_PARAMETER_ARRAY(FVector4f,BoxScales,[GMaxNumReflectionCaptures])
END_GLOBAL_SHADER_PARAMETER_STRUCT()

BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FOpaqueBasePassUniformParameters,)
    SHADER_PARAMETER_STRUCT(FSharedBasePassUniformParameters, Shared)
    SHADER_PARAMETER_STRUCT(FSubstrateBasePassUniformParameters, Substrate)
    // Forward shading 
    SHADER_PARAMETER(int32, UseForwardScreenSpaceShadowMask)
    SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ForwardScreenSpaceShadowMaskTexture)
    SHADER_PARAMETER_RDG_TEXTURE(Texture2D, IndirectOcclusionTexture)
    SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ResolvedSceneDepthTexture)
    // DBuffer decals
    SHADER_PARAMETER_STRUCT_INCLUDE(FDBufferParameters, DBuffer)
    // Misc
    SHADER_PARAMETER_TEXTURE(Texture2D, PreIntegratedGFTexture)
    SHADER_PARAMETER_SAMPLER(SamplerState, PreIntegratedGFSampler)
    SHADER_PARAMETER(int32, Is24BitUnormDepthStencil)
    SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<float4>, EyeAdaptationBuffer)
END_GLOBAL_SHADER_PARAMETER_STRUCT()

BEGIN_SHADER_PARAMETER_STRUCT(FInstanceCullingDrawParams, )
    RDG_BUFFER_ACCESS(DrawIndirectArgsBuffer, ERHIAccess::IndirectArgs)
    RDG_BUFFER_ACCESS(InstanceIdOffsetBuffer, ERHIAccess::VertexOrIndexBuffer)
    SHADER_PARAMETER(uint32, InstanceDataByteOffset) // offset into per-instance buffer
    SHADER_PARAMETER(uint32, IndirectArgsByteOffset) // offset into indirect args buffer
    SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FInstanceCullingGlobalUniforms, InstanceCulling)    
    SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene)
    SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FBatchedPrimitiveParameters, BatchedPrimitive)
END_SHADER_PARAMETER_STRUCT()

Pass Parameter包含View、ReflectionCaptureData、InstanceCulling、OpaqueBasePassUniform

FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
PassParameters->View = View.GetShaderParameters();
PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, bLumenGIEnabled);
PassParameters->RenderTargets = BasePassRenderTargets;
PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::BasePass);

Dispatch Base & Sky Pass
  • 先判断View是否可以执行Base Pass
    const bool bShouldRenderView = View.ShouldRenderView();
    
    bool ShouldRenderView() const 
    {
      if (bHasNoVisiblePrimitive)
      {
          return false;
      }
      else if (!bIsSinglePassStereo)
      {
          return true;
      }
      else if (bIsSinglePassStereo && !IStereoRendering::IsASecondaryPass(StereoPass))
      {
          return true;
      }
      else
      {
          return false;
      }
    }
    

    若没有可见图元不画

  • 若View可以执行Base Pass,且MeshDrawCommandPasses[EMeshPass::BasePass]不为null,Dispatch Base Pass

    if (auto* Pass = View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass]; Pass && bShouldRenderView)
    {
      Pass->BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
    
      GraphBuilder.AddDispatchPass(
          RDG_EVENT_NAME("BasePassParallel"),
          PassParameters,
          ERDGPassFlags::Raster,
          [Pass, PassParameters](FRDGDispatchPassBuilder& DispatchPassBuilder)
      {
          Pass->Dispatch(DispatchPassBuilder, &PassParameters->InstanceCullingDrawParams);
      });
    }
    
  • Dispatch Nanite Base Pass
    const bool bShouldRenderViewForNanite = bNaniteEnabled
      && !View.bHasNoVisiblePrimitive
      && (!bDrawSceneViewsInOneNanitePass || ViewIndex == 0); 
    if (bShouldRenderViewForNanite)
    {
      check(Renderer.ShouldRenderPrePass());
    
      RenderNaniteBasePass(View, ViewIndex);
    }
    
  • Dispatch Sky Atmosphere
    if (auto* Pass = View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass]; Pass && bShouldRenderView && View.Family->EngineShowFlags.Atmosphere)
    {
      FOpaqueBasePassParameters* SkyPassPassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
      SkyPassPassParameters->BasePass = PassParameters->BasePass;
      SkyPassPassParameters->RenderTargets = BasePassRenderTargets;
      SkyPassPassParameters->View = View.GetShaderParameters();
      SkyPassPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
    
      // Remove all but the SceneColor
      for (uint32 i = 1; i < MaxSimultaneousRenderTargets; ++i)
      {
          SkyPassPassParameters->RenderTargets[i] = FRenderTargetBinding();
      }
    
      Pass->BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassPassParameters->InstanceCullingDrawParams);
    
      GraphBuilder.AddDispatchPass(
          RDG_EVENT_NAME("SkyPassParallel"),
          SkyPassPassParameters,
          ERDGPassFlags::Raster,
          [Pass, SkyPassPassParameters](FRDGDispatchPassBuilder& DispatchPassBuilder)
      {
          Pass->Dispatch(DispatchPassBuilder, &SkyPassPassParameters->InstanceCullingDrawParams);
      });
    }
    

绑定渲染状态

依然Process获取绑定Shader、设置DepthStencilc测试、排序,流程与一般的Pass类似,不再赘述,这里只提两个额外的操作

  • 设置Decal Stencil Ref
    if (bEnableReceiveDecalOutput)
    {
    uint8 StencilValue = 0;
    StencilValue = 
        GET_STENCIL_BIT_MASK(RECEIVE_DECAL, PrimitiveSceneProxy ? !!PrimitiveSceneProxy->ReceivesDecals() : 0x00)
      | GET_STENCIL_BIT_MASK(RAY_TRACING_REPRESENTATION, bHasRayTracingRepresentation)
      | STENCIL_LIGHTING_CHANNELS_MASK(PrimitiveSceneProxy ? PrimitiveSceneProxy->GetLightingChannelStencilValue() : 0x00);
      DrawRenderState.SetStencilRef(StencilValue);
    }
    

    用于标记Decal 渲染区域

  • 若材质类型是Translucent,而非opaque,则设置适合Translucent的渲染状态

    if (bTranslucentBasePass)
    {
      SetTranslucentRenderState(DrawRenderState, MaterialResource, GShaderPlatformForFeatureLevel[FeatureLevel], TranslucencyPassType);
    }
    
  • 绑定的Shader
    • VS Shader
    #define IMPLEMENT_BASEPASS_VERTEXSHADER_TYPE(LightMapPolicyType,LightMapPolicyName) \
        typedef TBasePassVS< LightMapPolicyType > TBasePassVS##LightMapPolicyName ; \
        IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TBasePassVS##LightMapPolicyName,TEXT("/Engine/Private/BasePassVertexShader.usf"),TEXT("Main"),SF_Vertex);
    
    
    • PS Shader
    #define IMPLEMENT_BASEPASS_PIXELSHADER_TYPE(LightMapPolicyType,LightMapPolicyName,bEnableSkyLight,SkyLightName,GBufferLayout,LayoutName) \
        typedef TBasePassPS<LightMapPolicyType, bEnableSkyLight, GBufferLayout> TBasePassPS##LightMapPolicyName##SkyLightName##LayoutName; \
        IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TBasePassPS##LightMapPolicyName##SkyLightName##LayoutName,TEXT("/Engine/Private/BasePassPixelShader.usf"),TEXT("MainPS"),SF_Pixel);
    
    #define IMPLEMENT_BASEPASS_COMPUTESHADER_TYPE(LightMapPolicyType,LightMapPolicyName,bEnableSkyLight,SkyLightName,bVoxel,VoxelName) \
        typedef TBasePassCS<LightMapPolicyType, bEnableSkyLight, bVoxel, SF_Compute> TBasePassCS##LightMapPolicyName##SkyLightName##VoxelName; \
        IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TBasePassCS##LightMapPolicyName##SkyLightName##VoxelName,TEXT("/Engine/Private/BasePassPixelShader.usf"),TEXT("MainCS"),SF_Compute);
    
    

GPU端


GPU端流程大致如上图所示

BasePassVertexShader

  • 依然是计算World Position、Clip Position
    FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
    float4 WorldPositionExcludingWPO = VertexFactoryGetWorldPosition(Input, VFIntermediates);
    float4 WorldPosition = WorldPositionExcludingWPO;
    float4 ClipSpacePosition;
    
    float3x3 TangentToLocal = VertexFactoryGetTangentToLocal(Input, VFIntermediates); 
    FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, WorldPosition.xyz, TangentToLocal);
    
    WorldPosition.xyz += GetMaterialWorldPositionOffset(VertexParameters);
    ApplyMaterialFirstPersonTransform(VertexParameters, WorldPosition.xyz);
    
    float4 RasterizedWorldPosition = VertexFactoryGetRasterizedWorldPosition(Input, VFIntermediates, WorldPosition);
    ClipSpacePosition = mul(RasterizedWorldPosition, ResolvedView.TranslatedWorldToClip)
    
    Output.Position = INVARIANT(ClipSpacePosition);
    
  • 半透明雾效
    • 半透明且没有启用per-pixel fog,或 Forward Shading 不透明物体且启用 opaque vertex fogging
    #define NEEDS_BASEPASS_VERTEX_FOGGING     (TRANSLUCENCY_NEEDS_BASEPASS_FOGGING && !MATERIAL_COMPUTE_FOG_PER_PIXEL || OPAQUE_NEEDS_BASEPASS_FOGGING && PROJECT_VERTEX_FOGGING_FOR_OPAQUE)
    
    
    • 计算高度雾
    Output.BasePassInterpolants.VertexFog = CalculateHeightFog(WorldPosition.xyz - ResolvedView.TranslatedWorldCameraOrigin, EyeIndex, ResolvedView); // WorldPosition is in fact TranslatedWorldPosition 
    
    • 计算大气透视
    const float OneOverPreExposure = ResolvedView.OneOverPreExposure;
    
    #if PROJECT_SUPPORT_SKY_ATMOSPHERE && BASEPASS_SKYATMOSPHERE_AERIALPERSPECTIVE && MATERIAL_IS_SKY==0 // Do not apply aerial perpsective on sky materials
    if (ResolvedView.SkyAtmosphereApplyCameraAerialPerspectiveVolume > 0.0f)
    {
        Output.BasePassInterpolants.VertexFog = GetAerialPerspectiveLuminanceTransmittanceWithFogOver(
            ResolvedView.RealTimeReflectionCapture, ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeSizeAndInvSize,
            Output.Position, (WorldPosition.xyz - ResolvedView.TranslatedWorldCameraOrigin) * CM_TO_SKY_UNIT,
            View.CameraAerialPerspectiveVolume, View.CameraAerialPerspectiveVolumeSampler,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolutionInv,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolution,
            ResolvedView.SkyAtmosphereAerialPerspectiveStartDepthKm,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKm,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKmInv,
            OneOverPreExposure, Output.BasePassInterpolants.VertexFog);
    }
    #endif
    
    • 给半透明添加局部体积雾
    #if LOCAL_FOG_VOLUME_ON_TRANSLUCENT
        float4 VertexClipSpacePosition          = mul(float4(WorldPosition.xyz, 1), ResolvedView.TranslatedWorldToClip);
        float2 SvPosition                       = (VertexClipSpacePosition.xy / VertexClipSpacePosition.w * float2(.5f, -.5f) + .5f) * ResolvedView.ViewSizeAndInvSize.xy;
        uint2 TilePos                           = clamp(uint2(SvPosition.xy / float(LFVTilePixelSize)), uint2(0, 0), LFVTileDataResolution - 1);
        float4 LFVContribution                  = GetLFVContribution(ResolvedView, TilePos, WorldPosition.xyz);
        Output.BasePassInterpolants.VertexFog   = float4(LFVContribution.rgb + Output.BasePassInterpolants.VertexFog.rgb * LFVContribution.a, LFVContribution.a * Output.BasePassInterpolants.VertexFog.a);
    #endif
    
    #if MATERIAL_ENABLE_TRANSLUCENCY_CLOUD_FOGGING
        if (TranslucentBasePass.ApplyVolumetricCloudOnTransparent > 0.0f)
        {
            Output.BasePassInterpolants.VertexFog = GetCloudLuminanceTransmittanceOverFog(
                Output.Position, WorldPosition.xyz, ResolvedView.TranslatedWorldCameraOrigin,
                TranslucentBasePass.VolumetricCloudColor, TranslucentBasePass.VolumetricCloudColorSampler,
                TranslucentBasePass.VolumetricCloudDepth, TranslucentBasePass.VolumetricCloudDepthSampler,
                OneOverPreExposure, Output.BasePassInterpolants.VertexFog, TranslucentBasePass.SoftBlendingDistanceKm, 
                TranslucentBasePass.VolumetricCloudColorUVScale, TranslucentBasePass.VolumetricCloudColorUVMax);
        }
    #endif
    
    
    • 计算顶点着色

    • 预计算间接光 per-vertex 采样

    #if PRECOMPUTED_IRRADIANCE_VOLUME_LIGHTING && TRANSLUCENCY_ANY_PERVERTEX_LIGHTING
        float3 BrickTextureUVs = ComputeVolumetricLightmapBrickTextureUVs(WorldPositionForVertexLighting);
    
        #if TRANSLUCENCY_LIGHTING_VOLUMETRIC_PERVERTEX_NONDIRECTIONAL
            FOneBandSHVectorRGB IrradianceSH = GetVolumetricLightmapSH1(BrickTextureUVs);
            Output.BasePassInterpolants.VertexIndirectAmbient = float3(IrradianceSH.R.V, IrradianceSH.G.V, IrradianceSH.B.V);
        #elif TRANSLUCENCY_LIGHTING_VOLUMETRIC_PERVERTEX_DIRECTIONAL
            // Need to interpolate directional lighting so we can incorporate a normal in the pixel shader
            FTwoBandSHVectorRGB IrradianceSH = GetVolumetricLightmapSH2(BrickTextureUVs);
            Output.BasePassInterpolants.VertexIndirectSH[0] = IrradianceSH.R.V;
            Output.BasePassInterpolants.VertexIndirectSH[1] = IrradianceSH.G.V;
            Output.BasePassInterpolants.VertexIndirectSH[2] = IrradianceSH.B.V;
        #endif
    #endif
    

这里提到的雾效以后会在雾效专栏分析

BasePassPixelShader

Pixel Shader的主要逻辑由FPixelShaderInOut_MainPS()驱动

初始化

FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(Interpolants, In.SvPosition);
FPixelMaterialInputs PixelMaterialInputs;
  • 计算Lightmap VT Page Table
    VTPageTableResult LightmapVTPageTableResult = (VTPageTableResult)0.0f;
    #if LIGHTMAP_VT_ENABLED
    {
        LightmapUVType LightmapUV0, LightmapUV1;
        uint LightmapDataIndex;
        GetLightMapCoordinates(Interpolants, LightmapUV0, LightmapUV1, LightmapDataIndex);
        LightmapVTPageTableResult = LightmapGetVTSampleInfo(LightmapUV0, LightmapDataIndex, In.SvPosition.xy);
    }
    #endif
    
  • 从Lightmap采样AO
    #if HQ_TEXTURE_LIGHTMAP && USES_AO_MATERIAL_MASK && !MATERIAL_SHADINGMODEL_UNLIT
    {
        LightmapUVType LightmapUV0, LightmapUV1;
        uint LightmapDataIndex;
        GetLightMapCoordinates(Interpolants, LightmapUV0, LightmapUV1, LightmapDataIndex);
        // Must be computed before BaseColor, Normal, etc are evaluated
        MaterialParameters.AOMaterialMask = GetAOMaterialMask(LightmapVTPageTableResult, ScaleLightmapUV(LightmapUV0, float2(1, 2)), LightmapDataIndex, In.SvPosition.xy);
    }
    #endif
    
  • 计算World Position,且使用World Position计算法线、UV、ScreenPos等
    float4 ScreenPosition = SvPositionToResolvedScreenPosition(In.SvPosition);
    float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(In.SvPosition);
    

深度偏移与裁剪

  • 深度偏移
    #if OUTPUT_PIXEL_DEPTH_OFFSET
    ApplyPixelDepthOffsetForBasePass(MaterialParameters, PixelMaterialInputs, BasePassInterpolants, Out.Depth);
    
    #if APPLE_DEPTH_BIAS_HACK
    Out.Depth -= APPLE_DEPTH_BIAS_VALUE;
    #endif
    #endif
    
  • 当Mask没有在 PrePass 执行过 Clip,这里执行Clip
    #if !EARLY_Z_PASS_ONLY_MATERIAL_MASKING
    if (!bEditorWeightedZBuffering)
    {
    #if MATERIALBLENDING_MASKED_USING_COVERAGE
        Out.Coverage = DiscardMaterialWithPixelCoverage(MaterialParameters, PixelMaterialInputs);
    #else
        GetMaterialCoverageAndClipping(MaterialParameters, PixelMaterialInputs);
    #endif
    }
    #endif
    

    分成两条路:

    • 普通Mask:Clip
    • Alpha-to-Coverage:写 Coverage

获取材质属性,写入GBuffer

half3 BaseColor = GetMaterialBaseColor(PixelMaterialInputs);
half  Metallic = GetMaterialMetallic(PixelMaterialInputs);
half  Specular = GetMaterialSpecular(PixelMaterialInputs);

float Roughness = GetMaterialRoughness(PixelMaterialInputs);
float Anisotropy = GetMaterialAnisotropy(PixelMaterialInputs);
uint ShadingModel = GetMaterialShadingModel(PixelMaterialInputs);
half Opacity = GetMaterialOpacity(PixelMaterialInputs);
float MaterialAO = GetMaterialAmbientOcclusion(PixelMaterialInputs);

float4 SubsurfaceData = GetMaterialSubsurfaceData(PixelMaterialInputs);

const float BaseMaterialCoverageOverWater = Opacity;
const float WaterVisibility = 1.0 - BaseMaterialCoverageOverWater;

float3 VolumetricLightmapBrickTextureUVs;
#if PRECOMPUTED_IRRADIANCE_VOLUME_LIGHTING
    VolumetricLightmapBrickTextureUVs = ComputeVolumetricLightmapBrickTextureUVs(WSHackToFloat(GetWorldPosition(MaterialParameters)));
#endif

获取材质属性

FGBufferData GBuffer = (FGBufferData)0;

GBuffer.GBufferAO = MaterialAO;
GBuffer.PerObjectGBufferData = GetPrimitive_PerObjectGBufferData(MaterialParameters.PrimitiveId);
GBuffer.Depth = MaterialParameters.ScreenPosition.w;
GBuffer.PrecomputedShadowFactors = GetPrecomputedShadowMasks(LightmapVTPageTableResult, Interpolants, MaterialParameters, VolumetricLightmapBrickTextureUVs);

SetGBufferForShadingModel(
        GBuffer,
        MaterialParameters,
        PixelMaterialInputs,
        Opacity,
        BaseColor,
        Metallic,
        Specular,
        Roughness,
        Anisotropy,
        SubsurfaceColor,
        SubsurfaceProfile,
        Dither,
        ShadingModel
        );

将材质属性填进对应GBuffer

DBuffer

if ((GetPrimitiveData(MaterialParameters).Flags & PRIMITIVE_SCENE_DATA_FLAG_DECAL_RECEIVER) != 0 && View.ShowDecalsMask > 0)
{
    uint ValidDBufferTargetMask = GetDBufferTargetMask(uint2(In.SvPosition.xy)) & MATERIALDECALRESPONSEMASK;

    if (ValidDBufferTargetMask)
    {
        float2 BufferUV = SvPositionToBufferUV(In.SvPosition);
        FDBufferData DBufferData = GetDBufferData(BufferUV, ValidDBufferTargetMask);
        ApplyDBufferData(DBufferData, MaterialParameters.WorldNormal, SubsurfaceColor, Roughness, BaseColor, Metallic, Specular);
    }
}

获取DBuffer信息,写入材质属性

光照前准备工作

  • 获取预计算阴影
    GBuffer.PrecomputedShadowFactors = GetPrecomputedShadowMasks(LightmapVTPageTableResult, Interpolants, MaterialParameters, VolumetricLightmapBrickTextureUVs);
    
  • 需要时写入GBuffer & 获取Velocity
    #if WRITES_VELOCITY_TO_GBUFFER
    BRANCH
    if ((GetPrimitiveData(MaterialParameters).Flags & PRIMITIVE_SCENE_DATA_FLAG_OUTPUT_VELOCITY) != 0)
    {
        // 2d velocity, includes camera an object motion
    #if IS_NANITE_PASS
        float3 Velocity = Calculate3DVelocity(MaterialParameters.ScreenPosition, MaterialParameters.PrevScreenPosition);
    #else
        float3 Velocity = Calculate3DVelocity(MaterialParameters.ScreenPosition, BasePassInterpolants.VelocityPrevScreenPosition);
    #endif
        float TemporalResponsiveness = GetMaterialTemporalResponsiveness(MaterialParameters);   
        float4 EncodedVelocity = EncodeVelocityToTexture(Velocity, (GetPrimitiveData(MaterialParameters).Flags & PRIMITIVE_SCENE_DATA_FLAG_HAS_PIXEL_ANIMATION) != 0, TemporalResponsiveness);
    
    #if USES_GBUFFER
        GBuffer.Velocity = EncodedVelocity;
    #else
        OutVelocity = EncodedVelocity;
    #endif
    }
    #endif
    
  • 获取F0、DiffuseColor
    GBuffer.SpecularColor = ComputeF0(Specular, BaseColor, Metallic);
    
    GBuffer.DiffuseColor = BaseColor - BaseColor * Metallic;
    
  • 获取SubsurfaceProfile
    if (UseSubsurfaceProfile(GBuffer.ShadingModelID))
    {
      AdjustBaseColorAndSpecularColorForSubsurfaceProfileLighting(BaseColor, GBuffer.SpecularColor, Specular, bChecker);
    }
    

    checkerboard 下拆分 diffuse/specular

  • 获取BentNormal

    float3 InputBentNormal = MaterialParameters.WorldNormal;
    
    BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT && CLEAR_COAT_BOTTOM_NORMAL)
    {
      const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 4) - (512.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
      InputBentNormal = OctahedronToUnitVector(oct1);
    }
    

    若不处于clear coat,BentNormal为WorldNormal,否则需要额外计算

    const FShadingOcclusion ShadingOcclusion = ApplyBentNormal(MaterialParameters.CameraVector, InputBentNormal, GetWorldBentNormalZero(MaterialParameters), GBuffer.Roughness, MaterialAO);
    
  • AOMultiBounce
    GBuffer.GBufferAO = AOMultiBounce( Luminance( GBuffer.SpecularColor ), ShadingOcclusion.SpecOcclusion ).g;
    

    对已经有的AO做一次有颜色的近似修正,用 albedo + 单次 AO 拟合“缝隙里多次反弹”的结果

  • 计算漫反射间接采样方向是否被遮挡的AO

    #if !SUBSTRATE_INLINE_SINGLELAYERWATER
    GBuffer.DiffuseIndirectSampleOcclusion = GetDiffuseIndirectSampleOcclusion(GBuffer, MaterialParameters.CameraVector, MaterialParameters.WorldNormal, GetWorldBentNormalZero(MaterialParameters), In.SvPosition.xy, MaterialAO);
    #endif    
    

    根据bent normal、material AO计算带方向的 AO

    GBuffer.DiffuseIndirectSampleOcclusion // uint,每位对应一条采样方向
    给后续间接漫反射用的 per-direction occlusion mask

光照计算

  • 计算预计算简接光照与天光

    • 计算间接漫反射使用的Dir与Alebdo
    float3 DiffuseDir = ShadingOcclusion.BentNormal;
    float3 DiffuseColorForIndirect = GBuffer.DiffuseColor;
    

    当然不同shading model的Dir、Alebdo都不同

    • 计算预计算间接光是否需要计算背面
    const bool bEvaluateBackface = GetShadingModelRequiresBackfaceLighting(GBuffer.ShadingModelID);
    

    只有树叶需要

    • 计算预计算简介光照与天光
    GetPrecomputedIndirectLightingAndSkyLight(MaterialParameters, Interpolants, BasePassInterpolants, LightmapVTPageTableResult, bEvaluateBackface, DiffuseDir, VolumetricLightmapBrickTextureUVs, DiffuseIndirectLighting, SubsurfaceIndirectLighting, IndirectIrradiance);
    
  • 计算得到的间接光写入DiffuseColor
    DiffuseColor += (DiffuseIndirectLighting * DiffuseColorForIndirect + SubsurfaceIndirectLighting * SubsurfaceColor) * AOMultiBounce( GBuffer.BaseColor, ShadingOcclusion.DiffOcclusion );
    
  • 混合雾、大气、云、体积阴影
    • Fog组成
    float4 Fogging; 
    

    .rgb = 雾本身发出/散射的光(in-scatter)

    .a = 到表面的透过率 transmittance(1=全透,0=全挡)

    • 使用顶点雾还是像素雾
    #if NEEDS_BASEPASS_VERTEX_FOGGING
        float4 HeightFogging = BasePassInterpolants.VertexFog;
    #elif NEEDS_BASEPASS_PIXEL_FOGGING
        float4 HeightFogging = CalculateHeightFog(MaterialParameters.WorldPosition_CamRelative, EyeIndex, ResolvedView);
        #if LOCAL_FOG_VOLUME_ON_TRANSLUCENT
        const float4 LocalFogVolumeContrib = BasePassInterpolants.VertexFog;
        HeightFogging = float4(LocalFogVolumeContrib.rgb + HeightFogging.rgb * LocalFogVolumeContrib.a, LocalFogVolumeContrib.a * HeightFogging.a);
        #endif // LOCAL_FOG_VOLUME_ON_TRANSLUCENT
    #else
        float4 HeightFogging = float4(0,0,0,1);
    #endif
    

    若使用顶点雾,直接使用Vertex Shader的计算结果即可

    若使用像素雾,则需计算高度雾

    若半透明且局部雾,Vertex Shader计算的 Local Fog Volume 再 over 到高度雾上

    • 应用在半透明的像素级别局部雾
    float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(In.SvPosition);
    uint2 TilePos = clamp(uint2(In.SvPosition.xy / float(LFVTilePixelSize)), uint2(0, 0), LFVTileDataResolution - 1);
    float4 LocalFogVolumeContrib = GetLFVContribution(ResolvedView, TilePos, TranslatedWorldPosition);
    Fogging = float4(LocalFogVolumeContrib.rgb + Fogging.rgb * LocalFogVolumeContrib.a, LocalFogVolumeContrib.a * Fogging.a);
    

    上一步叠加的是顶点级别的局部雾,这里叠加的是像素级别

    • 步骤
      • 按像素位置查 LFV tile → GetLFVContribution
      • over 进 Fog
    • Volumetric Fog

    float3 VolumeUV = ComputeVolumeUV(MaterialParameters.AbsoluteWorldPosition, ResolvedView.WorldToClip, ResolvedView);
    Fogging = CombineVolumetricFog(Fogging, VolumeUV, EyeIndex, GBuffer.Depth, ResolvedView);
    

    采样 3D 体积雾,合并进 Fogging

    • 计算雾效的阴影
    float2 NDC = MaterialParameters.ScreenPosition.xy / MaterialParameters.ScreenPosition.w;
    float2 ScreenUV = NDC * ResolvedView.ScreenPositionScaleBias.xy + ResolvedView.ScreenPositionScaleBias.wz;
    float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(In.SvPosition);
    float4 HeterogeneousVolumeResult = saturate(AVSM_SampleCameraRadianceAndTransmittance4(ScreenUV, TranslatedWorldPosition, ResolvedView.TranslatedWorldCameraOrigin));
    Fogging.rgb = HeterogeneousVolumeResult.rgb + Fogging.rgb * HeterogeneousVolumeResult.a;
    Fogging.a *= HeterogeneousVolumeResult.a;
    

    这是自适应Volumetric Shadow Map,后续专门开篇讲解

    • 应用大气散射
    if (ResolvedView.SkyAtmosphereApplyCameraAerialPerspectiveVolume > 0.0f)
    {
        // Sample the aerial perspective (AP).
        Fogging = GetAerialPerspectiveLuminanceTransmittanceWithFogOver(
            ResolvedView.RealTimeReflectionCapture, ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeSizeAndInvSize,
            NDCPosition, MaterialParameters.WorldPosition_CamRelative * CM_TO_SKY_UNIT,
            View.CameraAerialPerspectiveVolume, SkyAtmAerialPerspecSharedSampler,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolutionInv,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthResolution,
            ResolvedView.SkyAtmosphereAerialPerspectiveStartDepthKm,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKm,
            ResolvedView.SkyAtmosphereCameraAerialPerspectiveVolumeDepthSliceLengthKmInv,
            OneOverPreExposure, Fogging);
    }
    
    • 若半透明,应用体积云
    if (TranslucentBasePass.ApplyVolumetricCloudOnTransparent > 0.0f)
    {
        Fogging = GetCloudLuminanceTransmittanceOverFog(
            NDCPosition, GetTranslatedWorldPosition(MaterialParameters), ResolvedView.TranslatedWorldCameraOrigin,
            TranslucentBasePass.VolumetricCloudColor, TranslucentBasePass.VolumetricCloudColorSampler,
            TranslucentBasePass.VolumetricCloudDepth, TranslucentBasePass.VolumetricCloudDepthSampler,
            OneOverPreExposure, Fogging, TranslucentBasePass.SoftBlendingDistanceKm, 
            TranslucentBasePass.VolumetricCloudColorUVScale, TranslucentBasePass.VolumetricCloudColorUVMax);
    }
    

Emissive与特殊模型

  • 计算半透明光照体积
    if (GBuffer.ShadingModelID == SHADINGMODELID_DEFAULT_LIT || GBuffer.ShadingModelID == SHADINGMODELID_SUBSURFACE)
    {
      float3 TLVDiffuseLighting;
      float3 TLVSpecularLighting;
      GetTranslucencyVolumeLighting(MaterialParameters, PixelMaterialInputs, BasePassInterpolants, GBuffer, IndirectIrradiance, TLVDiffuseLighting, TLVSpecularLighting);
      Color += TLVDiffuseLighting;
      Color += TLVSpecularLighting;
    }
    
  • 得到材质蓝图传递的Emissive,合并DiffuseColor、Emissive
    Emissive = GetMaterialEmissive(PixelMaterialInputs);
    
    Color += DiffuseColor;
    Color += Emissive;
    
  • 计算Single Layer Water的体积光照
    #if MATERIAL_SHADINGMODEL_SINGLELAYERWATER || SUBSTRATE_INLINE_SINGLELAYERWATER
    {
        const bool CameraIsUnderWater = false;  // Fade out the material contribution over to water contribution according to material opacity.
        float3 SunIlluminance = ResolvedView.DirectionalLightColor.rgb * PI;    // times PI because it is divided by PI on CPU (=luminance) and we want illuminance here. 
        float3 WaterDiffuseIndirectIlluminance = DiffuseIndirectLighting * PI;// DiffuseIndirectLighting is luminance. So we need to multiply by PI to get illuminance.
    #if USE_DEVELOPMENT_SHADERS
        SunIlluminance = lerp(SunIlluminance, 0.0f, View.UnlitViewmodeMask);
        WaterDiffuseIndirectIlluminance = lerp(WaterDiffuseIndirectIlluminance, PI, View.UnlitViewmodeMask);
    #endif
        const bool bSeparateWaterMainDirLightLuminance = (SINGLE_LAYER_WATER_SEPARATED_MAIN_LIGHT > 0) && SingleLayerWater.bSeparateMainDirLightLuminance;
    
        // Evaluate Fresnel effect
        const float3 N = MaterialParameters.WorldNormal;
        const float3 V = MaterialParameters.CameraVector;
        const float3 EnvBrdf = EnvBRDF(GBuffer.SpecularColor, GBuffer.Roughness, max(0.0, dot(N, V)));
    
    #if SINGLE_LAYER_WATER_SHADING_QUALITY == SINGLE_LAYER_WATER_SHADING_QUALITY_MOBILE_WITH_DEPTH_TEXTURE
        const float4 NullDistortionParams = 1.0f;
        WaterVolumeLightingOutput WaterLighting = EvaluateWaterVolumeLighting(
            MaterialParameters, PixelMaterialInputs, ResolvedView,
            DirectionalLightShadow * DirectionalLightCloudShadow,
            SingleLayerWater.SceneDepthWithoutSingleLayerWaterTexture, SingleLayerWaterSceneDepthSampler, // Scene depth texture
            SingleLayerWater.SceneWithoutSingleLayerWaterTextureSize,
            SingleLayerWater.SceneWithoutSingleLayerWaterInvTextureSize,
            Specular, NullDistortionParams,
            SunIlluminance, WaterDiffuseIndirectIlluminance, EnvBrdf,
            CameraIsUnderWater, WaterVisibility, EyeIndex,
            bSeparateWaterMainDirLightLuminance, SeparatedWaterMainDirLightLuminance);
    
        // Add water luminance contribution
        Color += WaterLighting.Luminance;
        // Combine top layer opacity with water transmittance (grey scale)
        Opacity = 1.0 - ((1.0 - Opacity) * dot(WaterLighting.WaterToSceneToLightTransmittance, float3(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0)));
    #else
        Color += EvaluateWaterVolumeLighting(
            MaterialParameters, PixelMaterialInputs, ResolvedView,
            DirectionalLightShadow * DirectionalLightCloudShadow,
            SingleLayerWater.SceneDepthWithoutSingleLayerWaterTexture, SingleLayerWaterSceneDepthSampler,
            SingleLayerWater.SceneWithoutSingleLayerWaterTextureSize,
            SingleLayerWater.SceneWithoutSingleLayerWaterInvTextureSize,
            SingleLayerWater.SceneColorWithoutSingleLayerWaterTexture, SingleLayerWaterSceneColorSampler,
            SingleLayerWater.SceneWithoutSingleLayerWaterMinMaxUV.xy,
            SingleLayerWater.SceneWithoutSingleLayerWaterMinMaxUV.zw,
            SingleLayerWater.RefractionMaskTexture,
            Specular, SingleLayerWater.DistortionParams,
            SunIlluminance, WaterDiffuseIndirectIlluminance, EnvBrdf,
            CameraIsUnderWater, WaterVisibility, EyeIndex,
            bSeparateWaterMainDirLightLuminance, SeparatedWaterMainDirLightLuminance
        #if USE_LIGHT_FUNCTION_ATLAS
            , GetLocalLightFunctionCommon(SvPositionToResolvedTranslatedWorld(In.SvPosition), GetDirectionalLightData().LightFunctionAtlasLightIndex)
        #endif
            ).Luminance;
    #endif
    }
    #endif
    

    在普通表面 lit 之后,再算 水面下的水体散射/透射/折射相关亮度,加进 Color

  • 计算Thin Translucent

    AccumulateThinTranslucentModel(
                          DualBlendSurfaceLuminancePostCoverage,
                          DualBlendSurfaceTransmittancePreCoverage,
                          DualBlendSurfaceCoverage,
                          MaterialParameters,
                          GBuffer,
                          DiffuseColor,
                          ColorSeparateSpecular,
                          Emissive,
                          Opacity);
    Color = 0;
    Opacity = 1.0f;
    

填充输出的RT

最后就是将计算的结果填充到输出的RT


他们曾如此骄傲的活过,贯彻始终