什么是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:
- NoClear
- 目的:不清除Clear
- 优点:省带宽
- 缺点:可能残留垃圾数据
- RHIClear
- 目的:整屏硬件 Clear
- 优点:实现简单、行为稳定
- 缺点:整屏带宽
- 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




Comments | NOTHING