1334 lines
56 KiB
Markdown
1334 lines
56 KiB
Markdown
---
|
||
title: Shadow
|
||
date: 2024-12-17 16:57:24
|
||
excerpt:
|
||
tags:
|
||
rating: ⭐
|
||
---
|
||
# 阴影
|
||
- [剖析虚幻渲染体系(05)- 光源和阴影](https://www.cnblogs.com/timlly/p/14817455.html)
|
||
|
||
## 阴影类型
|
||
- **Static Shadow**
|
||
- **Cascading Shadow Map**
|
||
- **Per Object Shadow**:可移动组件使用的逐物体阴影应用阴影图到物体的包围盒,因此包围盒必须是精确的。对于骨骼网格,这意味着它们应该有一个物理资产。对于粒子系统,任何固定的边界框必须足够大,以容纳所有的粒子。_在网格的Lighting属性组中,Dynamic Inset Shadow可以开启逐物体阴影,对需要高质量高精度的物体非常有用。_
|
||
- **Dynamic Shadow**:可移动光源在所有物体上投射出完全动态的阴影(和光)。这种光源的任何数据不会被烘焙到光照图中,它可以自由地在所有东西上投射动态阴影。静态网格、骨架网格、粒子效果等等将完全从可移动光源投射和接收动态阴影。
|
||
- **Capsule Shadow**
|
||
- **Contact Shadow**
|
||
- **Distance Field Shadow**
|
||
|
||
## 相关类型
|
||
- ShadowRendering.h
|
||
**FProjectedShadowInfo**:存储投影阴影先关信息。包含各种变换矩阵、阴影渲染函数以及渲染参数、灯光&场景&图元信息。
|
||
- LightSceneInfo.h
|
||
FLightSceneInfo:渲染一盏灯所用到的信息。可以看做是游戏线程中ULightComponent的镜像。
|
||
- SceneCore.h
|
||
FLightPrimitiveInteraction:存储LightSceneInfo、PrimitiveSceneInfo等信息。
|
||
- SceneRendering.h
|
||
FVisibleLightInfo:可见光源信息, 主要是阴影相关的信息.
|
||
FVisibleLightViewInfo:
|
||
## 相关Paas
|
||
1. ShadowDepths => RenderShadowDepthMaps()
|
||
2. Lights => RenderLights()
|
||
1. DirectLighting
|
||
1. UnbatchedLights
|
||
1. ShadowProjectionOnOpaque
|
||
# InitDynamicShadows
|
||
## InitDynamicShadows() => CreateDynamicShadows()
|
||
InitViews() => FSceneRenderer::InitDynamicShadows()
|
||
PS. UE5.3中相关逻辑移动到***CreateDynamicShadows()*** 中了。InitDynamicShadows() => BeginInitDynamicShadows() => BeginGatherShadowPrimitives() => CreateDynamicShadows()
|
||
|
||
计算各种灯光类型,之后调用:
|
||
- CreateWholeSceneProjectedShadow()
|
||
- AddViewDependentWholeSceneShadowsForView()
|
||
- SetupInteractionShadows()
|
||
- ***CreatePerObjectProjectedShadow()***
|
||
- InitProjectedShadowVisibility()
|
||
|
||
丛越文章中的注释:
|
||
```c++
|
||
void FSceneRenderer::InitDynamicShadows(FRHICommandListImmediate& RHICmdList, FGlobalDynamicIndexBuffer& DynamicIndexBuffer, FGlobalDynamicVertexBuffer& DynamicVertexBuffer, FGlobalDynamicReadBuffer& DynamicReadBuffer)
|
||
{
|
||
// 初始化各类标记和数量.
|
||
const bool bMobile = FeatureLevel < ERHIFeatureLevel::SM5;
|
||
bool bStaticSceneOnly = false;
|
||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
{
|
||
FViewInfo& View = Views[ViewIndex];
|
||
bStaticSceneOnly = bStaticSceneOnly || View.bStaticSceneOnly;
|
||
}
|
||
|
||
const bool bProjectEnablePointLightShadows = Scene->ReadOnlyCVARCache.bEnablePointLightShadows;
|
||
uint32 NumPointShadowCachesUpdatedThisFrame = 0;
|
||
uint32 NumSpotShadowCachesUpdatedThisFrame = 0;
|
||
|
||
// 预计算阴影.
|
||
TArray<FProjectedShadowInfo*,SceneRenderingAllocator> PreShadows;
|
||
// 视图关联的全景阴影.
|
||
TArray<FProjectedShadowInfo*,SceneRenderingAllocator> ViewDependentWholeSceneShadows;
|
||
// 视图关联的需要裁剪的全景阴影.
|
||
TArray<FProjectedShadowInfo*,SceneRenderingAllocator> ViewDependentWholeSceneShadowsThatNeedCulling;
|
||
{
|
||
// 遍历所有光源, 将不同类型的光源加入不同类型的待渲染的阴影列表中.
|
||
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt)
|
||
{
|
||
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
|
||
FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
|
||
|
||
FScopeCycleCounter Context(LightSceneInfo->Proxy->GetStatId());
|
||
|
||
FVisibleLightInfo& VisibleLightInfo = VisibleLightInfos[LightSceneInfo->Id];
|
||
|
||
// LightOcclusionType有阴影图和光追两种, 如果非阴影图类型, 则忽略.
|
||
const FLightOcclusionType OcclusionType = GetLightOcclusionType(LightSceneInfoCompact);
|
||
if (OcclusionType != FLightOcclusionType::Shadowmap)
|
||
continue;
|
||
|
||
// 如果光源没有开启阴影或阴影质量太小, 则忽略阴影图.
|
||
if ((LightSceneInfoCompact.bCastStaticShadow || LightSceneInfoCompact.bCastDynamicShadow) && GetShadowQuality() > 0)
|
||
{
|
||
// 检测该光源是否在某个view里可见, 如果不可见, 则忽略.
|
||
bool bIsVisibleInAnyView = false;
|
||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
{
|
||
bIsVisibleInAnyView = LightSceneInfo->ShouldRenderLight(Views[ViewIndex]);
|
||
|
||
if (bIsVisibleInAnyView)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 所有裁剪条件都通过了, 处理光源的阴影.
|
||
if (bIsVisibleInAnyView)
|
||
{
|
||
// 初始化阴影的各种标记和变量.
|
||
static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
|
||
const bool bAllowStaticLighting = (!AllowStaticLightingVar || AllowStaticLightingVar->GetValueOnRenderThread() != 0);
|
||
// 是否点光源阴影. 注意矩形光也当做点光源处理.
|
||
const bool bPointLightShadow = LightSceneInfoCompact.LightType == LightType_Point || LightSceneInfoCompact.LightType == LightType_Rect;
|
||
|
||
// 对不预计算阴影的移动光源只创建全景阴影(whole scene shadow).
|
||
const bool bShouldCreateShadowForMovableLight =
|
||
LightSceneInfoCompact.bCastDynamicShadow
|
||
&& (!LightSceneInfo->Proxy->HasStaticShadowing() || !bAllowStaticLighting);
|
||
|
||
const bool bCreateShadowForMovableLight =
|
||
bShouldCreateShadowForMovableLight
|
||
&& (!bPointLightShadow || bProjectEnablePointLightShadows);
|
||
|
||
// 对带有预计算阴影的尚未构建的光源创建全景阴影.
|
||
const bool bShouldCreateShadowToPreviewStaticLight =
|
||
LightSceneInfo->Proxy->HasStaticShadowing()
|
||
&& LightSceneInfoCompact.bCastStaticShadow
|
||
&& !LightSceneInfo->IsPrecomputedLightingValid();
|
||
|
||
const bool bCreateShadowToPreviewStaticLight =
|
||
bShouldCreateShadowToPreviewStaticLight
|
||
&& (!bPointLightShadow || bProjectEnablePointLightShadows);
|
||
|
||
// 对需要静态阴影但由于重叠导致没有有效阴影图的光源创建全景阴影.
|
||
const bool bShouldCreateShadowForOverflowStaticShadowing =
|
||
LightSceneInfo->Proxy->HasStaticShadowing()
|
||
&& !LightSceneInfo->Proxy->HasStaticLighting()
|
||
&& LightSceneInfoCompact.bCastStaticShadow
|
||
&& LightSceneInfo->IsPrecomputedLightingValid()
|
||
&& LightSceneInfo->Proxy->GetShadowMapChannel() == INDEX_NONE;
|
||
|
||
const bool bCreateShadowForOverflowStaticShadowing =
|
||
bShouldCreateShadowForOverflowStaticShadowing
|
||
&& (!bPointLightShadow || bProjectEnablePointLightShadows);
|
||
|
||
// 添加点光源的全景阴影.
|
||
const bool bPointLightWholeSceneShadow = (bShouldCreateShadowForMovableLight || bShouldCreateShadowForOverflowStaticShadowing || bShouldCreateShadowToPreviewStaticLight) && bPointLightShadow;
|
||
if (bPointLightWholeSceneShadow)
|
||
{
|
||
UsedWholeScenePointLightNames.Add(LightSceneInfoCompact.LightSceneInfo->Proxy->GetComponentName());
|
||
}
|
||
|
||
// 创建光源的全景阴影.
|
||
if (bCreateShadowForMovableLight || bCreateShadowToPreviewStaticLight || bCreateShadowForOverflowStaticShadowing)
|
||
{
|
||
CreateWholeSceneProjectedShadow(LightSceneInfo, NumPointShadowCachesUpdatedThisFrame, NumSpotShadowCachesUpdatedThisFrame);
|
||
}
|
||
|
||
// 允许移动和固定的光源创建CSM(级联阴影), 或者是尚未构建的静态光源.
|
||
if ((!LightSceneInfo->Proxy->HasStaticLighting() && LightSceneInfoCompact.bCastDynamicShadow) || bCreateShadowToPreviewStaticLight)
|
||
{
|
||
// 增加视图关联的全景阴影.
|
||
if( !bMobile ||
|
||
((LightSceneInfo->Proxy->UseCSMForDynamicObjects() || LightSceneInfo->Proxy->IsMovable())
|
||
&& (LightSceneInfo == Scene->MobileDirectionalLights[0] || LightSceneInfo == Scene->MobileDirectionalLights[1] || LightSceneInfo == Scene->MobileDirectionalLights[2])))
|
||
{
|
||
AddViewDependentWholeSceneShadowsForView(ViewDependentWholeSceneShadows, ViewDependentWholeSceneShadowsThatNeedCulling, VisibleLightInfo, *LightSceneInfo);
|
||
}
|
||
|
||
// 处理交互阴影, 此处的交互是指光源和图元之间的影响. 包含PerObject阴影、透明阴影、自阴影等.
|
||
if( !bMobile || (LightSceneInfo->Proxy->CastsModulatedShadows() && !LightSceneInfo->Proxy->UseCSMForDynamicObjects()))
|
||
{
|
||
Scene->FlushAsyncLightPrimitiveInteractionCreation();
|
||
|
||
// 处理动态图元的交互阴影.
|
||
for (FLightPrimitiveInteraction* Interaction = LightSceneInfo->GetDynamicInteractionOftenMovingPrimitiveList(false); Interaction; Interaction = Interaction->GetNextPrimitive())
|
||
{
|
||
SetupInteractionShadows(RHICmdList, Interaction, VisibleLightInfo, bStaticSceneOnly, ViewDependentWholeSceneShadows, PreShadows);
|
||
}
|
||
|
||
// 处理静态图元的交互阴影.
|
||
for (FLightPrimitiveInteraction* Interaction = LightSceneInfo->GetDynamicInteractionStaticPrimitiveList(false); Interaction; Interaction = Interaction->GetNextPrimitive())
|
||
{
|
||
SetupInteractionShadows(RHICmdList, Interaction, VisibleLightInfo, bStaticSceneOnly, ViewDependentWholeSceneShadows, PreShadows);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计算投射阴影的可见性.
|
||
InitProjectedShadowVisibility(RHICmdList);
|
||
}
|
||
|
||
// 清理旧的预计算阴影, 尝试增加新的到缓存中.
|
||
UpdatePreshadowCache(FSceneRenderTargets::Get(RHICmdList));
|
||
|
||
// 收集图元列表, 以绘制不同类型的阴影.
|
||
GatherShadowPrimitives(PreShadows, ViewDependentWholeSceneShadowsThatNeedCulling, bStaticSceneOnly);
|
||
|
||
// 分配阴影深度渲染纹理.
|
||
AllocateShadowDepthTargets(RHICmdList);
|
||
|
||
// 收集阴影的动态网格元素, 跟之前剖析的GatherDynamicMeshElements类似.
|
||
GatherShadowDynamicMeshElements(DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer);
|
||
}
|
||
```
|
||
|
||
# ShadowDepths
|
||
渲染阴影深度贴图与图集。
|
||
|
||
- FSceneRenderer::RenderShadowDepthMaps():位于CustomDepth之前。
|
||
- RenderVirtualShadowMaps()
|
||
- ***RenderShadowDepthMapAtlases()***
|
||
- SortedShadowsForShadowDepthPass.ShadowMapCubemaps循环。渲染点光源阴影立方体图
|
||
- **FProjectedShadowInfo::RenderDepth()**
|
||
- **FProjectedShadowInfo::RenderTranslucencyDepths**
|
||
|
||
RenderShadowDepthMapAtlases() 渲染的图集位于***FSceneRenderer::SortedShadowsForShadowDepthPass.ShadowMapAtlases*** (ShadowMapAtlases.RenderTargets.DepthTarget为深度,ShadowMapAtlases.Shadows 为FProjectedShadowInfo)
|
||
|
||
## RenderDepth
|
||
MeshDrawProcessor为***FShadowDepthPassMeshProcessor***。渲染的Shader为***ShadowDepthPixelShader.usf***
|
||
```c++
|
||
void FProjectedShadowInfo::RenderDepth(
|
||
FRDGBuilder& GraphBuilder,
|
||
const FSceneRenderer* SceneRenderer,
|
||
FRDGTextureRef ShadowDepthTexture,
|
||
bool bDoParallelDispatch,
|
||
bool bDoCrossGPUCopy)
|
||
{
|
||
#if WANTS_DRAW_MESH_EVENTS
|
||
FString EventName;
|
||
|
||
if (GetEmitDrawEvents())
|
||
{
|
||
GetShadowTypeNameForDrawEvent(EventName);
|
||
EventName += FString(TEXT(" ")) + FString::FromInt(ResolutionX) + TEXT("x") + FString::FromInt(ResolutionY);
|
||
}
|
||
|
||
RDG_EVENT_SCOPE(GraphBuilder, "%s", *EventName);
|
||
#endif
|
||
|
||
CONDITIONAL_SCOPE_CYCLE_COUNTER(STAT_RenderWholeSceneShadowDepthsTime, bWholeSceneShadow);
|
||
CONDITIONAL_SCOPE_CYCLE_COUNTER(STAT_RenderPerObjectShadowDepthsTime, !bWholeSceneShadow);
|
||
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderShadowDepth);
|
||
|
||
FScene* Scene = SceneRenderer->Scene;
|
||
const ERHIFeatureLevel::Type FeatureLevel = ShadowDepthView->FeatureLevel;
|
||
BeginRenderView(GraphBuilder, Scene);
|
||
|
||
FShadowDepthPassParameters* PassParameters = GraphBuilder.AllocParameters<FShadowDepthPassParameters>();
|
||
PassParameters->View = ShadowDepthView->ViewUniformBuffer;
|
||
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(
|
||
ShadowDepthTexture,
|
||
ERenderTargetLoadAction::ELoad,
|
||
ERenderTargetLoadAction::ENoAction,
|
||
FExclusiveDepthStencil::DepthWrite_StencilNop);
|
||
|
||
if (CacheMode == SDCM_MovablePrimitivesOnly || CacheMode == SDCM_CSMScrolling)
|
||
{
|
||
// Copy in depths of static primitives before we render movable primitives.
|
||
FMeshPassProcessorRenderState DrawRenderState;
|
||
SetStateForShadowDepth(bOnePassPointLightShadow, bDirectionalLight, DrawRenderState, MeshPassTargetType);
|
||
CopyCachedShadowMap(GraphBuilder, *ShadowDepthView, SceneRenderer, PassParameters->RenderTargets, DrawRenderState);
|
||
}
|
||
|
||
PassParameters->VirtualShadowMap = SceneRenderer->VirtualShadowMapArray.GetUniformBuffer();
|
||
|
||
switch (FSceneInterface::GetShadingPath(FeatureLevel))
|
||
{
|
||
case EShadingPath::Deferred:
|
||
{
|
||
auto* ShadowDepthPassParameters = GraphBuilder.AllocParameters<FShadowDepthPassUniformParameters>();
|
||
SetupShadowDepthPassUniformBuffer(this, GraphBuilder, *ShadowDepthView, *ShadowDepthPassParameters);//设置矩阵、Shader等相关ShadowDepthPassParameters参数
|
||
PassParameters->DeferredPassUniformBuffer = GraphBuilder.CreateUniformBuffer(ShadowDepthPassParameters);
|
||
}
|
||
break;
|
||
case EShadingPath::Mobile:
|
||
{
|
||
auto* ShadowDepthPassParameters = GraphBuilder.AllocParameters<FMobileShadowDepthPassUniformParameters>();
|
||
SetupShadowDepthPassUniformBuffer(this, GraphBuilder, *ShadowDepthView, *ShadowDepthPassParameters);//设置矩阵、Shader等相关ShadowDepthPassParameters参数
|
||
PassParameters->MobilePassUniformBuffer = GraphBuilder.CreateUniformBuffer(ShadowDepthPassParameters);
|
||
}
|
||
break;
|
||
default:
|
||
checkNoEntry();
|
||
}
|
||
|
||
ShadowDepthPass.BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
||
|
||
#if WITH_MGPU
|
||
// Need to fetch GPU mask outside "AddPass", as it's not updated during pass execution
|
||
FRHIGPUMask GPUMask = GraphBuilder.RHICmdList.GetGPUMask();
|
||
#endif
|
||
|
||
if (bDoParallelDispatch)
|
||
{
|
||
RDG_WAIT_FOR_TASKS_CONDITIONAL(GraphBuilder, IsShadowDepthPassWaitForTasksEnabled());
|
||
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("ShadowDepthPassParallel"),
|
||
PassParameters,
|
||
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
||
[this, PassParameters
|
||
#if WITH_MGPU
|
||
, ShadowDepthTexture, GPUMask, bDoCrossGPUCopy
|
||
#endif
|
||
](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
||
{
|
||
FShadowParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, *ShadowDepthView, *this, FParallelCommandListBindings(PassParameters));
|
||
ShadowDepthPass.DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||
|
||
#if WITH_MGPU
|
||
if (bDoCrossGPUCopy)
|
||
{
|
||
CopyCachedShadowMapCrossGPU(RHICmdList, ShadowDepthTexture->GetRHI(), GPUMask);
|
||
}
|
||
#endif
|
||
});
|
||
}
|
||
else
|
||
{
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("ShadowDepthPass"),
|
||
PassParameters,
|
||
ERDGPassFlags::Raster,
|
||
[this, PassParameters
|
||
#if WITH_MGPU
|
||
, ShadowDepthTexture, GPUMask, bDoCrossGPUCopy
|
||
#endif
|
||
](FRHICommandList& RHICmdList)
|
||
{
|
||
SetStateForView(RHICmdList);
|
||
ShadowDepthPass.DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||
|
||
#if WITH_MGPU
|
||
if (bDoCrossGPUCopy)
|
||
{
|
||
CopyCachedShadowMapCrossGPU(RHICmdList, ShadowDepthTexture->GetRHI(), GPUMask);
|
||
}
|
||
#endif
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
```c++
|
||
void SetupShadowDepthPassUniformBuffer(
|
||
const FProjectedShadowInfo* ShadowInfo,
|
||
FRDGBuilder& GraphBuilder,
|
||
const FViewInfo& View,
|
||
FShadowDepthPassUniformParameters& ShadowDepthPassParameters)
|
||
{
|
||
static const auto CSMCachingCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Shadow.CSMCaching"));
|
||
const bool bCSMCachingEnabled = CSMCachingCVar && CSMCachingCVar->GetValueOnAnyThread() != 0;
|
||
|
||
SetupSceneTextureUniformParameters(GraphBuilder, View.GetSceneTexturesChecked(), View.FeatureLevel, ESceneTextureSetupMode::None, ShadowDepthPassParameters.SceneTextures);//设置对应的RT与Texture
|
||
|
||
ShadowDepthPassParameters.ProjectionMatrix = FTranslationMatrix44f(FVector3f(ShadowInfo->PreShadowTranslation - View.ViewMatrices.GetPreViewTranslation())) * ShadowInfo->TranslatedWorldToClipOuterMatrix; // LWC_TDOO: Precision loss?
|
||
ShadowDepthPassParameters.ViewMatrix = FMatrix44f(ShadowInfo->TranslatedWorldToView); // LWC_TODO: Precision loss
|
||
|
||
// Disable the SlopeDepthBias because we couldn't reconstruct the depth offset if it is not 0.0f when scrolling the cached shadow map.
|
||
ShadowDepthPassParameters.ShadowParams = FVector4f(ShadowInfo->GetShaderDepthBias(), bCSMCachingEnabled ? 0.0f : ShadowInfo->GetShaderSlopeDepthBias(), ShadowInfo->GetShaderMaxSlopeDepthBias(), ShadowInfo->bOnePassPointLightShadow ? 1 : ShadowInfo->InvMaxSubjectDepth);
|
||
ShadowDepthPassParameters.bClampToNearPlane = ShadowInfo->ShouldClampToNearPlane() ? 1.0f : 0.0f;
|
||
|
||
if (ShadowInfo->bOnePassPointLightShadow)
|
||
{
|
||
check(ShadowInfo->BorderSize == 0);
|
||
|
||
// offset from translated world space to (pre translated) shadow space
|
||
const FMatrix Translation = FTranslationMatrix(ShadowInfo->PreShadowTranslation - View.ViewMatrices.GetPreViewTranslation());
|
||
|
||
for (int32 FaceIndex = 0; FaceIndex < 6; FaceIndex++)
|
||
{
|
||
ShadowDepthPassParameters.ShadowViewProjectionMatrices[FaceIndex] = FMatrix44f(Translation * ShadowInfo->OnePassShadowViewProjectionMatrices[FaceIndex]); // LWC_TODO: Precision loss?
|
||
ShadowDepthPassParameters.ShadowViewMatrices[FaceIndex] = FMatrix44f(Translation * ShadowInfo->OnePassShadowViewMatrices[FaceIndex]);
|
||
}
|
||
}
|
||
|
||
ShadowDepthPassParameters.bRenderToVirtualShadowMap = false;
|
||
ShadowDepthPassParameters.VirtualSmPageTable = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(uint32)));
|
||
ShadowDepthPassParameters.PackedNaniteViews = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(Nanite::FPackedView)));
|
||
ShadowDepthPassParameters.PageRectBounds = GraphBuilder.CreateSRV(GSystemTextures.GetDefaultStructuredBuffer(GraphBuilder, sizeof(FIntVector4)));
|
||
|
||
FRDGTextureRef DepthBufferArray = GraphBuilder.CreateTexture( FRDGTextureDesc::Create2DArray( FIntPoint(4,4), PF_R32_UINT, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV, 1 ), TEXT("Dummy-OutDepthBuffer") );
|
||
|
||
ShadowDepthPassParameters.OutDepthBufferArray = GraphBuilder.CreateUAV( DepthBufferArray );
|
||
}
|
||
```
|
||
|
||
## FShadowDepthPassMeshProcessor
|
||
CullMode:双面物体会使用CM_None,其余的使用 CM_CW或者CM_CCW。
|
||
|
||
```c++
|
||
bool FShadowDepthPassMeshProcessor::TryAddMeshBatch(const FMeshBatch& RESTRICT MeshBatch,
|
||
uint64 BatchElementMask,
|
||
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
||
int32 StaticMeshId,
|
||
const FMaterialRenderProxy& MaterialRenderProxy,
|
||
const FMaterial& Material)
|
||
{
|
||
const bool bShouldCastShadow = Material.ShouldCastDynamicShadows();
|
||
|
||
const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch);
|
||
const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(Material, OverrideSettings);
|
||
|
||
const ERasterizerCullMode MeshCullMode = FMeshPassProcessor::ComputeMeshCullMode(Material, OverrideSettings);
|
||
ERasterizerCullMode FinalCullMode = SetupShadowCullMode(FeatureLevel, MeshPassTargetType, ShadowDepthType, Material, MeshCullMode, PrimitiveSceneProxy->CastsShadowAsTwoSided());
|
||
|
||
//判断是否投射动态阴影 && 是否在MainPass中渲染 && 是否会走OpaquePass
|
||
bool bResult = true;
|
||
if (bShouldCastShadow
|
||
&& ShouldIncludeDomainInMeshPass(Material.GetMaterialDomain())
|
||
&& ShouldIncludeMaterialInDefaultOpaquePass(Material)
|
||
&& EnumHasAnyFlags(MeshSelectionMask, MeshBatch.VertexFactory->SupportsGPUScene(FeatureLevel) ? EShadowMeshSelection::VSM : EShadowMeshSelection::SM))
|
||
{
|
||
const FMaterialRenderProxy* EffectiveMaterialRenderProxy = &MaterialRenderProxy;
|
||
const FMaterial* EffectiveMaterial = &Material;
|
||
const bool bVFTypeSupportsNullPixelShader = MeshBatch.VertexFactory->SupportsNullPixelShader();
|
||
const bool bEvaluateWPO = Material.MaterialModifiesMeshPosition_RenderThread()
|
||
&& (!ShouldOptimizedWPOAffectNonNaniteShaderSelection() || PrimitiveSceneProxy->EvaluateWorldPositionOffset());
|
||
|
||
//取得NullPixelShader以及计算材质中的WPO,之后如果对没有Pixel进行写入且没有改变WPO使用默认材质渲染;否则则采用当前模型的材质。
|
||
if (UseDefaultMaterialForShadowDepth(Material, bVFTypeSupportsNullPixelShader, bEvaluateWPO))
|
||
{
|
||
const FMaterialRenderProxy* DefaultProxy = UMaterial::GetDefaultMaterial(MD_Surface)->GetRenderProxy();
|
||
const FMaterial* DefaultMaterialResource = DefaultProxy->GetMaterialNoFallback(FeatureLevel);
|
||
check(DefaultMaterialResource);
|
||
|
||
// Override with the default material for opaque materials that don't modify mesh position.
|
||
EffectiveMaterialRenderProxy = DefaultProxy;
|
||
EffectiveMaterial = DefaultMaterialResource;
|
||
}
|
||
|
||
bResult = Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, FinalCullMode);
|
||
}
|
||
|
||
return bResult;
|
||
}
|
||
```
|
||
|
||
## ShaderDepthBias
|
||
相关计算位于FProjectedShadowInfo::UpdateShaderDepthBias()。
|
||
起效条件:勾选Dynamic Inset Shadow(bCastInsetShadow)或者勾选bSelfShadowOnly。(骨骼物体有额外的bCastCapsuleDirectShadow)
|
||
CMD: CVarPerObjectDirectionalShadowDepthBias => r.Shadow.PerObjectDirectionalDepthBias
|
||
```c++
|
||
// per object shadows (the whole-scene are taken care of above)
|
||
if(bDirectionalLight)
|
||
{
|
||
// we use CSMShadowDepthBias cvar but this is per object shadows, maybe we want to use different settings
|
||
|
||
// the z range is adjusted to we need to adjust here as well
|
||
DepthBias = CVarPerObjectDirectionalShadowDepthBias.GetValueOnRenderThread() / (MaxSubjectZ - MinSubjectZ);
|
||
|
||
float WorldSpaceTexelScale = ShadowBounds.W / FMath::Max(ResolutionX, ResolutionY);
|
||
|
||
DepthBias *= WorldSpaceTexelScale;
|
||
DepthBias *= 0.5f; // avg GetUserShadowBias, in that case we don't want this adjustable
|
||
|
||
SlopeScaleDepthBias = CVarPerObjectDirectionalShadowSlopeScaleDepthBias.GetValueOnRenderThread();
|
||
SlopeScaleDepthBias *= LightSceneInfo->Proxy->GetUserShadowSlopeBias();
|
||
}
|
||
```
|
||
|
||
### bCastInsetShadow
|
||
bCastInsetShadow => PrimitiveProxy->CastsInsetShadow() => DoesPrimitiveCastInsetShadow() => FilterPrimitiveForShadows() => FGatherShadowPrimitivesPacket::AnyThreadTask() => FGatherShadowPrimitivesPrepareTask::DoTask()
|
||
|
||
## Shader
|
||
VertexShader为:ShadowDepthVertexShader.usf,有4种变体:
|
||
- IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_PerspectiveCorrect);
|
||
- IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_OutputDepth);
|
||
- IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_OnePassPointLight);
|
||
- IMPLEMENT_SHADOW_DEPTH_SHADERMODE_SHADERS(VertexShadowDepth_VirtualShadowMap);
|
||
PixelShader为:***ShadowDepthPixelShader.usf***,有4种变体:
|
||
- IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_NonPerspectiveCorrect);
|
||
- IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_PerspectiveCorrect);
|
||
- IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_OnePassPointLight);
|
||
- IMPLEMENT_SHADOWDEPTHPASS_PIXELSHADER_TYPE(PixelShadowDepth_VirtualShadowMap);
|
||
|
||
使用对应的Shader逻辑位于GetShadowDepthPassShaders()。只有非方向光同时不是OnePass的状态下会使用**PerspectiveCorrect**。
|
||
```c++
|
||
void SetShadowDepthOutputs(
|
||
float4x4 WorldToClipMatrix,
|
||
float4x4 WorldToShadowMatrix,
|
||
float4 WorldPosition,
|
||
float3 WorldVertexNormal,
|
||
out float4 OutPosition,
|
||
out float ShadowDepth
|
||
#if PERSPECTIVE_CORRECT_DEPTH
|
||
, out float OutDepthBias
|
||
#endif
|
||
)
|
||
{
|
||
OutPosition = mul(WorldPosition, WorldToClipMatrix);
|
||
|
||
// Clamp the vertex to the near plane if it is in front of the near plane
|
||
// This has problems if some vertices of a triangle get clamped and others do not, also causes artifacts with non-ortho projections
|
||
if (PassStruct.bClampToNearPlane > 0 && OutPosition.z > OutPosition.w)
|
||
{
|
||
OutPosition.z = 0.999999f;
|
||
OutPosition.w = 1.0f;
|
||
}
|
||
|
||
#if ONEPASS_POINTLIGHT_SHADOW
|
||
const float3 ViewDirection = -normalize(mul(WorldPosition, WorldToShadowMatrix).xyz);
|
||
const float3 ViewNormal = mul(float4(WorldVertexNormal,0), WorldToShadowMatrix).xyz;
|
||
const float NoL = abs(dot(ViewDirection, ViewNormal));
|
||
#else
|
||
const float NoL = abs(dot(
|
||
float3(WorldToShadowMatrix[0].z, WorldToShadowMatrix[1].z, WorldToShadowMatrix[2].z),
|
||
WorldVertexNormal));
|
||
#endif
|
||
|
||
const float MaxSlopeDepthBias = PassStruct.ShadowParams.z;
|
||
const float Slope = clamp(abs(NoL) > 0 ? sqrt(saturate(1 - NoL*NoL)) / NoL : MaxSlopeDepthBias, 0, MaxSlopeDepthBias);
|
||
|
||
const float SlopeDepthBias = PassStruct.ShadowParams.y;
|
||
const float SlopeBias = SlopeDepthBias * Slope;
|
||
|
||
const float ConstantDepthBias = PassStruct.ShadowParams.x;
|
||
const float DepthBias = SlopeBias + ConstantDepthBias;
|
||
|
||
#if PERSPECTIVE_CORRECT_DEPTH
|
||
ShadowDepth = OutPosition.z;
|
||
OutDepthBias = DepthBias;
|
||
#elif ONEPASS_POINTLIGHT_SHADOW
|
||
ShadowDepth = 0;
|
||
//OutPosition.z += DepthBias;
|
||
#else
|
||
// Output linear, normalized depth
|
||
const float InvMaxSubjectDepth = PassStruct.ShadowParams.w;
|
||
#if PLATFORM_NEEDS_PRECISE_SHADOW_DEPTH
|
||
precise
|
||
#endif
|
||
float AdjustedDepth = ( 1 - OutPosition.z * InvMaxSubjectDepth ) + DepthBias;
|
||
ShadowDepth = AdjustedDepth;
|
||
OutPosition.z = AdjustedDepth;
|
||
#endif
|
||
}
|
||
|
||
void Main(
|
||
FVertexFactoryInput Input,
|
||
#if ONEPASS_POINTLIGHT_SHADOW
|
||
out FShadowDepthVSToPS OutParameters,
|
||
#else
|
||
out FShadowDepthVSToPS OutParameters,
|
||
#endif
|
||
#if VIRTUAL_SM_ENABLED
|
||
out nointerpolation uint PackedPageInfo : TEXCOORD8,
|
||
#endif
|
||
out float4 OutPosition : SV_POSITION
|
||
#if ONEPASS_POINTLIGHT_SHADOW
|
||
, out uint LayerIndex : SV_RenderTargetArrayIndex
|
||
#endif
|
||
#if VIRTUAL_SM_ENABLED
|
||
// OLA-TODO: this collides with instanced stereo, which thankfully is not used with shadow maps, so should be fine, presumably.
|
||
, out float4 OutVirtualSmPageClip : SV_ClipDistance
|
||
#endif // VIRTUAL_SM_ENABLED
|
||
)
|
||
{
|
||
ResolvedView = ResolveView();
|
||
|
||
//计算FMaterialVertexParameters,最终获取顶点WorldPosition
|
||
FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
|
||
float4 WorldPos = VertexFactoryGetWorldPosition(Input, VFIntermediates);
|
||
half3x3 TangentToLocal = VertexFactoryGetTangentToLocal(Input, VFIntermediates);
|
||
|
||
FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, WorldPos.xyz, TangentToLocal);
|
||
const float3 WorldNormal = VertexFactoryGetWorldNormal(Input, VFIntermediates);
|
||
|
||
WorldPos.xyz += GetMaterialWorldPositionOffset(VertexParameters);
|
||
|
||
#if ONEPASS_POINTLIGHT_SHADOW
|
||
|
||
OutPosition = WorldPos;
|
||
|
||
#if INTERPOLATE_VF_ATTRIBUTES
|
||
// Masked materials need texture coords to clip
|
||
OutParameters.FactoryInterpolants = VertexFactoryGetInterpolantsVSToPS(Input, VFIntermediates, VertexParameters);
|
||
#endif
|
||
|
||
#if INTERPOLATE_POSITION
|
||
OutParameters.PixelPosition = WorldPos.xyz;
|
||
#endif
|
||
|
||
LayerIndex = bUseGpuSceneInstancing ? VertexFactoryGetViewIndex(VFIntermediates) : LayerId;
|
||
OutPosition = mul(WorldPos, PassStruct.ShadowViewProjectionMatrices[LayerIndex]);
|
||
|
||
#else
|
||
float Dummy;
|
||
|
||
SetShadowDepthOutputs(
|
||
PassStruct.ProjectionMatrix,
|
||
PassStruct.ViewMatrix,
|
||
WorldPos,
|
||
WorldNormal,
|
||
OutPosition,
|
||
#if !PERSPECTIVE_CORRECT_DEPTH
|
||
Dummy
|
||
#else
|
||
OutParameters.ShadowDepth,
|
||
OutParameters.DepthBias
|
||
#endif
|
||
);
|
||
|
||
#if INTERPOLATE_VF_ATTRIBUTES
|
||
// Masked materials need texture coords to clip
|
||
OutParameters.FactoryInterpolants = VertexFactoryGetInterpolantsVSToPS(Input, VFIntermediates, VertexParameters);
|
||
#endif
|
||
|
||
#if INTERPOLATE_POSITION
|
||
OutParameters.PixelPosition = WorldPos.xyz;
|
||
#endif
|
||
|
||
#if !PERSPECTIVE_CORRECT_DEPTH && !COMPILER_SUPPORTS_EMPTY_STRUCTS
|
||
OutParameters.Dummy = 0;
|
||
#endif
|
||
|
||
#endif
|
||
|
||
#if VIRTUAL_SM_ENABLED
|
||
PackedPageInfo = 0;
|
||
|
||
OutVirtualSmPageClip = float4(1.0f, 1.0f, 1.0f, 1.0f);
|
||
if (PassStruct.bRenderToVirtualShadowMap != 0)
|
||
{
|
||
// Get the offset from which we loaded the instance ID
|
||
uint InstanceIdIndex = VertexFactoryGetInstanceIdLoadIndex(VFIntermediates);
|
||
PackedPageInfo = InstanceCulling.PageInfoBuffer[InstanceIdIndex];
|
||
|
||
FPageInfo PageInfo = UnpackPageInfo(PackedPageInfo);
|
||
|
||
TransformToVirtualSmPage(OutPosition, OutVirtualSmPageClip, PageInfo, WorldPos.xyz);
|
||
}
|
||
#endif // VIRTUAL_SM_ENABLED
|
||
}
|
||
|
||
#if POSITION_ONLY
|
||
void PositionOnlyMain(
|
||
in FPositionAndNormalOnlyVertexFactoryInput Input,
|
||
#if ONEPASS_POINTLIGHT_SHADOW
|
||
out uint LayerIndex : SV_RenderTargetArrayIndex,
|
||
#endif
|
||
out FShadowDepthVSToPS OutParameters,
|
||
|
||
#if VIRTUAL_SM_ENABLED
|
||
out nointerpolation uint PackedPageInfo : TEXCOORD8,
|
||
#endif
|
||
out float4 OutPosition : SV_POSITION
|
||
#if VIRTUAL_SM_ENABLED
|
||
// OLA-TODO: this collides with instanced stereo, which thankfully is not used with shadow maps, so should be fine, presumably.
|
||
, out float4 OutVirtualSmPageClip : SV_ClipDistance
|
||
#endif // VIRTUAL_SM_ENABLED
|
||
)
|
||
{
|
||
ResolvedView = ResolveView();
|
||
|
||
float4 WorldPos = VertexFactoryGetWorldPosition(Input);
|
||
|
||
#if INTERPOLATE_VF_ATTRIBUTES
|
||
OutParameters.FactoryInterpolants = (FVertexFactoryInterpolantsVSToPS)0;
|
||
#endif
|
||
|
||
float3 WorldNormal = VertexFactoryGetWorldNormal(Input);
|
||
|
||
#if ONEPASS_POINTLIGHT_SHADOW
|
||
LayerIndex = bUseGpuSceneInstancing ? VertexFactoryGetViewIndex(Input) : LayerId;
|
||
OutPosition = mul(WorldPos, PassStruct.ShadowViewProjectionMatrices[LayerIndex]);
|
||
|
||
#else // !ONEPASS_POINTLIGHT_SHADOW
|
||
float ShadowDepth;
|
||
SetShadowDepthOutputs(
|
||
PassStruct.ProjectionMatrix,
|
||
PassStruct.ViewMatrix,
|
||
WorldPos,
|
||
WorldNormal,
|
||
OutPosition,
|
||
#if PERSPECTIVE_CORRECT_DEPTH
|
||
OutParameters.ShadowDepth,
|
||
OutParameters.DepthBias
|
||
#else
|
||
ShadowDepth
|
||
#endif
|
||
);
|
||
|
||
#if !PERSPECTIVE_CORRECT_DEPTH && !COMPILER_SUPPORTS_EMPTY_STRUCTS
|
||
OutParameters.Dummy = 0;
|
||
#endif
|
||
#endif // ONEPASS_POINTLIGHT_SHADOW
|
||
|
||
#if INTERPOLATE_POSITION
|
||
OutParameters.PixelPosition = WorldPos.xyz;
|
||
#endif
|
||
|
||
#if VIRTUAL_SM_ENABLED
|
||
PackedPageInfo = 0;
|
||
|
||
OutVirtualSmPageClip = float4(1.0f, 1.0f, 1.0f, 1.0f);
|
||
if (PassStruct.bRenderToVirtualShadowMap != 0)
|
||
{
|
||
// Get the offset from which we loaded the instance ID
|
||
uint InstanceIdIndex = VertexFactoryGetInstanceIdLoadIndex(Input);
|
||
// TODO: Maybe offset index to local buffer for this? We may not want to use a global since most passes are not supporting this
|
||
// Or perhaps both are different, as they are managed somewhat differently anyway, maybe.
|
||
PackedPageInfo = InstanceCulling.PageInfoBuffer[InstanceIdIndex];
|
||
FPageInfo PageInfo = UnpackPageInfo(PackedPageInfo);
|
||
TransformToVirtualSmPage(OutPosition, OutVirtualSmPageClip, PageInfo, WorldPos.xyz);
|
||
}
|
||
#endif // VIRTUAL_SM_ENABLED
|
||
}
|
||
|
||
#endif // POSITION_ONLY
|
||
```
|
||
|
||
|
||
```c++
|
||
void Main(
|
||
FShadowDepthVSToPS Inputs,
|
||
#if VIRTUAL_SM_ENABLED
|
||
nointerpolation uint PackedPageInfo : TEXCOORD8,
|
||
#endif
|
||
in float4 SvPosition : SV_Position // after all interpolators
|
||
#if PERSPECTIVE_CORRECT_DEPTH || COMPILER_METAL
|
||
,out float OutDepth : SV_DEPTH
|
||
#endif
|
||
)
|
||
{
|
||
ResolvedView = ResolveView();
|
||
|
||
#if INTERPOLATE_VF_ATTRIBUTES
|
||
|
||
FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(Inputs.FactoryInterpolants, SvPosition);//取得材质中的PixelShader相关变量PixelMaterialInputs。
|
||
FPixelMaterialInputs PixelMaterialInputs;
|
||
#if INTERPOLATE_POSITION
|
||
{
|
||
float4 ScreenPosition = SvPositionToResolvedScreenPosition(SvPosition);
|
||
|
||
float3 TranslatedWorld = Inputs.PixelPosition.xyz;
|
||
|
||
CalcMaterialParametersEx(MaterialParameters, PixelMaterialInputs, SvPosition, ScreenPosition, 1, TranslatedWorld, TranslatedWorld);
|
||
}
|
||
#else
|
||
CalcMaterialParameters(MaterialParameters, PixelMaterialInputs, SvPosition, 1);
|
||
#endif
|
||
|
||
// Evaluate the mask for masked materials
|
||
GetMaterialClippingShadowDepth(MaterialParameters, PixelMaterialInputs);
|
||
#else
|
||
ClipLODTransition(SvPosition.xy);
|
||
#endif
|
||
|
||
#if PERSPECTIVE_CORRECT_DEPTH
|
||
const float InvMaxSubjectDepth = PassStruct.ShadowParams.w;
|
||
Inputs.ShadowDepth = 1 - Inputs.ShadowDepth * InvMaxSubjectDepth;
|
||
Inputs.ShadowDepth += Inputs.DepthBias;
|
||
|
||
OutDepth = saturate(Inputs.ShadowDepth);
|
||
#elif COMPILER_METAL
|
||
// Metal fragment shader must not be empty,
|
||
// so output depth value explicitly if this shader permuation was not discarded
|
||
OutDepth = SvPosition.z;
|
||
#endif
|
||
|
||
#if ENABLE_NON_NANITE_VSM && VIRTUAL_TEXTURE_TARGET
|
||
uint2 vAddress = (uint2)SvPosition.xy;
|
||
float DeviceZ = SvPosition.z;
|
||
|
||
FPageInfo PageInfo = UnpackPageInfo( PackedPageInfo );
|
||
FNaniteView NaniteView = UnpackNaniteView( PassStruct.PackedNaniteViews[ PageInfo.ViewId ] );
|
||
|
||
FShadowPhysicalPage Page = ShadowDecodePageTable( PassStruct.VirtualSmPageTable[ CalcPageOffset( NaniteView.TargetLayerIndex, NaniteView.TargetMipLevel, vAddress >> VSM_LOG2_PAGE_SIZE ) ] );
|
||
|
||
if( Page.bThisLODValid )
|
||
{
|
||
uint2 pAddress = Page.PhysicalAddress * VSM_PAGE_SIZE + (vAddress & VSM_PAGE_SIZE_MASK);
|
||
// If requested, render to the static page
|
||
const int ArrayIndex = PageInfo.bStaticPage ? GetVirtualShadowMapStaticArrayIndex() : 0;
|
||
InterlockedMax( PassStruct.OutDepthBufferArray[ uint3( pAddress, ArrayIndex ) ], asuint( DeviceZ ) );
|
||
}
|
||
#endif
|
||
}
|
||
```
|
||
|
||
# RenderLights
|
||
渲染ScreenShadowMask,并用于光照计算。
|
||
|
||
FDeferredShadingSceneRenderer::RenderLights()
|
||
=>
|
||
RDG_EVENT_SCOPE(GraphBuilder, "UnbatchedLights");//batchedLights为没有LightFunction与没有阴影的灯光。
|
||
=>
|
||
for (int32 LightIndex = UnbatchedLightStart; LightIndex < LumenLightStart; LightIndex++)
|
||
=>
|
||
if (bDrawShadows){...} => RenderRayTracingShadows()/***RenderDeferredShadowProjections()***
|
||
=>
|
||
if (bDirectLighting){...}
|
||
if (bDirectLighting){...} => RenderLight()
|
||
if (bUseHairLighting){...}=> RenderLightForHair()
|
||
|
||
## RenderDeferredShadowProjections()
|
||
```c++
|
||
void FDeferredShadingSceneRenderer::RenderDeferredShadowProjections(
|
||
FRDGBuilder& GraphBuilder,
|
||
const FMinimalSceneTextures& SceneTextures,
|
||
const FTranslucencyLightingVolumeTextures& TranslucencyLightingVolumeTextures,
|
||
const FLightSceneInfo* LightSceneInfo,
|
||
FRDGTextureRef ScreenShadowMaskTexture,
|
||
FRDGTextureRef ScreenShadowMaskSubPixelTexture)
|
||
{
|
||
CheckShadowDepthRenderCompleted();
|
||
|
||
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderShadowProjections, FColor::Emerald);
|
||
SCOPE_CYCLE_COUNTER(STAT_ProjectedShadowDrawTime);
|
||
RDG_EVENT_SCOPE(GraphBuilder, "ShadowProjectionOnOpaque");
|
||
RDG_GPU_STAT_SCOPE(GraphBuilder, ShadowProjection);
|
||
|
||
const FVisibleLightInfo& VisibleLightInfo = VisibleLightInfos[LightSceneInfo->Id];
|
||
|
||
const bool bProjectingForForwardShading = false;
|
||
RenderShadowProjections(GraphBuilder, SceneTextures, ScreenShadowMaskTexture, ScreenShadowMaskSubPixelTexture, LightSceneInfo, bProjectingForForwardShading);
|
||
ShadowSceneRenderer->ApplyVirtualShadowMapProjectionForLight(GraphBuilder, SceneTextures, LightSceneInfo, ScreenShadowMaskTexture, ScreenShadowMaskSubPixelTexture);
|
||
|
||
RenderCapsuleDirectShadows(GraphBuilder, SceneTextures.UniformBuffer, *LightSceneInfo, ScreenShadowMaskTexture, VisibleLightInfo.CapsuleShadowsToProject, bProjectingForForwardShading);
|
||
|
||
// Inject deep shadow mask for regular shadow map. When using virtual shadow map, it is directly handled in the shadow kernel.
|
||
if (HairStrands::HasViewHairStrandsData(Views))
|
||
{
|
||
bool bNeedHairShadowMaskPass = false;
|
||
const bool bVirtualShadowOnePass = VisibleLightInfo.VirtualShadowMapClipmaps.Num() > 0;
|
||
if (!bVirtualShadowOnePass)
|
||
{
|
||
for (int32 ShadowIndex = 0; ShadowIndex < VisibleLightInfo.ShadowsToProject.Num(); ShadowIndex++)
|
||
{
|
||
FProjectedShadowInfo* ProjectedShadowInfo = VisibleLightInfo.ShadowsToProject[ShadowIndex];
|
||
if (ProjectedShadowInfo->HasVirtualShadowMap())
|
||
{
|
||
bNeedHairShadowMaskPass = false;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
bNeedHairShadowMaskPass = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (bNeedHairShadowMaskPass)
|
||
{
|
||
RenderHairStrandsShadowMask(GraphBuilder, Views, LightSceneInfo, false /*bForward*/, ScreenShadowMaskTexture);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
RenderShadowProjections():
|
||
1. 取得当前FVisibleLightInfo、FLightSceneProxy;创建FProjectedShadowInfoArray DistanceFieldShadows、NormalShadows。
|
||
2. 遍历VisibleLightInfo.ShadowsToProject,按照阴影特征将每个FProjectedShadowInfo加入DistanceFieldShadows、NormalShadows。
|
||
3. 调用Lamda来渲染ScreenShadowMaskTexture与ScreenShadowMaskSubPixelTexture。
|
||
4. 遍历DistanceFieldShadows,调用FProjectedShadowInfo->RenderRayTracedDistanceFieldProjection()来渲染距离场阴影。
|
||
|
||
### FSceneRenderer::RenderShadowProjections()
|
||
1. 初始化UniformStruct变量。
|
||
2. 遍历传入的FProjectedShadowInfo数组,并调用**ProjectedShadowInfo->RenderProjection()** 将所有灯光的阴影Mask绘制到一张ScreenShadowMask上。
|
||
1. 在TShadowProjectionPS中通过SetParameters() => ProjectionParameters.Set(),来设置ShadowDepthTextureValue。`ShadowDepthTextureValue = ShadowInfo->RenderTargets.DepthTarget->GetRHI();`
|
||
|
||
```c++
|
||
void FSceneRenderer::RenderShadowProjections(
|
||
FRDGBuilder& GraphBuilder,
|
||
FRDGTextureRef OutputTexture,
|
||
const FMinimalSceneTextures& SceneTextures,
|
||
const FLightSceneProxy* LightSceneProxy,
|
||
TArrayView<const FProjectedShadowInfo* const> Shadows,
|
||
bool bSubPixelShadow,
|
||
bool bProjectingForForwardShading)
|
||
{
|
||
CheckShadowDepthRenderCompleted();
|
||
|
||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
{
|
||
const FViewInfo& View = Views[ViewIndex];
|
||
const FExclusiveDepthStencil ExclusiveDepthStencil = FExclusiveDepthStencil::DepthRead_StencilWrite;
|
||
|
||
if (bSubPixelShadow && !HairStrands::HasViewHairStrandsData(View))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
View.BeginRenderView();
|
||
|
||
// Sanity check
|
||
if (bSubPixelShadow)
|
||
{
|
||
check(View.HairStrandsViewData.VisibilityData.HairOnlyDepthTexture);
|
||
}
|
||
|
||
FShadowProjectionPassParameters CommonPassParameters;
|
||
CommonPassParameters.SceneTextures = SceneTextures.GetSceneTextureShaderParameters(View.FeatureLevel);
|
||
CommonPassParameters.HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View);
|
||
|
||
if (Strata::IsStrataEnabled())
|
||
{
|
||
CommonPassParameters.Strata = Strata::BindStrataGlobalUniformParameters(View);
|
||
}
|
||
|
||
CommonPassParameters.RenderTargets[0] = FRenderTargetBinding(OutputTexture, ERenderTargetLoadAction::ELoad);
|
||
CommonPassParameters.RenderTargets.DepthStencil =
|
||
bSubPixelShadow ?
|
||
FDepthStencilBinding(View.HairStrandsViewData.VisibilityData.HairOnlyDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil) :
|
||
FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);
|
||
|
||
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
||
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
||
|
||
// Project the shadow depth buffers onto the scene.
|
||
for (const FProjectedShadowInfo* ProjectedShadowInfo : Shadows)
|
||
{
|
||
if (ProjectedShadowInfo->bAllocated)
|
||
{
|
||
// Only project the shadow if it's large enough in this particular view (split screen, etc... may have shadows that are large in one view but irrelevantly small in others)
|
||
if (ProjectedShadowInfo->FadeAlphas[ViewIndex] > 1.0f / 256.0f)
|
||
{
|
||
if (ProjectedShadowInfo->bOnePassPointLightShadow)
|
||
{
|
||
ProjectedShadowInfo->RenderOnePassPointLightProjection(GraphBuilder, CommonPassParameters, ViewIndex, View, LightSceneProxy, bProjectingForForwardShading, bSubPixelShadow);
|
||
}
|
||
else
|
||
{
|
||
ProjectedShadowInfo->RenderProjection(GraphBuilder, CommonPassParameters, ViewIndex, &View, LightSceneProxy, this, bProjectingForForwardShading, bSubPixelShadow);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Shader
|
||
投射阴影渲染用Shader为:
|
||
- ShadowProjectionVertexShader.usf
|
||
- FShadowProjectionNoTransformVS:直接输出顶点坐标。
|
||
- FShadowVolumeBoundProjectionVS:
|
||
- ShadowProjectionPixelShader.usf
|
||
- TShadowProjectionPS
|
||
- TShadowProjectionFromTranslucencyPS
|
||
- TDirectionalPercentageCloserShadowProjectionPS
|
||
- TModulatedShadowProjection
|
||
- ShadowProjectionCommon.ush
|
||
- ShadowDepthTexture
|
||
- ShadowDepthCubeTexture
|
||
|
||
主要步骤:
|
||
1. 将SceneDepth(ScreenSpace) =>(ShadowSpace)
|
||
2. 根据过滤方式调用:
|
||
1. 不过滤:`Shadow = LightSpacePixelDepthForOpaque < Texture2DSampleLevel(ShadowDepthTexture, ShadowDepthTextureSampler, ShadowPosition.xy, 0).r;`
|
||
2. PCSS:初始化FPCSSSamplerSettings之后调用DirectionalPCSS()
|
||
3. PCF:初始化FPCFSamplerSettings之后调用ManualPCF()
|
||
3. 调整阴影数值。钳制、模糊、过滤。
|
||
4. `OutColor = EncodeLightAttenuation(half4(FadedShadow, FadedSSSShadow, FadedShadow, FadedSSSShadow));`LinearSpace => sRGB 本质为sqrt(input),比pow(x, 1/2.2) 节约性能。
|
||
|
||
|
||
## RenderLight()
|
||
```c++
|
||
// Render the light to the scene color buffer, conditionally using the attenuation buffer or a 1x1 white texture as input
|
||
if (bDirectLighting)
|
||
{
|
||
for (int32 ViewIndex = 0, ViewCount = Views.Num(); ViewIndex < ViewCount; ++ViewIndex)
|
||
{
|
||
const FViewInfo& View = Views[ViewIndex];
|
||
|
||
// If the light elided the screen space shadow mask, sample directly from the packed shadow mask
|
||
int32 VirtualShadowMapId = INDEX_NONE;
|
||
if (bElideScreenShadowMask)
|
||
{
|
||
INC_DWORD_STAT(STAT_VSMLocalProjectionOnePassFast);
|
||
VirtualShadowMapId = VisibleLightInfo.GetVirtualShadowMapId(&View);
|
||
}
|
||
|
||
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, ViewCount > 1, "View%d", ViewIndex);
|
||
SCOPED_GPU_MASK(GraphBuilder.RHICmdList, View.GPUMask);
|
||
RenderLight(GraphBuilder, Scene, View, SceneTextures, &LightSceneInfo, VirtualShadowMapId != INDEX_NONE ? nullptr : ScreenShadowMaskTexture, LightingChannelsTexture, false /*bRenderOverlap*/, true /*bCloudShadow*/, VirtualShadowMapArray.GetUniformBuffer(), ShadowSceneRenderer->VirtualShadowMapMaskBits, VirtualShadowMapId);
|
||
}
|
||
}
|
||
```
|
||
|
||
在RenderLight()中通过PassParameters->PS = GetDeferredLightPSParameters()传入参数,使用ScreenShadowMaskTexture来渲染阴影(LightAttenuationTexture)。
|
||
### Shader
|
||
DeferredLightPixelShaders.usf
|
||
|
||
```c++
|
||
void DeferredLightPixelMain(
|
||
#if LIGHT_SOURCE_SHAPE > 0
|
||
float4 InScreenPosition : TEXCOORD0,
|
||
#else
|
||
float2 ScreenUV : TEXCOORD0,
|
||
float3 ScreenVector : TEXCOORD1,
|
||
#endif
|
||
float4 SVPos : SV_POSITION,
|
||
out float4 OutColor : SV_Target0
|
||
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
|
||
, out float3 OutOpaqueRoughRefractionSceneColor : SV_Target1
|
||
, out float3 OutSubSurfaceSceneColor : SV_Target2#endif
|
||
)
|
||
{
|
||
...
|
||
#else // STRATA_ENABLED
|
||
|
||
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(InputParams.ScreenUV);
|
||
// Only light pixels marked as using deferred shading
|
||
BRANCH if (ScreenSpaceData.GBuffer.ShadingModelID > 0
|
||
#if USE_LIGHTING_CHANNELS
|
||
&& (GetLightingChannelMask(InputParams.ScreenUV) & DeferredLightUniforms.LightingChannelMask)//灯光通道计算
|
||
#endif
|
||
)
|
||
{
|
||
const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV);
|
||
const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth);//FDerivedParams主要存储CameraVector以及摄像机的世界坐标
|
||
|
||
FDeferredLightData LightData = InitDeferredLightFromUniforms(CURRENT_LIGHT_TYPE);//根据灯光类型,填充对应灯光数据。
|
||
UpdateLightDataColor(LightData, InputParams, DerivedParams);//计算IES * Atmosphere * Cloud 的Attenuation。结果输出到LightData.Color
|
||
|
||
#if USE_HAIR_COMPLEX_TRANSMITTANCE
|
||
if (ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_HAIR && ShouldUseHairComplexTransmittance(ScreenSpaceData.GBuffer))
|
||
{
|
||
LightData.HairTransmittance = EvaluateDualScattering(ScreenSpaceData.GBuffer, DerivedParams.CameraVector, -DeferredLightUniforms.Direction);
|
||
}
|
||
#endif
|
||
|
||
float Dither = InterleavedGradientNoise(InputParams.PixelPos, View.StateFrameIndexMod8);//http://advances.realtimerendering.com/s2014/index.html
|
||
|
||
float SurfaceShadow = 1.0f;
|
||
|
||
float4 LightAttenuation = GetLightAttenuationFromShadow(InputParams, SceneDepth);//取得ScreenShadowProjection
|
||
float4 Radiance = GetDynamicLighting(DerivedParams.TranslatedWorldPosition, DerivedParams.CameraVector, ScreenSpaceData.GBuffer, ScreenSpaceData.AmbientOcclusion, ScreenSpaceData.GBuffer.ShadingModelID, LightData, LightAttenuation, Dither, uint2(InputParams.PixelPos), SurfaceShadow);
|
||
|
||
OutColor += Radiance;
|
||
}
|
||
|
||
#endif // STRATA_ENABLED
|
||
|
||
// RGB:SceneColor Specular and Diffuse
|
||
// A:Non Specular SceneColor Luminance
|
||
// So we need PreExposure for both color and alpha
|
||
OutColor.rgba *= GetExposure();
|
||
...
|
||
}
|
||
```
|
||
|
||
GetLightAttenuationFromShadow()
|
||
```c++
|
||
float4 GetLightAttenuationFromShadow(in FInputParams InputParams, float SceneDepth)
|
||
{
|
||
float4 LightAttenuation = float4(1, 1, 1, 1);
|
||
|
||
#if USE_VIRTUAL_SHADOW_MAP_MASK
|
||
if (VirtualShadowMapId != INDEX_NONE)
|
||
{
|
||
float ShadowMask = GetVirtualShadowMapMaskForLight(
|
||
ShadowMaskBits,
|
||
uint2(InputParams.PixelPos),
|
||
SceneDepth,
|
||
0, // TODO: EyeIndex
|
||
VirtualShadowMapId);
|
||
|
||
// TODO: Subsurface...?
|
||
return ShadowMask.xxxx;
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
return GetPerPixelLightAttenuation(InputParams.ScreenUV);
|
||
}
|
||
}
|
||
|
||
float4 GetPerPixelLightAttenuation(float2 UV)
|
||
{
|
||
return DecodeLightAttenuation(Texture2DSampleLevel(LightAttenuationTexture, LightAttenuationTextureSampler, UV, 0));//DecodeLightAttenuation return x * x;
|
||
}
|
||
```
|
||
|
||
GetDynamicLighting()
|
||
```c++
|
||
float4 GetDynamicLighting(
|
||
float3 TranslatedWorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID,
|
||
FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos,
|
||
inout float SurfaceShadow)
|
||
{
|
||
FDeferredLightingSplit SplitLighting = GetDynamicLightingSplit(
|
||
TranslatedWorldPosition, CameraVector, GBuffer, AmbientOcclusion, ShadingModelID,
|
||
LightData, LightAttenuation, Dither, SVPos,
|
||
SurfaceShadow);
|
||
|
||
return SplitLighting.SpecularLighting + SplitLighting.DiffuseLighting;
|
||
}
|
||
|
||
FDeferredLightingSplit GetDynamicLightingSplit(
|
||
float3 TranslatedWorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID,
|
||
FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos,
|
||
inout float SurfaceShadow)
|
||
{
|
||
FLightAccumulator LightAccumulator = AccumulateDynamicLighting(TranslatedWorldPosition, CameraVector, GBuffer, AmbientOcclusion, ShadingModelID, LightData, LightAttenuation, Dither, SVPos, SurfaceShadow);
|
||
return LightAccumulator_GetResultSplit(LightAccumulator);
|
||
}
|
||
|
||
FLightAccumulator AccumulateDynamicLighting(
|
||
float3 TranslatedWorldPosition, half3 CameraVector, FGBufferData GBuffer, half AmbientOcclusion, uint ShadingModelID,
|
||
FDeferredLightData LightData, half4 LightAttenuation, float Dither, uint2 SVPos,
|
||
inout float SurfaceShadow)
|
||
{
|
||
...
|
||
BRANCH
|
||
if( LightMask > 0 )
|
||
{
|
||
FShadowTerms Shadow;
|
||
Shadow.SurfaceShadow = AmbientOcclusion;
|
||
Shadow.TransmissionShadow = 1;
|
||
Shadow.TransmissionThickness = 1;
|
||
Shadow.HairTransmittance.OpaqueVisibility = 1;
|
||
const float ContactShadowOpacity = GBuffer.CustomData.a;
|
||
GetShadowTerms(GBuffer.Depth, GBuffer.PrecomputedShadowFactors, GBuffer.ShadingModelID, ContactShadowOpacity,
|
||
LightData, TranslatedWorldPosition, L, LightAttenuation, Dither, Shadow);
|
||
SurfaceShadow = Shadow.SurfaceShadow;
|
||
|
||
LightAccumulator.EstimatedCost += 0.3f; // add the cost of getting the shadow terms
|
||
|
||
#if SHADING_PATH_MOBILE
|
||
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
|
||
|
||
FDirectLighting Lighting = (FDirectLighting)0;
|
||
|
||
half NoL = max(0, dot(GBuffer.WorldNormal, L));
|
||
#if TRANSLUCENCY_NON_DIRECTIONAL
|
||
NoL = 1.0f;
|
||
#endif
|
||
Lighting = EvaluateBxDF(GBuffer, N, V, L, NoL, Shadow);
|
||
|
||
Lighting.Specular *= LightData.SpecularScale;
|
||
|
||
LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
|
||
LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
|
||
#else // SHADING_PATH_MOBILE
|
||
BRANCH
|
||
if( Shadow.SurfaceShadow + Shadow.TransmissionShadow > 0 )
|
||
{
|
||
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
|
||
|
||
#if NON_DIRECTIONAL_DIRECT_LIGHTING
|
||
float Lighting;
|
||
|
||
if( LightData.bRectLight )
|
||
{
|
||
FRect Rect = GetRect( ToLight, LightData );
|
||
|
||
Lighting = IntegrateLight( Rect );
|
||
}
|
||
else
|
||
{
|
||
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
|
||
|
||
Lighting = IntegrateLight( Capsule, LightData.bInverseSquared );
|
||
}
|
||
|
||
float3 LightingDiffuse = Diffuse_Lambert( GBuffer.DiffuseColor ) * Lighting;
|
||
LightAccumulator_AddSplit(LightAccumulator, LightingDiffuse, 0.0f, 0, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation);
|
||
#else
|
||
FDirectLighting Lighting;
|
||
|
||
if (LightData.bRectLight)
|
||
{
|
||
FRect Rect = GetRect( ToLight, LightData );
|
||
const FRectTexture SourceTexture = ConvertToRectTexture(LightData);
|
||
|
||
#if REFERENCE_QUALITY
|
||
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture, SVPos );
|
||
#else
|
||
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture);
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
|
||
|
||
#if REFERENCE_QUALITY
|
||
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, SVPos );
|
||
#else
|
||
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );
|
||
#endif
|
||
}
|
||
|
||
Lighting.Specular *= LightData.SpecularScale;
|
||
|
||
LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
|
||
LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
|
||
|
||
LightAccumulator.EstimatedCost += 0.4f; // add the cost of the lighting computations (should sum up to 1 form one light)
|
||
#endif
|
||
}
|
||
#endif // SHADING_PATH_MOBILE
|
||
}
|
||
return LightAccumulator;
|
||
}
|
||
```
|
||
|
||
### GetShadowTerms()
|
||
1. ContactShadow在GetShadowTerms()的ShadowRayCast()处开始计算。
|
||
|
||
```c++
|
||
void GetShadowTerms(float SceneDepth, half4 PrecomputedShadowFactors, uint ShadingModelID, float ContactShadowOpacity, FDeferredLightData LightData, float3 TranslatedWorldPosition, half3 L, half4 LightAttenuation, float Dither, inout FShadowTerms Shadow)
|
||
{
|
||
float ContactShadowLength = 0.0f;
|
||
const float ContactShadowLengthScreenScale = GetTanHalfFieldOfView().y * SceneDepth;
|
||
|
||
BRANCH
|
||
if (LightData.ShadowedBits)
|
||
{
|
||
// Remapping the light attenuation buffer (see ShadowRendering.cpp)
|
||
|
||
// LightAttenuation: Light function + per-object shadows in z, per-object SSS shadowing in w,
|
||
// Whole scene directional light shadows in x, whole scene directional light SSS shadows in y
|
||
// Get static shadowing from the appropriate GBuffer channel
|
||
#if ALLOW_STATIC_LIGHTING
|
||
half UsesStaticShadowMap = dot(LightData.ShadowMapChannelMask, half4(1, 1, 1, 1));
|
||
half StaticShadowing = lerp(1, dot(PrecomputedShadowFactors, LightData.ShadowMapChannelMask), UsesStaticShadowMap);
|
||
#else
|
||
half StaticShadowing = 1.0f;
|
||
#endif
|
||
|
||
if (LightData.bRadialLight || SHADING_PATH_MOBILE)
|
||
{
|
||
// Remapping the light attenuation buffer (see ShadowRendering.cpp)
|
||
|
||
Shadow.SurfaceShadow = LightAttenuation.z * StaticShadowing;
|
||
// SSS uses a separate shadowing term that allows light to penetrate the surface
|
||
//@todo - how to do static shadowing of SSS correctly?
|
||
Shadow.TransmissionShadow = LightAttenuation.w * StaticShadowing;
|
||
|
||
Shadow.TransmissionThickness = LightAttenuation.w;
|
||
}
|
||
else
|
||
{
|
||
// Remapping the light attenuation buffer (see ShadowRendering.cpp)
|
||
// R: WholeSceneShadows, non SSS
|
||
// G: WholeSceneShadows, SSS
|
||
// B: non WholeSceneShadows, non SSS
|
||
// A: non WholeSceneShadows, SSS
|
||
//
|
||
// SSS: SubsurfaceScattering materials
|
||
// non SSS: shadow for opaque materials
|
||
// WholeSceneShadows: directional light CSM
|
||
// non WholeSceneShadows: spotlight, per object shadows, translucency lighting, omni-directional lights
|
||
|
||
float DynamicShadowFraction = DistanceFromCameraFade(SceneDepth, LightData);
|
||
// For a directional light, fade between static shadowing and the whole scene dynamic shadowing based on distance + per object shadows
|
||
Shadow.SurfaceShadow = lerp(LightAttenuation.x, StaticShadowing, DynamicShadowFraction);//针对DirectionalLight有一个StaticShadow与DynamicShadow过度。
|
||
// Fade between SSS dynamic shadowing and static shadowing based on distance
|
||
Shadow.TransmissionShadow = min(lerp(LightAttenuation.y, StaticShadowing, DynamicShadowFraction), LightAttenuation.w);//SSS阴影
|
||
|
||
Shadow.SurfaceShadow *= LightAttenuation.z;
|
||
Shadow.TransmissionShadow *= LightAttenuation.z;
|
||
|
||
// Need this min or backscattering will leak when in shadow which cast by non perobject shadow(Only for directional light)
|
||
Shadow.TransmissionThickness = min(LightAttenuation.y, LightAttenuation.w);
|
||
}
|
||
|
||
FLATTEN
|
||
if (LightData.ShadowedBits > 1 && LightData.ContactShadowLength > 0)
|
||
{
|
||
ContactShadowLength = LightData.ContactShadowLength * (LightData.ContactShadowLengthInWS ? 1.0f : ContactShadowLengthScreenScale);
|
||
}
|
||
}
|
||
|
||
#if SUPPORT_CONTACT_SHADOWS
|
||
|
||
#if STRATA_ENABLED == 0
|
||
if (LightData.ShadowedBits < 2 && (ShadingModelID == SHADINGMODELID_HAIR))
|
||
{
|
||
ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
|
||
}
|
||
// World space distance to cover eyelids and eyelashes but not beyond
|
||
if (ShadingModelID == SHADINGMODELID_EYE)
|
||
{
|
||
ContactShadowLength = 0.5;
|
||
|
||
}
|
||
#endif
|
||
|
||
#if MATERIAL_CONTACT_SHADOWS
|
||
ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
|
||
#endif
|
||
|
||
BRANCH
|
||
if (ContactShadowLength > 0.0)//开始计算接触阴影
|
||
{
|
||
float StepOffset = Dither - 0.5;
|
||
bool bHitCastContactShadow = false;
|
||
bool bHairNoShadowLight = ShadingModelID == SHADINGMODELID_HAIR && !LightData.ShadowedBits;
|
||
float HitDistance = ShadowRayCast( TranslatedWorldPosition, L, ContactShadowLength, 8, StepOffset, bHairNoShadowLight, bHitCastContactShadow );
|
||
|
||
if ( HitDistance > 0.0 )
|
||
{
|
||
float ContactShadowOcclusion = bHitCastContactShadow ? LightData.ContactShadowCastingIntensity : LightData.ContactShadowNonCastingIntensity;
|
||
|
||
#if STRATA_ENABLED == 0
|
||
// Exponential attenuation is not applied on hair/eye/SSS-profile here, as the hit distance (shading-point to blocker) is different from the estimated
|
||
// thickness (closest-point-from-light to shading-point), and this creates light leaks. Instead we consider first hit as a blocker (old behavior)
|
||
BRANCH
|
||
if (ContactShadowOcclusion > 0.0 &&
|
||
IsSubsurfaceModel(ShadingModelID) &&
|
||
ShadingModelID != SHADINGMODELID_HAIR &&
|
||
ShadingModelID != SHADINGMODELID_EYE &&
|
||
ShadingModelID != SHADINGMODELID_SUBSURFACE_PROFILE)
|
||
{
|
||
// Reduce the intensity of the shadow similar to the subsurface approximation used by the shadow maps path
|
||
// Note that this is imperfect as we don't really have the "nearest occluder to the light", but this should at least
|
||
// ensure that we don't darken-out the subsurface term with the contact shadows
|
||
float Density = SubsurfaceDensityFromOpacity(ContactShadowOpacity);
|
||
ContactShadowOcclusion *= 1.0 - saturate( exp( -Density * HitDistance ) );
|
||
}
|
||
#endif
|
||
|
||
float ContactShadow = 1.0 - ContactShadowOcclusion;
|
||
|
||
Shadow.SurfaceShadow *= ContactShadow;
|
||
Shadow.TransmissionShadow *= ContactShadow;
|
||
}
|
||
|
||
}
|
||
#endif
|
||
|
||
Shadow.HairTransmittance = LightData.HairTransmittance;
|
||
Shadow.HairTransmittance.OpaqueVisibility = Shadow.SurfaceShadow;
|
||
}
|
||
```
|
||
|
||
# Remark
|
||
1. RenderDepth()渲染完深度贴图之后,经过GatherAndSortLights()、ComputeLightGrid()获取到经过剪裁的FSortedLightSetSceneInfo &SortedLightSet,之后传递给RenderLights()。
|
||
2. BindShadowProjectionShaders() => BindShaderShaders() =>
|
||
3. TShadowProjectionPS => ProjectionParameters.Bind(Initializer); => ShadowDepthTexture.Bind(ParameterMap,TEXT("ShadowDepthTexture"));
|