763 lines
30 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: 2025-02-11 11:30:34
excerpt:
tags:
rating: ⭐
---
# FSortedLightSetSceneInfo
有序的光源集合相关定义:
```c++
/** Data for a simple dynamic light. */
class FSimpleLightEntry
{
public:
FVector3f Color;
float Radius;
float Exponent;
float InverseExposureBlend = 0.0f;
float VolumetricScatteringIntensity;
bool bAffectTranslucency;
};
struct FSortedLightSceneInfo
{
union
{
struct
{
// Note: the order of these members controls the light sort order!
// Currently bHandledByLumen is the MSB and LightType is LSB /** The type of light. */ uint32 LightType : LightType_NumBits;
/** Whether the light has a texture profile. */
uint32 bTextureProfile : 1;
/** Whether the light uses a light function. */
uint32 bLightFunction : 1;
/** Whether the light uses lighting channels. */
uint32 bUsesLightingChannels : 1;
/** Whether the light casts shadows. */
uint32 bShadowed : 1;
/** Whether the light is NOT a simple light - they always support tiled/clustered but may want to be selected separately. */
uint32 bIsNotSimpleLight : 1;
/* We want to sort the lights that write into the packed shadow mask (when enabled) to the front of the list so we don't waste slots in the packed shadow mask. */
uint32 bDoesNotWriteIntoPackedShadowMask : 1;
/**
* True if the light doesn't support clustered deferred, logic is inverted so that lights that DO support clustered deferred will sort first in list
* Super-set of lights supporting tiled, so the tiled lights will end up in the first part of this range.
*/
uint32 bClusteredDeferredNotSupported : 1;
/** Whether the light should be handled by Lumen's Final Gather, these will be sorted to the end so they can be skipped */
uint32 bHandledByLumen : 1;
} Fields;
/** Sort key bits packed into an integer. */
int32 Packed;
} SortKey;
const FLightSceneInfo* LightSceneInfo;
int32 SimpleLightIndex;
/** Initialization constructor. */
explicit FSortedLightSceneInfo(const FLightSceneInfo* InLightSceneInfo)
: LightSceneInfo(InLightSceneInfo),
SimpleLightIndex(-1)
{
SortKey.Packed = 0;
SortKey.Fields.bIsNotSimpleLight = 1;
}
explicit FSortedLightSceneInfo(int32 InSimpleLightIndex)
: LightSceneInfo(nullptr),
SimpleLightIndex(InSimpleLightIndex)
{
SortKey.Packed = 0;
SortKey.Fields.bIsNotSimpleLight = 0;
}};
/**
* Stores info about sorted lights and ranges.
* The sort-key in FSortedLightSceneInfo gives rise to the following order:
* [SimpleLights,Clustered,UnbatchedLights,LumenLights] * Note that some shadowed lights can be included in the clustered pass when virtual shadow maps and one pass projection are used. */struct FSortedLightSetSceneInfo
{
int32 SimpleLightsEnd;
int32 ClusteredSupportedEnd;
/** First light with shadow map or */
int32 UnbatchedLightStart;
int32 LumenLightStart;
FSimpleLightArray SimpleLights;
TArray<FSortedLightSceneInfo, SceneRenderingAllocator> SortedLights;
};
```
## 开始获取有序光源集合
UE的光源分配由`FDeferredShadingSceneRenderer::Render`内的`bComputeLightGrid`变量决定的bComputeLightGrid的赋值逻辑如下
```c++
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList) {
...
bool bComputeLightGrid = false;
if (RendererOutput == ERendererOutput::FinalSceneColor)
{
if (bUseVirtualTexturing)
{
// Note, should happen after the GPU-Scene update to ensure rendering to runtime virtual textures is using the correctly updated scene
FVirtualTextureSystem::Get().EndUpdate(GraphBuilder, MoveTemp(VirtualTextureUpdater), FeatureLevel);
}
#if RHI_RAYTRACING
GatherRayTracingWorldInstancesForView(GraphBuilder, ReferenceView, RayTracingScene, InitViewTaskDatas.RayTracingRelevantPrimitives);
#endif // RHI_RAYTRACING
bool bAnyLumenEnabled = false;
{
if (bUseGBuffer)
{
bComputeLightGrid = bRenderDeferredLighting;
}
else
{
bComputeLightGrid = ViewFamily.EngineShowFlags.Lighting;
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
bAnyLumenEnabled = bAnyLumenEnabled
|| GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen
|| GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen;
}
bComputeLightGrid |= (
ShouldRenderVolumetricFog() ||
VolumetricCloudWantsToSampleLocalLights(Scene, ViewFamily.EngineShowFlags) ||
ViewFamily.ViewMode != VMI_Lit ||
bAnyLumenEnabled ||
VirtualShadowMapArray.IsEnabled() ||
ShouldVisualizeLightGrid());
}
}
...
}
```
获取有序的光源集合
```c++
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList) {
...
// 有序的光源集合.
FSortedLightSetSceneInfo& SortedLightSet = *GraphBuilder.AllocObject<FSortedLightSetSceneInfo>();
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, SortLights);
RDG_GPU_STAT_SCOPE(GraphBuilder, SortLights);
ComputeLightGridOutput = GatherLightsAndComputeLightGrid(GraphBuilder, bComputeLightGrid, SortedLightSet);
}
...
}
```
PS. 简单光源都可以被分块或分簇渲染,但对于非简单光源,只有满足以下条件的光源才可被分块或分簇渲染:
- 没有使用光源的附加特性TextureProfile、LightFunction、LightingChannel
- 没有开启阴影。
- 非平行光或矩形光。
另外,是否支持分块渲染,还需要光源场景代理的`IsTiledDeferredLightingSupported`返回true长度为0的点光源才支持分块渲染。
## GatherLightsAndComputeLightGrid
```c++
FComputeLightGridOutput FDeferredShadingSceneRenderer::GatherLightsAndComputeLightGrid(FRDGBuilder& GraphBuilder, bool bNeedLightGrid, FSortedLightSetSceneInfo& SortedLightSet)
{
SCOPED_NAMED_EVENT(GatherLightsAndComputeLightGrid, FColor::Emerald);
FComputeLightGridOutput Result = {};
bool bShadowedLightsInClustered = ShouldUseClusteredDeferredShading()
&& CVarVirtualShadowOnePassProjection.GetValueOnRenderThread()
&& VirtualShadowMapArray.IsEnabled();
const bool bUseLumenDirectLighting = ShouldRenderLumenDirectLighting(Scene, Views[0]);
GatherAndSortLights(SortedLightSet, bShadowedLightsInClustered, bUseLumenDirectLighting);
if (!bNeedLightGrid)
{
SetDummyForwardLightUniformBufferOnViews(GraphBuilder, ShaderPlatform, Views);
return Result;
}
bool bAnyViewUsesForwardLighting = false;
bool bAnyViewUsesLumen = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
bAnyViewUsesForwardLighting |= View.bTranslucentSurfaceLighting || ShouldRenderVolumetricFog() || View.bHasSingleLayerWaterMaterial || VolumetricCloudWantsToSampleLocalLights(Scene, ViewFamily.EngineShowFlags) || ShouldVisualizeLightGrid();
bAnyViewUsesLumen |= GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen || GetViewPipelineState(View).ReflectionsMethod == EReflectionsMethod::Lumen;
}
const bool bCullLightsToGrid = GLightCullingQuality
&& (IsForwardShadingEnabled(ShaderPlatform) || bAnyViewUsesForwardLighting || IsRayTracingEnabled() || ShouldUseClusteredDeferredShading() ||
bAnyViewUsesLumen || ViewFamily.EngineShowFlags.VisualizeMeshDistanceFields || VirtualShadowMapArray.IsEnabled());
// Store this flag if lights are injected in the grids, check with 'AreLightsInLightGrid()'
bAreLightsInLightGrid = bCullLightsToGrid;
Result = ComputeLightGrid(GraphBuilder, bCullLightsToGrid, SortedLightSet);
return Result;
}
```
- GatherAndSortLights收集与排序当前场景中所有的可见光源当前View
- ComputeLightGrid是在锥体空间frustum space裁剪局部光源和反射探针到3D格子中构建每个视图相关的光源列表和格子。
# RenderLights() -> RenderLight()
## InternalRenderLight()
## DeferredLightVertexShaders
```c++
// 输入参数.
struct FInputParams
{
float2 PixelPos;
float4 ScreenPosition;
float2 ScreenUV;
float3 ScreenVector;
};
// 派生参数.
struct FDerivedParams
{
float3 CameraVector;
float3 WorldPosition;
};
// 获取派生参数.
FDerivedParams GetDerivedParams(in FInputParams Input, in float SceneDepth)
{
FDerivedParams Out;
#if LIGHT_SOURCE_SHAPE > 0
// With a perspective projection, the clip space position is NDC * Clip.w
// With an orthographic projection, clip space is the same as NDC
float2 ClipPosition = Input.ScreenPosition.xy / Input.ScreenPosition.w * (View.ViewToClip[3][3] < 1.0f ? SceneDepth : 1.0f);
Out.WorldPosition = mul(float4(ClipPosition, SceneDepth, 1), View.ScreenToWorld).xyz;
Out.CameraVector = normalize(Out.WorldPosition - View.WorldCameraOrigin);
#else
Out.WorldPosition = Input.ScreenVector * SceneDepth + View.WorldCameraOrigin;
Out.CameraVector = normalize(Input.ScreenVector);
#endif
return Out;
}
Texture2D<uint> LightingChannelsTexture;
uint GetLightingChannelMask(float2 UV)
{
uint2 IntegerUV = UV * View.BufferSizeAndInvSize.xy;
return LightingChannelsTexture.Load(uint3(IntegerUV, 0)).x;
}
float GetExposure()
{
return View.PreExposure;
}
```
向往文章中的SetupLightDataForStandardDeferred()变为InitDeferredLightFromUniforms()。位于LightDataUniform.ush。
```c++
FDeferredLightData InitDeferredLightFromUniforms(uint InLightType)
{
const bool bIsRadial = InLightType != LIGHT_TYPE_DIRECTIONAL;
FDeferredLightData Out;
Out.TranslatedWorldPosition = GetDeferredLightTranslatedWorldPosition();
Out.InvRadius = DeferredLightUniforms.InvRadius;
Out.Color = DeferredLightUniforms.Color;
Out.FalloffExponent = DeferredLightUniforms.FalloffExponent;
Out.Direction = DeferredLightUniforms.Direction;
Out.Tangent = DeferredLightUniforms.Tangent;
Out.SpotAngles = DeferredLightUniforms.SpotAngles;
Out.SourceRadius = DeferredLightUniforms.SourceRadius;
Out.SourceLength = bIsRadial ? DeferredLightUniforms.SourceLength : 0;
Out.SoftSourceRadius = DeferredLightUniforms.SoftSourceRadius;
Out.SpecularScale = DeferredLightUniforms.SpecularScale;
Out.ContactShadowLength = abs(DeferredLightUniforms.ContactShadowLength);
Out.ContactShadowLengthInWS = DeferredLightUniforms.ContactShadowLength < 0.0f;
Out.ContactShadowCastingIntensity = DeferredLightUniforms.ContactShadowCastingIntensity;
Out.ContactShadowNonCastingIntensity = DeferredLightUniforms.ContactShadowNonCastingIntensity;
Out.DistanceFadeMAD = DeferredLightUniforms.DistanceFadeMAD;
Out.ShadowMapChannelMask = DeferredLightUniforms.ShadowMapChannelMask;
Out.ShadowedBits = DeferredLightUniforms.ShadowedBits;
Out.bInverseSquared = bIsRadial && DeferredLightUniforms.FalloffExponent == 0; // Directional lights don't use 'inverse squared attenuation'
Out.bRadialLight = bIsRadial;
Out.bSpotLight = InLightType == LIGHT_TYPE_SPOT;
Out.bRectLight = InLightType == LIGHT_TYPE_RECT;
Out.RectLightData.BarnCosAngle = DeferredLightUniforms.RectLightBarnCosAngle;
Out.RectLightData.BarnLength = DeferredLightUniforms.RectLightBarnLength;
Out.RectLightData.AtlasData.AtlasMaxLevel = DeferredLightUniforms.RectLightAtlasMaxLevel;
Out.RectLightData.AtlasData.AtlasUVOffset = DeferredLightUniforms.RectLightAtlasUVOffset;
Out.RectLightData.AtlasData.AtlasUVScale = DeferredLightUniforms.RectLightAtlasUVScale;
Out.HairTransmittance = InitHairTransmittanceData();
return Out;
}
```
### DeferredLightPixelMain
```c++
void DeferredLightPixelMain(
#if LIGHT_SOURCE_SHAPE > 0
float4 InScreenPosition : TEXCOORD0,
#else
float2 ScreenUV : TEXCOORD0,
float3 ScreenVector : TEXCOORD1,
#endif
float4 SVPos : SV_POSITION,
out float4 OutColor : SV_Target0
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
, out float3 OutOpaqueRoughRefractionSceneColor : SV_Target1
, out float3 OutSubSurfaceSceneColor : SV_Target2
#endif
)
{
const float2 PixelPos = SVPos.xy;
OutColor = 0;
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
OutOpaqueRoughRefractionSceneColor = 0;
OutSubSurfaceSceneColor = 0;
#endif
// Convert input data (directional/local light)
// 计算屏幕UV
FInputParams InputParams = (FInputParams)0;
InputParams.PixelPos = SVPos.xy;
#if LIGHT_SOURCE_SHAPE > 0
InputParams.ScreenPosition = InScreenPosition;
InputParams.ScreenUV = InScreenPosition.xy / InScreenPosition.w * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
InputParams.ScreenVector = 0;
#else
InputParams.ScreenPosition = 0;
InputParams.ScreenUV = ScreenUV;
InputParams.ScreenVector = ScreenVector;
#endif
#if STRATA_ENABLED
FStrataAddressing StrataAddressing = GetStrataPixelDataByteOffset(PixelPos, uint2(View.BufferSizeAndInvSize.xy), Strata.MaxBytesPerPixel);
FStrataPixelHeader StrataPixelHeader = UnpackStrataHeaderIn(Strata.MaterialTextureArray, StrataAddressing, Strata.TopLayerTexture);
BRANCH
if (StrataPixelHeader.BSDFCount > 0 // This test is also enough to exclude sky pixels
#if USE_LIGHTING_CHANNELS
//灯光通道逻辑
&& (GetLightingChannelMask(InputParams.ScreenUV) & DeferredLightUniforms.LightingChannelMask)
#endif
)
{
//通过SceneDepth获取的CameraVector以及当前像素的世界坐标
const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV);
const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth);
//设置获取光源各种信息
FDeferredLightData LightData = InitDeferredLightFromUniforms(CURRENT_LIGHT_TYPE);
UpdateLightDataColor(LightData, InputParams, DerivedParams);//根据当前世界坐标计算LightData.Color *= 大气&云&阴影的衰减值 * IES灯亮度非IES灯数值为1
float3 V =-DerivedParams.CameraVector;
float3 L = LightData.Direction; // Already normalized
float3 ToLight = L;
float LightMask = 1;
if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation(DerivedParams.TranslatedWorldPosition, LightData, ToLight, L);
}
if (LightMask > 0)
{
FShadowTerms ShadowTerms = { StrataGetAO(StrataPixelHeader), 1.0, 1.0, InitHairTransmittanceData() };
float4 LightAttenuation = GetLightAttenuationFromShadow(InputParams, SceneDepth);
float Dither = InterleavedGradientNoise(InputParams.PixelPos, View.StateFrameIndexMod8);
const uint FakeShadingModelID = 0;
const float FakeContactShadowOpacity = 1.0f;
float4 PrecomputedShadowFactors = StrataReadPrecomputedShadowFactors(StrataPixelHeader, PixelPos, SceneTexturesStruct.GBufferETexture);
GetShadowTerms(SceneDepth, PrecomputedShadowFactors, FakeShadingModelID, FakeContactShadowOpacity,
LightData, DerivedParams.TranslatedWorldPosition, L, LightAttenuation, Dither, ShadowTerms);
FStrataDeferredLighting StrataLighting = StrataDeferredLighting(
LightData,
V,
L,
ToLight,
LightMask,
ShadowTerms,
Strata.MaterialTextureArray,
StrataAddressing,
StrataPixelHeader);
OutColor += StrataLighting.SceneColor;
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
OutOpaqueRoughRefractionSceneColor += StrataLighting.OpaqueRoughRefractionSceneColor;
OutSubSurfaceSceneColor += StrataLighting.SubSurfaceSceneColor;
#endif
}
}
#else // STRATA_ENABLED
//取得屏幕空间数据FGbufferData、AO
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(InputParams.ScreenUV);
// Only light pixels marked as using deferred shading
BRANCH if (ScreenSpaceData.GBuffer.ShadingModelID > 0
#if USE_LIGHTING_CHANNELS
&& (GetLightingChannelMask(InputParams.ScreenUV) & DeferredLightUniforms.LightingChannelMask)
#endif
)
{
//通过SceneDepth获取的CameraVector以及当前像素的世界坐标
const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV);
const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth);
//设置获取光源各种信息
FDeferredLightData LightData = InitDeferredLightFromUniforms(CURRENT_LIGHT_TYPE);
UpdateLightDataColor(LightData, InputParams, DerivedParams);//根据当前世界坐标计算LightData.Color *= 大气&云&阴影的衰减值 * IES灯亮度非IES灯数值为1
#if USE_HAIR_COMPLEX_TRANSMITTANCE
//针对ShadingModel Hair同时需要CustomData.a > 0)计算头发散射结果
if (ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_HAIR && ShouldUseHairComplexTransmittance(ScreenSpaceData.GBuffer))
{
LightData.HairTransmittance = EvaluateDualScattering(ScreenSpaceData.GBuffer, DerivedParams.CameraVector, -DeferredLightUniforms.Direction);
}
#endif
//计算当前像素的抖动值
float Dither = InterleavedGradientNoise(InputParams.PixelPos, View.StateFrameIndexMod8);
float SurfaceShadow = 1.0f;
float4 LightAttenuation = GetLightAttenuationFromShadow(InputParams, SceneDepth);//根绝是否开启VSM 分别从VirtualShadowMap 或者 LightAttenuationTexture上一阶段渲染的ShadowProjction 获取灯光衰减值。
float4 Radiance = GetDynamicLighting(DerivedParams.TranslatedWorldPosition, DerivedParams.CameraVector, ScreenSpaceData.GBuffer, ScreenSpaceData.AmbientOcclusion, ScreenSpaceData.GBuffer.ShadingModelID, LightData, LightAttenuation, Dither, uint2(InputParams.PixelPos), SurfaceShadow);
OutColor += Radiance;
}
#endif // STRATA_ENABLED
// RGB:SceneColor Specular and Diffuse
// A:Non Specular SceneColor Luminance
// So we need PreExposure for both color and alpha
OutColor.rgba *= GetExposure();
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
// Idem
OutOpaqueRoughRefractionSceneColor *= GetExposure();
OutSubSurfaceSceneColor *= GetExposure();
#endif
}
#endif
```
### GetDynamicLighting() => GetDynamicLightingSplit()
```c++
FDeferredLightingSplit GetDynamicLightingSplit(
float3 TranslatedWorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID,
FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos,
inout float SurfaceShadow)
{
FLightAccumulator LightAccumulator = AccumulateDynamicLighting(TranslatedWorldPosition, CameraVector, GBuffer, AmbientOcclusion, ShadingModelID, LightData, LightAttenuation, Dither, SVPos, SurfaceShadow);
return LightAccumulator_GetResultSplit(LightAccumulator);
}
```
LightAccumulator_GetResultSplit()针对Subsurface`RetDiffuse.a = In.ScatterableLightLuma;` 或者 `RetDiffuse.a = Luminance(In.ScatterableLight);`
```c++
FDeferredLightingSplit LightAccumulator_GetResultSplit(FLightAccumulator In)
{
float4 RetDiffuse;
float4 RetSpecular;
if (VISUALIZE_LIGHT_CULLING == 1)
{
// a soft gradient from dark red to bright white, can be changed to be different
RetDiffuse = 0.1f * float4(1.0f, 0.25f, 0.075f, 0) * In.EstimatedCost;
RetSpecular = 0.1f * float4(1.0f, 0.25f, 0.075f, 0) * In.EstimatedCost;
}
else
{
RetDiffuse = float4(In.TotalLightDiffuse, 0);
RetSpecular = float4(In.TotalLightSpecular, 0);
//针对Subsurface会额外对RetDiffuse的Alpha设置数值 ScatterableLight的亮度数值
if (SUBSURFACE_CHANNEL_MODE == 1 )
{
if (View.bCheckerboardSubsurfaceProfileRendering == 0)
{
// RGB accumulated RGB HDR color, A: specular luminance for screenspace subsurface scattering
RetDiffuse.a = In.ScatterableLightLuma;
}
}
else if (SUBSURFACE_CHANNEL_MODE == 2)
{
// RGB accumulated RGB HDR color, A: view independent (diffuse) luminance for screenspace subsurface scattering
// 3 add, 1 mul, 2 mad, can be optimized to use 2 less temporary during accumulation and remove the 3 add
RetDiffuse.a = Luminance(In.ScatterableLight);
// todo, need second MRT for SUBSURFACE_CHANNEL_MODE==2
}
}
FDeferredLightingSplit Ret;
Ret.DiffuseLighting = RetDiffuse;
Ret.SpecularLighting = RetSpecular;
return Ret;
}
```
#### AccumulateDynamicLighting
```c++
FLightAccumulator AccumulateDynamicLighting(
float3 TranslatedWorldPosition, half3 CameraVector, FGBufferData GBuffer, half AmbientOcclusion, uint ShadingModelID,
FDeferredLightData LightData, half4 LightAttenuation, float Dither, uint2 SVPos,
inout float SurfaceShadow)
{
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
half3 V = -CameraVector;
half3 N = GBuffer.WorldNormal;
//针对开启CLEAR_COAT_BOTTOM_NORMAL的清漆ShadingModel进行Normal处理
BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT && CLEAR_COAT_BOTTOM_NORMAL)
{
const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 4) - (512.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
N = OctahedronToUnitVector(oct1);
}
float3 L = LightData.Direction; // Already normalized
float3 ToLight = L;
float3 MaskedLightColor = LightData.Color;//灯光颜色
float LightMask = 1;
// 获取辐射光源的衰减值衰减方法根据LightData.bInverseSquared会分别使用新版衰减方法InverseSquared 或者 旧方法。如果是SpotLight与RectLight就乘以SpotLight、RectLight对应的形状衰减数值。
if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation( TranslatedWorldPosition, LightData, ToLight, L );
MaskedLightColor *= LightMask;
}
LightAccumulator.EstimatedCost += 0.3f; // running the PixelShader at all has a cost
BRANCH
if( LightMask > 0 )//如果不是完全死黑就计算阴影部分逻辑
{
FShadowTerms Shadow;
Shadow.SurfaceShadow = AmbientOcclusion;//GBuffer中的AO
Shadow.TransmissionShadow = 1;
Shadow.TransmissionThickness = 1;
Shadow.HairTransmittance.OpaqueVisibility = 1;
const float ContactShadowOpacity = GBuffer.CustomData.a;//TODO:修正ToonStandard对应的逻辑
//
GetShadowTerms(GBuffer.Depth, GBuffer.PrecomputedShadowFactors, GBuffer.ShadingModelID, ContactShadowOpacity,
LightData, TranslatedWorldPosition, L, LightAttenuation, Dither, Shadow);
SurfaceShadow = Shadow.SurfaceShadow;
LightAccumulator.EstimatedCost += 0.3f; // add the cost of getting the shadow terms
#if SHADING_PATH_MOBILE
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
FDirectLighting Lighting = (FDirectLighting)0;
half NoL = max(0, dot(GBuffer.WorldNormal, L));
#if TRANSLUCENCY_NON_DIRECTIONAL
NoL = 1.0f;
#endif
Lighting = EvaluateBxDF(GBuffer, N, V, L, NoL, Shadow);
Lighting.Specular *= LightData.SpecularScale;
LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
#else // SHADING_PATH_MOBILE
BRANCH
if( Shadow.SurfaceShadow + Shadow.TransmissionShadow > 0 )
{
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
#if NON_DIRECTIONAL_DIRECT_LIGHTING
float Lighting;
if( LightData.bRectLight )
{
FRect Rect = GetRect( ToLight, LightData );
Lighting = IntegrateLight( Rect );
}
else
{
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
Lighting = IntegrateLight( Capsule, LightData.bInverseSquared );
}
float3 LightingDiffuse = Diffuse_Lambert( GBuffer.DiffuseColor ) * Lighting;
LightAccumulator_AddSplit(LightAccumulator, LightingDiffuse, 0.0f, 0, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation);
#else
FDirectLighting Lighting;
if (LightData.bRectLight)
{
FRect Rect = GetRect( ToLight, LightData );
const FRectTexture SourceTexture = ConvertToRectTexture(LightData);
#if REFERENCE_QUALITY
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture, SVPos );
#else
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture);
#endif
}
else
{
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
#if REFERENCE_QUALITY
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, SVPos );
#else
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );
#endif
}
Lighting.Specular *= LightData.SpecularScale;
LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator.EstimatedCost += 0.4f; // add the cost of the lighting computations (should sum up to 1 form one light)
#endif
}
#endif // SHADING_PATH_MOBILE
}
return LightAccumulator;
}
```
光源新衰减公式,相关计算位于`GetLocalLightAttenuation()`
$$Falloff = \frac{saturate(1-(distance/lightRadius)^4)^2}{distance^2 + 1}$$
光源旧衰减公式相关函数位于DynamicLightingCommon.ush中的`RadialAttenuation()`
$$Falloff = (1 - saturate(length(WorldLightVector)))^ {FalloffExponent}$$
##### GetShadowTerms()
```c++
void GetShadowTerms(float SceneDepth, half4 PrecomputedShadowFactors, uint ShadingModelID, float ContactShadowOpacity, FDeferredLightData LightData, float3 TranslatedWorldPosition, half3 L, half4 LightAttenuation, float Dither, inout FShadowTerms Shadow)
{
float ContactShadowLength = 0.0f;
const float ContactShadowLengthScreenScale = GetTanHalfFieldOfView().y * SceneDepth;
BRANCH
if (LightData.ShadowedBits)
{
// Remapping the light attenuation buffer (see ShadowRendering.cpp)
// LightAttenuation: Light function + per-object shadows in z, per-object SSS shadowing in w,
// Whole scene directional light shadows in x, whole scene directional light SSS shadows in y
// Get static shadowing from the appropriate GBuffer channel
#if ALLOW_STATIC_LIGHTING
half UsesStaticShadowMap = dot(LightData.ShadowMapChannelMask, half4(1, 1, 1, 1));
half StaticShadowing = lerp(1, dot(PrecomputedShadowFactors, LightData.ShadowMapChannelMask), UsesStaticShadowMap);
#else
half StaticShadowing = 1.0f;
#endif
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);
}
FLATTEN
if (LightData.ShadowedBits > 1 && LightData.ContactShadowLength > 0)
{
ContactShadowLength = LightData.ContactShadowLength * (LightData.ContactShadowLengthInWS ? 1.0f : ContactShadowLengthScreenScale);
}
}
#if SUPPORT_CONTACT_SHADOWS
#if STRATA_ENABLED == 0
if (LightData.ShadowedBits < 2 && (ShadingModelID == SHADINGMODELID_HAIR))
{
ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
}
// World space distance to cover eyelids and eyelashes but not beyond
if (ShadingModelID == SHADINGMODELID_EYE)
{
ContactShadowLength = 0.5;
}
#endif
#if MATERIAL_CONTACT_SHADOWS
ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
#endif
BRANCH
if (ContactShadowLength > 0.0)
{
float StepOffset = Dither - 0.5;
bool bHitCastContactShadow = false;
bool bHairNoShadowLight = ShadingModelID == SHADINGMODELID_HAIR && !LightData.ShadowedBits;
float HitDistance = ShadowRayCast( TranslatedWorldPosition, L, ContactShadowLength, 8, StepOffset, bHairNoShadowLight, bHitCastContactShadow );
if ( HitDistance > 0.0 )
{
float ContactShadowOcclusion = bHitCastContactShadow ? LightData.ContactShadowCastingIntensity : LightData.ContactShadowNonCastingIntensity;
#if STRATA_ENABLED == 0
// Exponential attenuation is not applied on hair/eye/SSS-profile here, as the hit distance (shading-point to blocker) is different from the estimated
// thickness (closest-point-from-light to shading-point), and this creates light leaks. Instead we consider first hit as a blocker (old behavior)
BRANCH
if (ContactShadowOcclusion > 0.0 &&
IsSubsurfaceModel(ShadingModelID) &&
ShadingModelID != SHADINGMODELID_HAIR &&
ShadingModelID != SHADINGMODELID_EYE &&
ShadingModelID != SHADINGMODELID_SUBSURFACE_PROFILE)
{
// Reduce the intensity of the shadow similar to the subsurface approximation used by the shadow maps path
// Note that this is imperfect as we don't really have the "nearest occluder to the light", but this should at least
// ensure that we don't darken-out the subsurface term with the contact shadows
float Density = SubsurfaceDensityFromOpacity(ContactShadowOpacity);
ContactShadowOcclusion *= 1.0 - saturate( exp( -Density * HitDistance ) );
}
#endif
float ContactShadow = 1.0 - ContactShadowOcclusion;
Shadow.SurfaceShadow *= ContactShadow;
Shadow.TransmissionShadow *= ContactShadow;
}
}
#endif
Shadow.HairTransmittance = LightData.HairTransmittance;
Shadow.HairTransmittance.OpaqueVisibility = Shadow.SurfaceShadow;
}
```