1138 lines
48 KiB
Markdown
Raw Normal View History

2024-12-08 12:40:30 +08:00
---
title: Untitled
date: 2024-12-08 12:18:54
excerpt:
tags:
rating: ⭐
---
2024-12-10 11:52:13 +08:00
# 阴影
2024-12-10 19:12:19 +08:00
- [剖析虚幻渲染体系05- 光源和阴影](https://www.cnblogs.com/timlly/p/14817455.html)
2024-12-10 11:52:13 +08:00
## 阴影类型
- **Static Shadow**
- **Cascading Shadow Map**
- **Per Object Shadow**可移动组件使用的逐物体阴影应用阴影图到物体的包围盒因此包围盒必须是精确的。对于骨骼网格这意味着它们应该有一个物理资产。对于粒子系统任何固定的边界框必须足够大以容纳所有的粒子。_在网格的Lighting属性组中Dynamic Inset Shadow可以开启逐物体阴影对需要高质量高精度的物体非常有用。_
- **Dynamic Shadow**:可移动光源在所有物体上投射出完全动态的阴影(和光)。这种光源的任何数据不会被烘焙到光照图中,它可以自由地在所有东西上投射动态阴影。静态网格、骨架网格、粒子效果等等将完全从可移动光源投射和接收动态阴影。
- **Capsule Shadow**
- **Contact Shadow**
- **Distance Field Shadow**
## 相关类型
2024-12-10 14:06:18 +08:00
- ShadowRendering.h
**FProjectedShadowInfo**:存储投影阴影先关信息。包含各种变换矩阵、阴影渲染函数以及渲染参数、灯光&场景&图元信息。
- SceneRendering.h
FVisibleLightInfo可见光源信息, 主要是阴影相关的信息.
FVisibleLightViewInfo
2024-12-10 11:52:13 +08:00
2024-12-10 14:06:18 +08:00
## DynamicShadows
2024-12-10 16:35:29 +08:00
### InitDynamicShadows() => CreateDynamicShadows()
2024-12-10 14:06:18 +08:00
InitViews() => FSceneRenderer::InitDynamicShadows()
2024-12-10 16:35:29 +08:00
PS. UE5.3中相关逻辑移动到***CreateDynamicShadows()*** 中了。InitDynamicShadows() => BeginInitDynamicShadows() => BeginGatherShadowPrimitives() => CreateDynamicShadows()
计算各种灯光类型,之后调用:
- CreateWholeSceneProjectedShadow()
- AddViewDependentWholeSceneShadowsForView()
- SetupInteractionShadows()
2024-12-10 17:25:56 +08:00
- ***CreatePerObjectProjectedShadow()***
2024-12-10 19:12:19 +08:00
- 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);
}
```
2024-12-10 14:06:18 +08:00
2024-12-10 19:12:19 +08:00
### 阴影渲染
2024-12-10 14:06:18 +08:00
2024-12-10 11:52:13 +08:00
2024-12-08 12:40:30 +08:00
# 阴影偏移
可以考虑的Buffer有
2024-12-08 13:54:44 +08:00
- ShadowDepths
2024-12-08 12:40:30 +08:00
- CustomDepth
2024-12-09 19:06:34 +08:00
# 相关Paas
2024-12-10 19:12:19 +08:00
1. ShadowDepths => RenderShadowDepthMaps()
2. Lights => RenderLights()
2024-12-08 19:13:24 +08:00
1. DirectLighting
1. UnbatchedLights
1. ShadowProjectionOnOpaque
2024-12-09 19:06:34 +08:00
# ShadowDepths
2024-12-16 22:25:20 +08:00
渲染阴影深度贴图与图集。
2024-12-09 19:06:34 +08:00
- FSceneRenderer::RenderShadowDepthMaps()位于CustomDepth之前。
2024-12-16 22:25:20 +08:00
- RenderVirtualShadowMaps()
- ***RenderShadowDepthMapAtlases()***
2024-12-12 18:54:53 +08:00
- SortedShadowsForShadowDepthPass.ShadowMapCubemaps循环。渲染点光源阴影立方体图
- **FProjectedShadowInfo::RenderDepth()**
- **FProjectedShadowInfo::RenderTranslucencyDepths**
2024-12-17 00:14:17 +08:00
RenderShadowDepthMapAtlases() 渲染的图集位于***FSceneRenderer::SortedShadowsForShadowDepthPass.ShadowMapAtlases*** (ShadowMapAtlases.RenderTargets.DepthTarget为深度ShadowMapAtlases.Shadows 为FProjectedShadowInfo)
2024-12-16 22:25:20 +08:00
2024-12-12 18:54:53 +08:00
## RenderDepth
2024-12-12 23:06:32 +08:00
MeshDrawProcessor为***FShadowDepthPassMeshProcessor***。渲染的Shader为***ShadowDepthPixelShader.usf***
2024-12-12 19:11:10 +08:00
```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 );
}
```
2024-12-11 10:03:39 +08:00
2024-12-12 22:27:42 +08:00
## 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;
}
```
2024-12-12 23:06:32 +08:00
## 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
2024-12-12 22:27:42 +08:00
2024-12-12 23:06:32 +08:00
#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
}
```
2024-12-16 10:18:10 +08:00
# RenderLights
2024-12-16 22:25:20 +08:00
渲染ScreenShadowMask并用于光照计算。
2024-12-16 10:18:10 +08:00
FDeferredShadingSceneRenderer::RenderLights()
=>
RDG_EVENT_SCOPE(GraphBuilder, "UnbatchedLights");//batchedLights为没有LightFunction与没有阴影的灯光。
=>
for (int32 LightIndex = UnbatchedLightStart; LightIndex < LumenLightStart; LightIndex++)
=>
2024-12-16 12:48:15 +08:00
if (bDrawShadows){...} => RenderRayTracingShadows()/***RenderDeferredShadowProjections()***
2024-12-16 10:18:10 +08:00
=>
if (bDirectLighting){...}
2024-12-16 12:48:15 +08:00
if (bDirectLighting){...} => RenderLight()
if (bUseHairLighting){...}=> RenderLightForHair()
2024-12-16 10:18:10 +08:00
## RenderDeferredShadowProjections()
2024-12-16 12:48:15 +08:00
```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);
}
}
}
```
2024-12-16 10:18:10 +08:00
2024-12-16 12:48:15 +08:00
RenderShadowProjections():
1. 取得当前FVisibleLightInfo、FLightSceneProxy创建FProjectedShadowInfoArray DistanceFieldShadows、NormalShadows。
2. 遍历VisibleLightInfo.ShadowsToProject按照阴影特征将每个FProjectedShadowInfo加入DistanceFieldShadows、NormalShadows。
3. 调用Lamda来渲染ScreenShadowMaskTexture与ScreenShadowMaskSubPixelTexture。
2024-12-17 10:55:22 +08:00
4. 遍历DistanceFieldShadows调用FProjectedShadowInfo->RenderRayTracedDistanceFieldProjection()来渲染距离场阴影。
### FSceneRenderer::RenderShadowProjections()
2024-12-16 22:25:20 +08:00
1. 初始化UniformStruct变量。
2. 遍历传入的FProjectedShadowInfo数组并调用**ProjectedShadowInfo->RenderProjection()** 将所有灯光的阴影Mask绘制到一张ScreenShadowMask上。
1. 在TShadowProjectionPS中通过SetParameters() => ProjectionParameters.Set()来设置ShadowDepthTextureValue。`ShadowDepthTextureValue = ShadowInfo->RenderTargets.DepthTarget->GetRHI();`
2024-12-17 10:55:22 +08:00
```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
2024-12-16 22:25:20 +08:00
- ShadowProjectionCommon.ush
- ShadowDepthTexture
- ShadowDepthCubeTexture
2024-12-16 10:18:10 +08:00
2024-12-17 00:14:17 +08:00
主要步骤:
1. 将SceneDepthScreenSpace =>ShadowSpace
2024-12-17 00:14:17 +08:00
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) 节约性能。
## 总结
1. RenderDepth()渲染完深度贴图之后经过GatherAndSortLights()、ComputeLightGrid()获取到经过剪裁的FSortedLightSetSceneInfo &SortedLightSet之后传递给RenderLights()。
2. BindShadowProjectionShaders() => BindShaderShaders() =>
3. TShadowProjectionPS => ProjectionParameters.Bind(Initializer); => ShadowDepthTexture.Bind(ParameterMap,TEXT("ShadowDepthTexture"));
2024-12-17 10:55:22 +08:00
## 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);
}
}
```
2024-12-17 12:08:05 +08:00
在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;
}
```
2024-12-17 15:47:36 +08:00
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);
}
```
2024-12-16 10:18:10 +08:00
2024-12-13 16:22:58 +08:00
# 半程阴影
由晨风&Neverwind提出
- 【[UFSH2024]用虚幻引擎5为《幻塔》定制高品质动画流程风格化渲染管线 | 晨风 Neverwind 完美世界游戏】 【精准空降到 07:27】 https://www.bilibili.com/video/BV1rW2LYvEox/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=447
**阴影Setup阶段**:
```c++
if 启用半程阴影:
{
额外进行一次CreatePerObjectProjectedShadow()
{
2024-12-16 10:18:10 +08:00
处理阴影光照Matrix //猜测:
//在ProjectedShadowInfo->SetupPerObjectProjection()中调整FProjectedShadowInfo.TranslatedWorldToView。
//在LightSceneInfo->Proxy->GetPerObjectProjectedShadowInitializer(Bounds, ShadowInitializer)之后修改WorldTolight。
向阴影信息写入IsHalfViewShadowFlag//猜测在FProjectedShadowInfo中添加判断Flag并且写入。
用新的光源方向画Atlas //猜测给带有对应Flog的DirectionLigh多创建一个对应的Atlas
2024-12-13 16:22:58 +08:00
}
}
```
2024-12-16 10:18:10 +08:00
截图代码(半程阴影修改LightDirection逻辑)
2024-12-13 16:22:58 +08:00
```c++
if(PrimitiveSceneinfo->Proxy->IsToonDisableSelfShadow())
{
...
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const FMatrix& ViewMatrix = View.ShadowViewMatrices.GetViewMatrix()
FVector LightDirection = Lightsceneinfo->Proxy- GetDirection();
const FVector CameraDirection = ViewMatrix.GetColumn(2).GetSafeNonmal()
float LightViewBlendFactor = PrimitiveSceneinfo->Proxy->GetToonHalfVienShadowFactor();
Fvector HalfViewLightDir = (LightDirection + ( 1 - LightViewBlendFactor) + CameraDirection * LightViewBlendFactor).GetSafeNormal();
FMatrix FinalCombineMatrix = FNnverseRotationMatrix(HalfViewLightDir.Rotation())
ShadowInitializer.WorldTolight = FinalCombineMatrix;
}
...
}
2024-12-16 10:18:10 +08:00
```
PS.很有可能需要创建2个Atlas。Atlas的创建位于***FSceneRenderer::AllocateCachedShadowDepthTargets()***。数据存储在***SortedShadowsForShadowDepthPass.ShadowMapAtlases***中。大致由FSceneRenderer::FinishInitDynamicShadows()调用。
**阴影Projection阶段**:
//此阶段需要屏蔽角色投射到自己的非半程阴影
//和角色投射到场景中会跟随视角移动的阴影
```c++
2024-12-16 22:25:20 +08:00
if(Toon材质,且没有半程阴影Flag的阴影
2024-12-16 10:18:10 +08:00
&&非Toon材质但有半程阴影Flag的阴影)
{
屏蔽此阴影
}
```
2024-12-16 22:25:20 +08:00
PS.很有可能在FProjectedShadowInfo::RenderProjection()阶段进行判断以此保证合成正确的**ScreenShadowMask**。