Merge remote-tracking branch 'origin/master'

# Conflicts:
#	03-UnrealEngine/卡通渲染相关资料/渲染功能/阴影控制/ToonShadow.md
This commit is contained in:
BlueRose 2024-12-23 11:48:17 +08:00
commit 7473ba5c5d
11 changed files with 1635 additions and 944 deletions

View File

@ -1 +1 @@
{"SequoiaCamShotEvalTemplate":{"SequoiaCamShotEvalTemplate":{"currentFile":{"count":1,"lastUpdated":1732609264076}}},"c++内存泄漏分析工具":{"c++内存泄漏分析工具":{"internalLink":{"count":1,"lastUpdated":1733137754779}}},"Lights":{"Lights":{"currentFile":{"count":1,"lastUpdated":1733637911876}}},"渲染屏幕空间阴影遮罩,并用于光照计算。":{"渲染屏幕空间阴影遮罩,并用于光照计算。":{"currentFile":{"count":1,"lastUpdated":1734354537396}}}}
{"SequoiaCamShotEvalTemplate":{"SequoiaCamShotEvalTemplate":{"currentFile":{"count":1,"lastUpdated":1732609264076}}},"c++内存泄漏分析工具":{"c++内存泄漏分析工具":{"internalLink":{"count":1,"lastUpdated":1733137754779}}},"Lights":{"Lights":{"currentFile":{"count":1,"lastUpdated":1733637911876}}},"FShadowProjectionNoTransformVSShadowProjectionNoTransformVS、FShadowVolumeBoundProjectionVSShadowVolumeBoundProjectionVS":{"FShadowProjectionNoTransformVSShadowProjectionNoTransformVS、FShadowVolumeBoundProjectionVSShadowVolumeBoundProjectionVS":{"currentFile":{"count":1,"lastUpdated":1734345863513}}},"DecodeLightAttenuation":{"DecodeLightAttenuation":{"currentFile":{"count":1,"lastUpdated":1734406452310}}},"clamped":{"clamped":{"currentFile":{"count":1,"lastUpdated":1734590559877}}}}

View File

@ -15,7 +15,33 @@
- [x] 恢复灯牌显影功能
- [x] 帮助跃洋在动画蓝图中使用骨骼缩放的方式制作JiaRan、NaiLin2套衣服的长款版本并且修改资产名称解决相关资产的上传问题。
- [x] 添加水晶石场景道具。
- [ ] 4周年相关工作
- [x] 4周年相关工作
- [x] 解决王志 4周年舞台 反射球问题。
- [x] 帮罗导添加2个才艺的VJ更换一个才艺中所有角色为轻量版角色。
- [ ]
# 12.16~12.20
- [x] 帮助朱雨辰解决:
- [x] 4周年角色不能完全变黑问题。
- [x] 渲片时的天空球问题切换天空盒问题。
- [x] 给2个Sequence添加VJ
- [x] 巨蛋开关切换以及显示荧光棒。
- [x] 协助月哥找到雪交互道具的问题。
- [x] BP07 小舞台
- [x] BP07 才艺数据没有物理的问题。
- [x] 乃琳道具耳朵的描边模型的物理与耳朵模型不匹配。=> 移除描边模型
- [x] 飞行状态下需要给贝拉的物理设置忽略RootMotion移动。=> 在罗导的Sequence中K后处理蓝图事件来设置Ignore Component Transform
# 12.23~12.27
- [x] 添加LiveArea Name搜索功能。
- [ ] BP_CatHand_R失效判定
# 场景迁移需求
给天气系统添加新的配置与渲染功能参数调整。
- 相关渲染功能
1. ExponentialHeightFog
2. SkyLight
3. Multi DirectionalLights
1. ~~固定时间?~~
PS. SkyLight需要避免使用Cubemap这个参数没办法插值。

View File

@ -0,0 +1,6 @@
ALiveDirectorGameMode::GetSeamlessTravelActorList
https://www.uejoy.com/archives/1130
使用了UE5的RelicationGraphUDirectorReplicationGraphNode_AlwaysRelevant_ForConnection::GatherActorListsForConnection()

View File

@ -0,0 +1,76 @@
Content/UIAssets/MapEnvironments/Prop/WBP_MapEnvironmentSelectPropPopupView
搜索逻辑:
```c++
private ChangeSource(source: number/**@cpp:int*/): void {
this.curSelect = null;
MapEnvironmentSetting.SelectPropType = source;
this.widget.List.ClearListItems();
this.widget.List.BP_ClearSelection();
this.widget.Effect.SetVisibility((source == UE.EScenePropType.Item || source == UE.EScenePropType.LiveLink)? UE.ESlateVisibility.Visible: UE.ESlateVisibility.Collapsed);
let colorNormal = new UE.LinearColor(0, 0, 0, 0);
let colorSelect = new UE.LinearColor(73/255, 73/255, 73/255, 1);
for (let index = 0; index < this.btnBgs.Num(); index++) {
const element = this.btnBgs.GetRef(index);
element.SetBrushColor(index == source? colorSelect: colorNormal);
}
if (this.manager == null) {
return
}
let search =this.widget.SearchBox.GetText();
let assets = this.manager.AssetManager.AllScenePropAssetList
for (let index = 0; index < assets.Num(); index++) {
const element = assets.GetRef(index);
if (element.PropType === source) {
if (search === "" || element.DisplayName.includes(search)) {
this.widget.List.AddItem(element);
}
}
}
}
```
- WBP_AreaSelectPopupView
- WBP_MapEnvironmentSingleSelectItemView
```c++
private SetSelectLevel(levelData: UE.MapEnvironmentLevelItem): void {
this.curSelectLevelName = levelData.LevelData.LevelName;
this.curSelectAreaName = "";
this.curSelectAreaGuid = new UE.Guid();
this.bAreaIsDynamic = false;
this.widget.AreaList.ClearListItems();
this.widget.AreaList.BP_ClearSelection();
let areas = levelData.LevelData.Areas;
// let selectIdx = -1;
for (let i = 0; i < areas.Num(); i++) {
const area = areas.GetRef(i);
let data = new UE.MapEnvironmentAreaItem();
data.AreaData = area;
this.widget.AreaList.AddItem(data);
// if (DirectorCamUtil.IsGuidEqual(area.UUID, this.curSelectAreaGuid)) {
// selectIdx = i;
// }
}
// if (selectIdx >= 0) {
// this.widget.AreaList.SetSelectedIndex(selectIdx);
// }
this.RefreshDynamicBtn();
}
```

View File

@ -1,65 +0,0 @@
---
title: Lighting阶段的ShadowMap逻辑
date: 2023-04-10 14:45:45
excerpt:
tags:
rating: ⭐
---
## PS Parameter
`FDeferredLightPS::FParameters GetDeferredLightPSParameters()`
- LightAttenuationTexture = ShadowMaskTexture ? ShadowMaskTexture : WhiteDummy;
- One pass projection
- VirtualShadowMap = VirtualShadowMapUniformBuffer;
- VirtualShadowMapId = VirtualShadowMapId;
- ShadowMaskBits = ShadowMaskBits ? ShadowMaskBits : GSystemTextures.GetZeroUIntDummy(GraphBuilder);
最后输出
- SceneColorTexture
- SceneDepthTexture
## Lighting阶段中VSM 渲染函数
RenderDeferredShadowProjections(),输出`ScreenShadowMaskTexture`
## 问题所在
LightAttenuation
- x整个场景的方向光阴影。
- y整个场景的方向光SSS阴影。
- zLight function + per-object shadows
- wper-object SSS shadowing in w
zw用于非方向光或者移动端渲染路径。
```c++
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)
// Also fix up the fade between dynamic and static shadows // to work with plane splits rather than spheres.
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);
// Fade between SSS dynamic shadowing and static shadowing based on distance
Shadow.TransmissionShadow = min(lerp(LightAttenuation.y, StaticShadowing, DynamicShadowFraction), LightAttenuation.w);
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);
}
```
**DeferredLightingCommon.ush**与**DeferredLightPixelShaders.usf**中的**LightAttenuation**即为ShadowMap数据。具体可以参考**GetLightAttenuationFromShadow()**可以看得出EPIC之后打算通过抖动来消除VSM的阴影锯齿。
主要的阴影效果由**LightAttenuation**与ShadingModels.ush中的NoL提供。我们只需要调整自阴影也就是z通道其他的整个场景的阴影。

File diff suppressed because it is too large Load Diff

View File

@ -21,11 +21,6 @@ Shader推荐
- DepthOnlyVertexShader.usf
- DepthOnlyPixelShader.usf
# NaniteMeshDraw
`Engine\Source\Runtime\Renderer\Private\Nanite\`NaniteMaterials.h & NaniteMaterials.cpp
PS.使用的Shader必须是`FNaniteGlobalShader`的子类。
## BasePass
### DrawBasePass()
该函数在FDeferredShadingSceneRenderer::RenderBasePassInternal()中调用。
@ -255,4 +250,164 @@ FViewInfo& ViewInfo
- DECLARE_GPU_STAT_NAMED_EXTERN(NaniteBasePass, TEXT("Nanite BasePass"));
- GET_STATID(STAT_CLP_BasePass)
- FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), View, FParallelCommandListBindings(PassParameters));
- DECLARE_CYCLE_STAT(TEXT("BasePass"), STAT_CLP_BasePass, STATGROUP_ParallelCommandListMarkers);
- DECLARE_CYCLE_STAT(TEXT("BasePass"), STAT_CLP_BasePass, STATGROUP_ParallelCommandListMarkers);
# NaniteMeshDraw
`Engine\Source\Runtime\Renderer\Private\Nanite\`NaniteMaterials.h & NaniteMaterials.cpp
PS.使用的Shader必须是`FNaniteGlobalShader`的子类。
以下是Nanite物体的CustomDepth绘制过程
```c++
bool FSceneRenderer::RenderCustomDepthPass(
FRDGBuilder& GraphBuilder,
FCustomDepthTextures& CustomDepthTextures,
const FSceneTextureShaderParameters& SceneTextures,
TConstArrayView<Nanite::FRasterResults> PrimaryNaniteRasterResults,
TConstArrayView<Nanite::FPackedView> PrimaryNaniteViews)
{
if (!CustomDepthTextures.IsValid())
{
return false;
}
// Determine if any of the views have custom depth and if any of them have Nanite that is rendering custom depth
// 构建NaniteDrawLists用于后面的绘制
bool bAnyCustomDepth = false;
TArray<FNaniteCustomDepthDrawList, SceneRenderingAllocator> NaniteDrawLists;
NaniteDrawLists.AddDefaulted(Views.Num());
uint32 TotalNaniteInstances = 0;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
if (View.ShouldRenderView() && View.bHasCustomDepthPrimitives)
{
if (PrimaryNaniteRasterResults.IsValidIndex(ViewIndex))
{
const FNaniteVisibilityResults& VisibilityResults = PrimaryNaniteRasterResults[ViewIndex].VisibilityResults;
// Get the Nanite instance draw list for this view. (NOTE: Always use view index 0 for now because we're not doing
// multi-view yet).
NaniteDrawLists[ViewIndex] = BuildNaniteCustomDepthDrawList(View, 0u, VisibilityResults);
TotalNaniteInstances += NaniteDrawLists[ViewIndex].Num();
}
bAnyCustomDepth = true;
}
}
SET_DWORD_STAT(STAT_NaniteCustomDepthInstances, TotalNaniteInstances);
..
if (TotalNaniteInstances > 0)
{
RDG_EVENT_SCOPE(GraphBuilder, "Nanite CustomDepth");
const FIntPoint RasterTextureSize = CustomDepthTextures.Depth->Desc.Extent;
FIntRect RasterTextureRect(0, 0, RasterTextureSize.X, RasterTextureSize.Y);
if (Views.Num() == 1)
{
const FViewInfo& View = Views[0];
if (View.ViewRect.Min.X == 0 && View.ViewRect.Min.Y == 0)
{
RasterTextureRect = View.ViewRect;
}
}
const bool bWriteCustomStencil = IsCustomDepthPassWritingStencil();
Nanite::FSharedContext SharedContext{};
SharedContext.FeatureLevel = Scene->GetFeatureLevel();
SharedContext.ShaderMap = GetGlobalShaderMap(SharedContext.FeatureLevel);
SharedContext.Pipeline = Nanite::EPipeline::Primary;
// TODO: If !bWriteCustomStencil, we could copy off the depth and rasterize depth-only (probable optimization)
//初始化Nanite::FRasterContext RasterContext。
Nanite::FRasterContext RasterContext = Nanite::InitRasterContext(
GraphBuilder,
SharedContext,
ViewFamily,
RasterTextureSize,
RasterTextureRect,
false, // bVisualize
Nanite::EOutputBufferMode::VisBuffer,
true, // bClearTarget
nullptr, // RectMinMaxBufferSRV
0, // NumRects
nullptr, // ExternalDepthBuffer
true // bCustomPass
);
//用于构建FCustomDepthContext结构体创建对应的贴图资源。主要包含了InputDepth、InputStencilSRV、DepthTarget、StencilTarget以及bComputeExport是否使用ComputeShader输出
Nanite::FCustomDepthContext CustomDepthContext = Nanite::InitCustomDepthStencilContext(
GraphBuilder,
CustomDepthTextures,
bWriteCustomStencil);
Nanite::FConfiguration CullingConfig = { 0 };//Nanite剔除设置用的较多的是bUpdateStreaming、bPrimaryContext、bTwoPassOcclusion设置为true。
CullingConfig.bUpdateStreaming = true;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
FViewInfo& View = Views[ViewIndex];
if (!View.ShouldRenderView() || NaniteDrawLists[ViewIndex].Num() == 0)
{
continue;
}
//创建Nanite渲染器
auto NaniteRenderer = Nanite::IRenderer::Create(
GraphBuilder,
*Scene,
View,
GetSceneUniforms(),
SharedContext,
RasterContext,
CullingConfig,
View.ViewRect,
/* PrevHZB = */ nullptr
);
NaniteRenderer->DrawGeometry(
Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass],
PrimaryNaniteRasterResults[ViewIndex].VisibilityResults,
*Nanite::FPackedViewArray::Create(GraphBuilder, PrimaryNaniteViews[ViewIndex]),
NaniteDrawLists[ViewIndex]
);
Nanite::FRasterResults RasterResults;
NaniteRenderer->ExtractResults( RasterResults );
// Emit depth
Nanite::EmitCustomDepthStencilTargets(
GraphBuilder,
*Scene,
View,
RasterResults.PageConstants,
RasterResults.VisibleClustersSWHW,
RasterResults.ViewsBuffer,
RasterContext.VisBuffer64,
CustomDepthContext
);
}
Nanite::FinalizeCustomDepthStencil(GraphBuilder, CustomDepthContext, CustomDepthTextures);
}
...
}
```
- Nanite::InitRasterContext()初始化Nanite::FRasterContext RasterContext。
- Nanite::InitCustomDepthStencilContext()位于NaniteMaterials.cpp用于构建FCustomDepthContext结构体创建对应的贴图资源。主要包含了InputDepth、InputStencilSRV、DepthTarget、StencilTarget以及bComputeExport是否使用ComputeShader输出
- auto NaniteRenderer = Nanite::IRenderer::Create()创建Nanite渲染器
- NaniteRenderer->DrawGeometry()Nanite物体绘制
- NaniteRenderer->ExtractResults( RasterResults ):提取渲染结果。
- ***Nanite::EmitCustomDepthStencilTargets()***使用提取到的Nanite::FRasterResults RasterResults输出CustomDepth。结果存储在 FCustomDepthContext& CustomDepthContext中。
- FDepthExportCS / FEmitCustomDepthStencilPS
- Nanite::FinalizeCustomDepthStencil()将CustomDepthContext中的结果输出到CustomDepthTextures上。
## BasePass Nanite
1. Render()中向RenderBasePass()传入const TArrayView<Nanite::FRasterResults>& NaniteRasterResults。
2. 调用Lambda RenderNaniteBasePass()本质上是调用Nanite::DrawBasePass()进行渲染。

View File

@ -117,6 +117,8 @@ rating: ⭐⭐⭐
11. [ ] 阴影控制
1. [ ] 控制深度偏移
2. [ ] CustomDepth制作头发阴影偏移效果哦 https://zhuanlan.zhihu.com/p/689578355
3. [ ] ContactShadow接触阴影实现衣服细节的DetailShadow
4. [ ] 半程阴影
12. [ ] ToonTranslucent
1. [ ] 通过模仿SingleLayerWater实现折射效果 https://zhuanlan.zhihu.com/p/657928532
2. [ ] 眉眼细节 [[幻塔-眉眼细节.png]]

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7cdcc698ed2dc5057ad9897c5d149371593463ff83195697f4b1c5ac51e6566
size 797256

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b725b29d30f2f059ce6da4271f4bc0da6db0676807d8d666861197ae243f8590
size 590235

View File

@ -5,861 +5,14 @@ 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**
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;
}
```
## 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. 调用FSceneRenderer::RenderShadowProjections()渲染ScreenShadowMask。
FSceneRenderer::RenderShadowProjections()
1. 初始化UniformStruct变量。
2. 遍历传入的FProjectedShadowInfo数组并调用**ProjectedShadowInfo->RenderProjection()** 将所有灯光的阴影Mask绘制到一张ScreenShadowMask上。
1. 在TShadowProjectionPS中通过SetParameters() => ProjectionParameters.Set()来设置ShadowDepthTextureValue。`ShadowDepthTextureValue = ShadowInfo->RenderTargets.DepthTarget->GetRHI();`
### Shader
ShadowProjectionPixelShader.usf
- ShadowProjectionCommon.ush
- ShadowDepthTexture
- ShadowDepthCubeTexture
主要步骤:
1. 将SceneDepthScreenSpace => (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) 节约性能。
## 总结
1. RenderDepth()渲染完深度贴图之后经过GatherAndSortLights()、ComputeLightGrid()获取到经过剪裁的FSortedLightSetSceneInfo &SortedLightSet之后传递给RenderLights()。
2. BindShadowProjectionShaders() => BindShaderShaders() =>
3. TShadowProjectionPS => ProjectionParameters.Bind(Initializer); => ShadowDepthTexture.Bind(ParameterMap,TEXT("ShadowDepthTexture"));
# 前言
阴影渲染笔记:[[Shadow]]
实现功能:
1. [ ] 控制深度偏移
2. [ ] CustomDepth制作头发阴影偏移效果哦 https://zhuanlan.zhihu.com/p/689578355
3. [ ] ContactShadow接触阴影实现衣服细节的DetailShadow
4. [ ] 半程阴影
# 半程阴影
由晨风&Neverwind提出
- 【[UFSH2024]用虚幻引擎5为《幻塔》定制高品质动画流程风格化渲染管线 | 晨风 Neverwind 完美世界游戏】 【精准空降到 07:27】 https://www.bilibili.com/video/BV1rW2LYvEox/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=447
@ -914,23 +67,22 @@ if(Toon材质,且没有半程阴影Flag的阴影
PS.很有可能在FProjectedShadowInfo::RenderProjection()阶段进行判断以此保证合成正确的**ScreenShadowMask**。
视频中说:
1. 在Setup阶段打入一个Flag也就是向FProjectedShadowInfo添加标识。
2. ***屏蔽掉角色身上没有半程向量的阴影。这样可以保持角色身上阴影比较干净。***
3. ***屏蔽掉非角色(场景)的半程阴影。***
PS. 或者考虑使用GBuffer的ShadingModelID以及ToonDataAsset查表。
# 实现方法
```c++
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
bool bEnableToonMeshDrawOutline = MaterialRenderProxy->GetToonOutlineDataAssetRT()->Settings.bEnableToonMeshDrawOutline;
角色身上非半程阴影屏蔽前
![[角色非半程阴影屏蔽前.png|300]]
```
FProjectedShadowInfo->Scene
FPrimitiveSceneProxy
## 深度偏移
1. FProjectedShadowInfo添加变量。
角色身上非半程阴影屏蔽后
![[角色非半程阴影屏蔽后.png|300]]
FSceneRenderer::RenderShadowDepthMaps() => RenderShadowDepthMapAtlases() => ProjectedShadowInfo->RenderDepth()
场景上半程阴影屏蔽后
![[场景半程阴影屏蔽后.png|300]]
如果不屏蔽就会有一个跟随视角移动的阴影
## DirectionOffsetToViewShadow
1. 在FProjectedShadowInfo添加变量。
FSceneRenderer::CreateDynamicShadows() => SetupInteractionShadows()在CreatePerObjectProjectedShadow()添加相关逻辑
![[SDF阴影与额发阴影.png]]
![[角色阴影最终合成.png]]
CreatePerObjectProjectedShadow() => ProjectedPreShadowInfo->SetupPerObjectProjection