199 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

---
title: Untitled
date: 2024-12-08 12:18:54
excerpt:
tags:
rating: ⭐
---
# 前言
阴影渲染笔记:[[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
**阴影Setup阶段**:
```c++
if 启用半程阴影:
{
额外进行一次CreatePerObjectProjectedShadow()
{
处理阴影光照Matrix //猜测:
//在ProjectedShadowInfo->SetupPerObjectProjection()中调整FProjectedShadowInfo.TranslatedWorldToView。
//在LightSceneInfo->Proxy->GetPerObjectProjectedShadowInitializer(Bounds, ShadowInitializer)之后修改WorldTolight。
向阴影信息写入IsHalfViewShadowFlag//猜测在FProjectedShadowInfo中添加判断Flag并且写入。
用新的光源方向画Atlas //猜测给带有对应Flog的DirectionLigh多创建一个对应的Atlas
}
}
```
截图代码(半程阴影修改LightDirection逻辑)
```c++
if(PrimitiveSceneinfo->Proxy->IsToonDisableSelfShadow())
{
...
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const FMatrix& ViewMatrix = View.ShadowViewMatrices.GetViewMatrix()
FVector LightDirection = Lightsceneinfo->Proxy->GetDirection();
const FVector CameraDirection = ViewMatrix.GetColumn(2).GetSafeNormal()
float LightViewBlendFactor = PrimitiveSceneinfo->Proxy->GetToonHalfVienShadowFactor();
Fvector HalfViewLightDir = (LightDirection + ( 1 - LightViewBlendFactor) + CameraDirection * LightViewBlendFactor).GetSafeNormal();
FMatrix FinalCombineMatrix = FInverseRotationMatrix(HalfViewLightDir.Rotation())
ShadowInitializer.WorldToLight = FinalCombineMatrix;
}
...
}
```
相关代码位于`FDirectionalLightSceneProxy::GetPerObjectProjectedShadowInitializer()`在在FSceneRenderer::CreatePerObjectProjectedShadow()被调用。
```c++
virtual bool GetPerObjectProjectedShadowInitializer(const FBoxSphereBounds& SubjectBounds,FPerObjectProjectedShadowInitializer& OutInitializer) const override
{
OutInitializer.PreShadowTranslation = -SubjectBounds.Origin;
OutInitializer.WorldToLight = FInverseRotationMatrix(FVector(WorldToLight.M[0][0],WorldToLight.M[1][0],WorldToLight.M[2][0]).GetSafeNormal().Rotation());
OutInitializer.Scales = FVector2D(1.0f / SubjectBounds.SphereRadius,1.0f / SubjectBounds.SphereRadius);
OutInitializer.SubjectBounds = FBoxSphereBounds(FVector::ZeroVector,SubjectBounds.BoxExtent,SubjectBounds.SphereRadius);
OutInitializer.WAxis = FVector4(0,0,0,1);
OutInitializer.MinLightW = -UE_OLD_HALF_WORLD_MAX;
// Reduce casting distance on a directional light
// This is necessary to improve floating point precision in several places, especially when deriving frustum verts from InvReceiverMatrix
// This takes the object size into account to ensure that large objects get an extended distance
OutInitializer.MaxDistanceToCastInLightW = FMath::Clamp(SubjectBounds.SphereRadius * CVarPerObjectCastDistanceRadiusScale.GetValueOnRenderThread(), CVarPerObjectCastDistanceMin.GetValueOnRenderThread(), (float)WORLD_MAX);
return true;
}
```
PS.很有可能需要创建2个Atlas。Atlas的创建位于***FSceneRenderer::AllocateCachedShadowDepthTargets()***。数据存储在***SortedShadowsForShadowDepthPass.ShadowMapAtlases***中。大致由FSceneRenderer::FinishInitDynamicShadows()调用。
**阴影Projection阶段**:
//此阶段需要屏蔽角色投射到自己的非半程阴影
//和角色投射到场景中会跟随视角移动的阴影
```c++
if(Toon材质,且没有半程阴影Flag的阴影
&&非Toon材质但有半程阴影Flag的阴影)
{
屏蔽此阴影
}
```
PS.很有可能在FProjectedShadowInfo::RenderProjection()阶段进行判断以此保证合成正确的**ScreenShadowMask**。
# 实现方法
```c++
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
bool bEnableToonMeshDrawOutline = MaterialRenderProxy->GetToonOutlineDataAssetRT()->Settings.bEnableToonMeshDrawOutline;
```
FProjectedShadowInfo->Scene
FPrimitiveSceneProxy
## 深度偏移
### ~~方法一~~
1. FProjectedShadowInfo添加变量。
FSceneRenderer::RenderShadowDepthMaps() => RenderShadowDepthMapAtlases() => ProjectedShadowInfo->RenderDepth()
已放弃FProjectedShadowInfo无法判断MeshSection。
### 方法二(最终实现方法)
在材质中使用ShadowPassSwitch再对ViewSpace的Z轴方向使用DirectionalLightVector比较可以只对方向光进行偏移进行WPO偏移实现。
其优点就是可以用贴图来控制偏移过渡。
## DirectionOffsetToViewShadow
### 最终实现方法
1. 在**FProjectionShadowInfo**中添加**bDirectionOffsetToViewShadow**标记以及对应的判断函数IsDirectionOffsetToViewShadow()来判断是否是DirectionOffsetToViewShadow。
1. 在**FSceneRenderer::CreatePerObjectProjectedShadow()** 中再次调用SetupPerObjectProjection()逻辑创建**DirectionOffsetToViewShadow**时将FProjectedShadowInfo的***bDirectionOffsetToViewShadow设置成true***。
2. 在PrimitiveSceneProxy.h 中添加DirectionOffsetToViewShadowAlpha变量作为偏移Alpha同事添加函数UseDirectionOffsetToViewShadow()来判断是否开启这个功能。
1. 在**FSceneRenderer::CreatePerObjectProjectedShadow()** 中取得PrimitiveSceneProxy中的DirectionOffsetToViewShadowAlpha最后计算向量来设置***ShadowInitializer.WorldToLight***。大致为[[#DirectionOffsetToViewShadow Direction Code]]
3. 在ToonDataAsset中添加RecivedViewOffsetShadow作为是否接收DirectionOffsetToViewShadow的依据。
1. 将数据渲染到ToonDataAsset Texture中。
2. 最终在ShadowProjectionPixelShader.usf获取并且计算。代码如下
```c++
//BlueRose Modify
#if SHADING_PATH_DEFERRED && !FORWARD_SHADING && !SUBPIXEL_SHADOW && !STRATA_ENABLED /*&& !USE_TRANSMISSION*/
FGBufferData GBufferData_Toon = GetGBufferData(ScreenUV);
const uint ToonDataAssetID = GetToonDataAssetIDFromGBuffer(GBufferData_Toon);
float RecivedViewOffsetShadow = GetRecivedViewOffsetShadow(ToonDataAssetID);
if (IsDirectionOffsetToViewShadow > 0)//ViewShadow ProjectionShadowInfo
{
/*if (RecivedViewOffsetShadow > 0)//ViewOffsetShadow
{
PerObjectDistanceFadeFraction *= 1.0;
}*/
if (RecivedViewOffsetShadow == 0)
{
PerObjectDistanceFadeFraction *= 0.0;
}
}else//Normal ProjectionShadowInfo
{
if (RecivedViewOffsetShadow > 0)
{
PerObjectDistanceFadeFraction *= 0.0;
}
}
#endif
//BlueRose Modify End
float FadedShadow = lerp(1.0f, Shadow, ShadowFadeFraction * PerObjectDistanceFadeFraction);
#if FORWARD_SHADING || SHADING_PATH_MOBILE
float LightInfluenceMask = GetLightInfluenceMask(TranslateWorldPosition);
// Constrain shadowing from this light to pixels inside the light's influence, since other non-overlapping lights are packed into the same channel
FadedShadow = lerp(1, FadedShadow, LightInfluenceMask);
// Write into all channels, the write mask will constrain to the correct one
OutColor = EncodeLightAttenuation(FadedShadow);
#else
float FadedSSSShadow = lerp(1.0f, SSSTransmission, ShadowFadeFraction * PerObjectDistanceFadeFraction);
// the channel assignment is documented in ShadowRendering.cpp (look for Light Attenuation channel assignment)
OutColor = EncodeLightAttenuation(half4(FadedShadow, FadedSSSShadow, FadedShadow, FadedSSSShadow));
#endif
```
#### DirectionOffsetToViewShadow Direction Code
```c++
...
bool bToonDirectionOffsetToViewShadow = ToonDirectionOffsetToViewShadowCVar->GetValueOnRenderThread();
float LightViewBlendFactor = PrimitiveSceneInfo->Proxy->DirectionOffsetToViewShadowAlpha;
if (bToonDirectionOffsetToViewShadow && LightSceneInfo->Proxy->GetLightType() == LightType_Directional && LightViewBlendFactor > 0.0f)
{
//计算半程向量针对每个View都会生成一个FProjectedShadowInfo之后在RenderShadowProjection()中判断?
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const FMatrix& ViewMatrix = View.ShadowViewMatrices.GetViewMatrix();
FVector LightDirection = LightSceneInfo->Proxy->GetDirection();
const FVector CameraDirection = ViewMatrix.GetColumn(2).GetSafeNormal();
FVector HalfViewLightDir = (LightDirection * ( 1 - LightViewBlendFactor) + CameraDirection * LightViewBlendFactor).GetSafeNormal();
FMatrix FinalCombineMatrix = FInverseRotationMatrix(HalfViewLightDir.Rotation());
ShadowInitializer.WorldToLight = FinalCombineMatrix;
...
}
}
```
# NoSelfShadow
https://zhuanlan.zhihu.com/p/10073818586?utm_psn=1877051516055076866
UPrimitiveComponent::bSelfShadowOnly
=>
FPrimitiveSceneProxy::bSelfShadowOnly => **CastsSelfShadowOnly()**
=>
FProjectedShadowInfo::bSelfShadowOnly
- FProjectedShadowInfo::SetupMeshDrawCommandsForProjectionStenciling()设置Stencil为1。
- 在`FProjectedShadowInfo::GatherDynamicMeshElements()`被调用FSceneRenderer::GatherShadowDynamicMeshElements()。
- FProjectedShadowInfo::SetupProjectionStencilMask设置Stencil为7 pre-shadow/per-object static shadow
- 在`FProjectedShadowInfo::RenderProjectionInternal()`被调用。
FProjectedShadowInfo::RenderProjectionInternal()