179 lines
8.7 KiB
Markdown
Raw Normal View History

2024-12-08 12:40:30 +08:00
---
title: Untitled
date: 2024-12-08 12:18:54
excerpt:
tags:
rating: ⭐
---
2024-12-17 17:06:38 +08:00
# 前言
阴影渲染笔记:[[Shadow]]
实现功能:
1. [ ] 控制深度偏移
2. [ ] CustomDepth制作头发阴影偏移效果哦 https://zhuanlan.zhihu.com/p/689578355
3. [ ] ContactShadow接触阴影实现衣服细节的DetailShadow
4. [ ] 半程阴影
2024-12-13 16:22:58 +08:00
# 半程阴影
由晨风&Neverwind提出
- 【[UFSH2024]用虚幻引擎5为《幻塔》定制高品质动画流程风格化渲染管线 | 晨风 Neverwind 完美世界游戏】 【精准空降到 07:27】 https://www.bilibili.com/video/BV1rW2LYvEox/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=447
**阴影Setup阶段**:
```c++
if 启用半程阴影:
{
额外进行一次CreatePerObjectProjectedShadow()
{
2024-12-16 10:18:10 +08:00
处理阴影光照Matrix //猜测:
//在ProjectedShadowInfo->SetupPerObjectProjection()中调整FProjectedShadowInfo.TranslatedWorldToView。
//在LightSceneInfo->Proxy->GetPerObjectProjectedShadowInitializer(Bounds, ShadowInitializer)之后修改WorldTolight。
向阴影信息写入IsHalfViewShadowFlag//猜测在FProjectedShadowInfo中添加判断Flag并且写入。
用新的光源方向画Atlas //猜测给带有对应Flog的DirectionLigh多创建一个对应的Atlas
2024-12-13 16:22:58 +08:00
}
}
```
2024-12-16 10:18:10 +08:00
截图代码(半程阴影修改LightDirection逻辑)
2024-12-13 16:22:58 +08:00
```c++
if(PrimitiveSceneinfo->Proxy->IsToonDisableSelfShadow())
{
...
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
const FMatrix& ViewMatrix = View.ShadowViewMatrices.GetViewMatrix()
2025-01-08 22:09:25 +08:00
FVector LightDirection = Lightsceneinfo->Proxy->GetDirection();
const FVector CameraDirection = ViewMatrix.GetColumn(2).GetSafeNormal()
2024-12-13 16:22:58 +08:00
float LightViewBlendFactor = PrimitiveSceneinfo->Proxy->GetToonHalfVienShadowFactor();
Fvector HalfViewLightDir = (LightDirection + ( 1 - LightViewBlendFactor) + CameraDirection * LightViewBlendFactor).GetSafeNormal();
2025-01-08 22:09:25 +08:00
FMatrix FinalCombineMatrix = FInverseRotationMatrix(HalfViewLightDir.Rotation())
ShadowInitializer.WorldToLight = FinalCombineMatrix;
2024-12-13 16:22:58 +08:00
}
...
}
2024-12-16 10:18:10 +08:00
```
2025-01-08 22:09:25 +08:00
相关代码位于`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;
}
```
2024-12-16 10:18:10 +08:00
PS.很有可能需要创建2个Atlas。Atlas的创建位于***FSceneRenderer::AllocateCachedShadowDepthTargets()***。数据存储在***SortedShadowsForShadowDepthPass.ShadowMapAtlases***中。大致由FSceneRenderer::FinishInitDynamicShadows()调用。
**阴影Projection阶段**:
//此阶段需要屏蔽角色投射到自己的非半程阴影
//和角色投射到场景中会跟随视角移动的阴影
```c++
2024-12-16 22:25:20 +08:00
if(Toon材质,且没有半程阴影Flag的阴影
2024-12-16 10:18:10 +08:00
&&非Toon材质但有半程阴影Flag的阴影)
{
屏蔽此阴影
}
```
2024-12-16 22:25:20 +08:00
2024-12-19 12:49:37 +08:00
PS.很有可能在FProjectedShadowInfo::RenderProjection()阶段进行判断以此保证合成正确的**ScreenShadowMask**。
# 实现方法
2024-12-19 16:34:02 +08:00
```c++
2024-12-19 12:49:37 +08:00
const FMaterialRenderProxy* MaterialRenderProxy = MeshBatch.MaterialRenderProxy;
bool bEnableToonMeshDrawOutline = MaterialRenderProxy->GetToonOutlineDataAssetRT()->Settings.bEnableToonMeshDrawOutline;
2024-12-19 16:34:02 +08:00
```
FProjectedShadowInfo->Scene
FPrimitiveSceneProxy
2024-12-19 12:49:37 +08:00
## 深度偏移
2025-01-20 16:27:47 +08:00
### ~~方法一~~
2024-12-19 14:10:21 +08:00
1. FProjectedShadowInfo添加变量。
FSceneRenderer::RenderShadowDepthMaps() => RenderShadowDepthMapAtlases() => ProjectedShadowInfo->RenderDepth()
2024-12-19 12:49:37 +08:00
2025-01-10 17:27:43 +08:00
已放弃FProjectedShadowInfo无法判断MeshSection。
2025-01-20 16:27:47 +08:00
### 方法二(最终实现方法)
2025-01-10 17:27:43 +08:00
在材质中使用ShadowPassSwitch再对ViewSpace的Z轴方向使用DirectionalLightVector比较可以只对方向光进行偏移进行WPO偏移实现。
其优点就是可以用贴图来控制偏移过渡。
2024-12-19 12:49:37 +08:00
## DirectionOffsetToViewShadow
2025-01-20 16:27:47 +08:00
### 最终实现方法
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获取并且计算。代码如下
2025-01-12 22:09:34 +08:00
2025-01-20 16:27:47 +08:00
```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;
...
}
}
```