515 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Untitled
date: 2024-12-08 12:18:54
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**:存储投影阴影先关信息。包含各种变换矩阵、阴影渲染函数以及渲染参数、灯光&场景&图元信息。
- SceneRendering.h
FVisibleLightInfo可见光源信息, 主要是阴影相关的信息.
FVisibleLightViewInfo
## DynamicShadows
### 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);
}
```
### 阴影渲染
# 阴影偏移
可以考虑的Buffer有
- ShadowDepths
- CustomDepth
# 相关Paas
1. ShadowDepths => RenderShadowDepthMaps()
2. Lights => RenderLights()
1. DirectLighting
1. UnbatchedLights
1. ShadowProjectionOnOpaque
# ShadowDepths
- FSceneRenderer::RenderShadowDepthMaps()位于CustomDepth之前。
- **RenderVirtualShadowMaps()**
- RenderShadowDepthMapAtlases()
- SortedShadowsForShadowDepthPass.ShadowMapCubemaps循环。渲染点光源阴影立方体图
- **FProjectedShadowInfo::RenderDepth()**
- **FProjectedShadowInfo::RenderTranslucencyDepths**
## 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;
}
```
# Lights
### Shader
ShadowProjectionPixelShader.usf
- TShadowProjectionPS
- TDirectionalPercentageCloserShadowProjectionPS方向光投影
- TSpotPercentageCloserShadowProjectionPSSpotLight
- FOnePassPointShadowProjectionPS(Moible?)
### 相关函数
- FDeferredShadingSceneRenderer::RenderLights()
- FDeferredShadingSceneRenderer::RenderDeferredShadowProjections()
- FSceneRenderer::RenderShadowProjections()
- FProjectedShadowInfo::SetupFrustumForProjection()构建阴影投影4棱椎平面信息。
- FProjectedShadowInfo::SetupProjectionStencilMask()
# 其他
顺序:
RenderCustomDepthPass
FSceneRenderer::CreateDynamicShadows
=>
FSceneRenderer::CreatePerObjectProjectedShadow
```c++
if (!IsForwardShadingEnabled(ShaderPlatform))
{
// Dynamic shadows are synced later when using the deferred path to make more headroom for tasks.
FinishInitDynamicShadows(GraphBuilder, InitViewTaskDatas.DynamicShadows, InstanceCullingManager, ExternalAccessQueue);
}
```
```c++
if (RendererOutput == ERendererOutput::DepthPrepassOnly)
{
RenderOcclusionLambda();
if (bUpdateNaniteStreaming)
{
Nanite::GStreamingManager.SubmitFrameStreamingRequests(GraphBuilder);
}
CopySceneCaptureComponentToTarget(GraphBuilder, SceneTextures, ViewFamilyTexture, ViewFamily, Views);
}
else
{
GVRSImageManager.PrepareImageBasedVRS(GraphBuilder, ViewFamily, SceneTextures);
if (!IsForwardShadingEnabled(ShaderPlatform))
{
// Dynamic shadows are synced later when using the deferred path to make more headroom for tasks.
FinishInitDynamicShadows(GraphBuilder, InitViewTaskDatas.DynamicShadows, InstanceCullingManager, ExternalAccessQueue);
}
// Update groom only visible in shadow
if (IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform()) && RendererOutput == ERendererOutput::FinalSceneColor)
{
UpdateHairStrandsBookmarkParameters(Scene, Views, HairStrandsBookmarkParameters);
// Interpolation for cards/meshes only visible in shadow needs to happen after the shadow jobs are completed
const bool bRunHairStrands = HairStrandsBookmarkParameters.HasInstances() && (Views.Num() > 0);
if (bRunHairStrands)
{
RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessCardsAndMeshesInterpolation_ShadowView, HairStrandsBookmarkParameters);
}
}
// NOTE: The ordering of the lights is used to select sub-sets for different purposes, e.g., those that support clustered deferred.
FSortedLightSetSceneInfo& SortedLightSet = *GraphBuilder.AllocObject<FSortedLightSetSceneInfo>();
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, SortLights);
RDG_GPU_STAT_SCOPE(GraphBuilder, SortLights);
ComputeLightGridOutput = GatherLightsAndComputeLightGrid(GraphBuilder, bComputeLightGrid, SortedLightSet);
}
```