Merge remote-tracking branch 'origin/master'
# Conflicts: # 03-UnrealEngine/卡通渲染相关资料/渲染功能/阴影控制/ToonShadow.md
This commit is contained in:
commit
7473ba5c5d
@ -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}}},"FShadowProjectionNoTransformVS(ShadowProjectionNoTransformVS)、FShadowVolumeBoundProjectionVS(ShadowVolumeBoundProjectionVS)":{"FShadowProjectionNoTransformVS(ShadowProjectionNoTransformVS)、FShadowVolumeBoundProjectionVS(ShadowVolumeBoundProjectionVS)":{"currentFile":{"count":1,"lastUpdated":1734345863513}}},"DecodeLightAttenuation":{"DecodeLightAttenuation":{"currentFile":{"count":1,"lastUpdated":1734406452310}}},"clamped":{"clamped":{"currentFile":{"count":1,"lastUpdated":1734590559877}}}}
|
@ -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,这个参数没办法插值。
|
6
02-Note/ASoul/导播台笔记/大世界换地图功能.md
Normal file
6
02-Note/ASoul/导播台笔记/大世界换地图功能.md
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
ALiveDirectorGameMode::GetSeamlessTravelActorList
|
||||
|
||||
https://www.uejoy.com/archives/1130
|
||||
|
||||
使用了UE5的RelicationGraph,UDirectorReplicationGraphNode_AlwaysRelevant_ForConnection::GatherActorListsForConnection()
|
76
02-Note/ASoul/导播台笔记/搜索功能.md
Normal file
76
02-Note/ASoul/导播台笔记/搜索功能.md
Normal 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();
|
||||
}
|
||||
```
|
@ -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阴影。
|
||||
- z:Light function + per-object shadows
|
||||
- w:per-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通道,其他的整个场景的阴影。
|
1333
03-UnrealEngine/Rendering/RenderingPipeline/Lighting/Shadow.md
Normal file
1333
03-UnrealEngine/Rendering/RenderingPipeline/Lighting/Shadow.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -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()进行渲染。
|
@ -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]]
|
||||
|
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e7cdcc698ed2dc5057ad9897c5d149371593463ff83195697f4b1c5ac51e6566
|
||||
size 797256
|
3
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/鸣潮_女主.png
Normal file
3
03-UnrealEngine/卡通渲染相关资料/渲染功能/其他参考/其他游戏与软件参考/鸣潮_女主.png
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b725b29d30f2f059ce6da4271f4bc0da6db0676807d8d666861197ae243f8590
|
||||
size 590235
|
@ -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. 将SceneDepth(ScreenSpace) => (ShadowSpace)
|
||||
2. 根据过滤方式调用:
|
||||
1. 不过滤:`Shadow = LightSpacePixelDepthForOpaque < Texture2DSampleLevel(ShadowDepthTexture, ShadowDepthTextureSampler, ShadowPosition.xy, 0).r;`
|
||||
2. PCSS:初始化FPCSSSamplerSettings之后调用DirectionalPCSS()
|
||||
3. PCF:初始化FPCFSamplerSettings之后调用ManualPCF()
|
||||
3. 调整阴影数值。钳制、模糊、过滤。
|
||||
4. `OutColor = EncodeLightAttenuation(half4(FadedShadow, FadedSSSShadow, FadedShadow, FadedSSSShadow));`LinearSpace => sRGB 本质为sqrt(input),比pow(x, 1/2.2) 节约性能。
|
||||
|
||||
## 总结
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user