520 lines
21 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: ToonReflection
date: 2025-03-20 17:04:16
excerpt:
tags:
rating: ⭐
---
# 反射功能相关Pass
- ReflectionIndirect(None)
- [[#ReflectionEnvironmentAndSky]]
- DiffuseIndirectAndAO(Lumen/SSR)
- LumenReflections
- [[#DiffuseIndirectComposite]]
## ReflectionEnvironmentAndSky
位于IndirectLightRendering.cpp的RenderDeferredReflectionsAndSkyLighting() => `AddSkyReflectionPass()`
`DiffuseIndirectMethod = EDiffuseIndirectMethod::Lumen`也就是开启Lumen GI如果反射方法为Lumen或者SSR则不会执行后续逻辑。
不开启Lumen GI反射方法为
- Lumen`RenderLumenReflections()`
- RT Reflection`RenderRayTracingReflections()`
- SSR`ScreenSpaceRayTracing::RenderScreenSpaceReflections()`
`RenderDeferredReflectionsAndSkyLighting()`主要执行了:
1. SkyLightDiffuse
1. RenderDistanceFieldLighting()
1. RenderDistanceFieldAOScreenGrid()渲染距离场AO。
2. RenderCapsuleShadowsForMovableSkylight():渲染胶囊阴影。
2. ReflectionIndirect
- RenderLumenReflections()
- RenderRayTracingReflections()
- RenderScreenSpaceReflections()
3. Denoise
- DenoiserIScreenSpaceDenoiser::DenoiseReflections()
- TemporalFilterAddTemporalAAPass()
4. RenderDeferredPlanarReflections():合成平面反射结果。
5. AddSkyReflectionPass()
几种反射方式的大致执行逻辑:
- LumenReflection
1. 输出FRDGTextureRef ReflectionsColor。
- SSR与RT
1. 输出结果到IScreenSpaceDenoiser::FReflectionsInputs DenoiserInputs的FRDGTextureRef Color。
2. 执行对应的降噪算法。
3. 结果赋予给FRDGTextureRef ReflectionsColor。
- 执行完上述反射方法后,最后执行`AddSkyReflectionPass()`
FReflectionEnvironmentSkyLightingPS位于/Engine/Private/ReflectionEnvironmentPixelShader.usf的`ReflectionEnvironmentSkyLighting()`
### ReflectionEnvironmentSkyLighting
```c++
void ReflectionEnvironmentSkyLighting(
in float4 SvPosition : SV_Position,
out float4 OutColor : SV_Target0
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
, out float3 OutOpaqueRoughRefractionSceneColor : SV_Target1
, out float3 OutSubSurfaceSceneColor : SV_Target2
#endif
)
{
ResolvedView = ResolveView();
//计算获去BufferUV、ScreenPosition
uint2 PixelPos = SvPosition.xy;
float2 BufferUV = SvPositionToBufferUV(SvPosition);
float2 ScreenPosition = SvPositionToScreenPosition(SvPosition).xy;
OutColor = 0.0f;
#if STRATA_OPAQUE_ROUGH_REFRACTION_ENABLED
OutOpaqueRoughRefractionSceneColor = 0.0f;
OutSubSurfaceSceneColor = 0.0f;
#endif
#if STRATA_ENABLED
...
...
#else // STRATA_ENABLED
// Sample scene textures.
FGBufferData GBuffer = GetGBufferDataFromSceneTextures(BufferUV);
uint ShadingModelID = GBuffer.ShadingModelID;
const bool bUnlitMaterial = ShadingModelID == SHADINGMODELID_UNLIT;
float3 DiffuseColor = GBuffer.DiffuseColor;
float3 SpecularColor = GBuffer.SpecularColor;
RemapClearCoatDiffuseAndSpecularColor(GBuffer, ScreenPosition, DiffuseColor, SpecularColor);//针对清漆材质进行Diffuse颜色与Specular颜色重新映射
// Sample the ambient occlusion that is dynamically generated every frame.
float AmbientOcclusion = AmbientOcclusionTexture.SampleLevel(AmbientOcclusionSampler, BufferUV, 0).r;
float3 BentNormal = GBuffer.WorldNormal;
#if APPLY_SKY_SHADOWING
{
BentNormal = UpsampleDFAO(BufferUV, GBuffer.Depth, GBuffer.WorldNormal);
}
#endif
#if ENABLE_DYNAMIC_SKY_LIGHT
BRANCH
if (!bUnlitMaterial) // Only light pixels marked as lit //Unlit材质不会计算动态天光GI的效果。
{
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, GBuffer.Depth), GBuffer.Depth, 1), View.ScreenToTranslatedWorld).xyz;
const float CloudVolumetricAOShadow = GetCloudVolumetricAOShadow(TranslatedWorldPosition);//从体积云 VolumetricCloudShadowMapTexture中取得ShadowFrontDepthKm、MaxOpticalDepthMeanExtinction最终计算出体积云阴影。UE5.3该函数没有启用。
float3 SkyLighting = CloudVolumetricAOShadow * SkyLightDiffuse(GBuffer, AmbientOcclusion, BufferUV, ScreenPosition, BentNormal, DiffuseColor);
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(ShadingModelID);
LightAccumulator_Add(LightAccumulator, SkyLighting, SkyLighting, 1.0f, bNeedsSeparateSubsurfaceLightAccumulation);
OutColor = LightAccumulator_GetResult(LightAccumulator);
}
#endif // ENABLE_DYNAMIC_SKY_LIGHT
BRANCH
if (!bUnlitMaterial && ShadingModelID != SHADINGMODELID_HAIR)//
{
OutColor.xyz += ReflectionEnvironment(GBuffer, AmbientOcclusion, BufferUV, ScreenPosition, SvPosition, BentNormal, SpecularColor, ShadingModelID);
}
#endif // STRATA_ENABLED
}
```
### SkyLightDiffuse
1. 计算float3 SkyLightingNormal、FSkyLightVisibilityData SkyVisData。
2. 计算Normal、ViewVector、NoV。
3. 针对制定ShadingModel进行额外计算
1. SHADINGMODELID_TWOSIDED_FOLIAGE使用Normal反向量取得SkySHDiffuse在乘以SubsurfaceColor、SkyVisData.SkyDiffuseLookUpMul后累加到结果上。
2. SHADINGMODELID_SUBSURFACE、SHADINGMODELID_PREINTEGRATED_SKIN从GBuffer中提取SubsurfaceColor并累加到结果上。
3. SHADINGMODELID_CLOTH从GBuffer中提取ClothFuzz(SubsurfaceColor)乘以CustomData.a并累加到结果上。
4. SHADINGMODELID_HAIR
1. DiffuseColor = EvaluateEnvHair(GBuffer, V, N, L);
2. SkyVisData.SkyDiffuseLookUpNormal = L;
3. DiffuseWeight = 1.0f;
4. 调用GetSkySHDiffuse()计算天光光照效果。GetSkySHDiffuse()本质是采样球谐贴图来获得天光GI结果。
### ReflectionEnvironment
```c++
float3 ReflectionEnvironment(FGBufferData GBuffer, float AmbientOcclusion, float2 BufferUV, float2 ScreenPosition, float4 SvPosition, float3 BentNormal, float3 SpecularColor, uint ShadingModelID)
{
float4 Color = float4(0, 0, 0, 1);
float IndirectIrradiance = GBuffer.IndirectIrradiance;
#if ENABLE_SKY_LIGHT && ALLOW_STATIC_LIGHTING
BRANCH
// Add in diffuse contribution from dynamic skylights so reflection captures will have something to mix with
if (ReflectionStruct.SkyLightParameters.y > 0 && ReflectionStruct.SkyLightParameters.z > 0)
{
//如果开启天光、并且开启静态关照。会在这里采样SkySH以此累加间接照明。
IndirectIrradiance += GetDynamicSkyIndirectIrradiance(BentNormal, GBuffer.WorldNormal);
}
#endif
//计算反射Vector、WorldNormal、ViewVector
float3 TranslatedWorldPosition = mul(float4(GetScreenPositionForProjectionType(ScreenPosition, GBuffer.Depth), GBuffer.Depth, 1), View.ScreenToTranslatedWorld).xyz;
float3 CameraToPixel = normalize(TranslatedWorldPosition - View.TranslatedWorldCameraOrigin);
float3 ReflectionVector = reflect(CameraToPixel, GBuffer.WorldNormal);
float3 V = -CameraToPixel;
float3 N = GBuffer.WorldNormal;
const float3 SavedTopLayerNormal = N;
#if SUPPORTS_ANISOTROPIC_MATERIALS
ModifyGGXAnisotropicNormalRoughness(GBuffer.WorldTangent, GBuffer.Anisotropy, GBuffer.Roughness, N, V);
#endif
float3 R = 2 * dot( V, N ) * N - V;
float NoV = saturate( dot( N, V ) );
// Point lobe in off-specular peak direction
R = GetOffSpecularPeakReflectionDir(N, R, GBuffer.Roughness);
// 采样 SSR, planar reflections, RT reflections or Lumen 反射结果。
float4 ReflectionInput = Texture2DSample(ReflectionTexture, ReflectionTextureSampler, BufferUV);
Color = CompositeReflections(ReflectionInput, BufferUV, GBuffer.Roughness, ShadingModelID);//Color = float4(ReflectionInput.rgb, 1 - ReflectionInput.a)
#if RAY_TRACED_REFLECTIONS
float4 SavedColor = Color; // When a clear coat material is encountered, we save the reflection buffer color for it to not be affected by operations.
#endif
if(GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT )
{
#if RAY_TRACED_REFLECTIONS
Color = float4(0, 0, 0, 1); // Clear coat reflection is expected to be computed on a black background
#endif
const float ClearCoat = GBuffer.CustomData.x;
Color = lerp( Color, float4(0,0,0,1), ClearCoat );
#if CLEAR_COAT_BOTTOM_NORMAL
const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 4) - (512.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
const float3 ClearCoatUnderNormal = OctahedronToUnitVector(oct1);
const float3 BottomEffectiveNormal = ClearCoatUnderNormal;
R = 2 * dot( V, ClearCoatUnderNormal ) * ClearCoatUnderNormal - V;
#endif
}
float AO = GBuffer.GBufferAO * AmbientOcclusion;//AmbientOcclusion为SSAO或者RTAO或者DFAO或者Lumen……
float RoughnessSq = GBuffer.Roughness * GBuffer.Roughness;
float SpecularOcclusion = GetSpecularOcclusion(NoV, RoughnessSq, AO);
Color.a *= SpecularOcclusion;
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
float2 LocalPosition = SvPosition.xy - View.ViewRectMin.xy;
uint GridIndex = ComputeLightGridCellIndex(uint2(LocalPosition.x, LocalPosition.y), GBuffer.Depth);
uint NumCulledEntryIndex = (ForwardLightData.NumGridCells + GridIndex) * NUM_CULLED_LIGHTS_GRID_STRIDE;
uint NumCulledReflectionCaptures = min(ForwardLightData.NumCulledLightsGrid[NumCulledEntryIndex + 0], ForwardLightData.NumReflectionCaptures);
uint DataStartIndex = ForwardLightData.NumCulledLightsGrid[NumCulledEntryIndex + 1];
#else
uint DataStartIndex = 0;
uint NumCulledReflectionCaptures = 0;
#endif
const FBxDFEnergyTerms EnergyTerms = ComputeGGXSpecEnergyTerms(GBuffer.Roughness, NoV, GBuffer.SpecularColor);
//常规反射 或 底层清漆 光照计算
//Top of regular reflection or bottom layer of clear coat.
Color.rgb += View.PreExposure * GatherRadiance(Color.a, TranslatedWorldPosition, R, GBuffer.Roughness, BentNormal, IndirectIrradiance, GBuffer.ShadingModelID, NumCulledReflectionCaptures, DataStartIndex);
BRANCH
if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT)
{
const float ClearCoat = GBuffer.CustomData.x;
const float ClearCoatRoughness = GBuffer.CustomData.y;
// Restore saved values needed for the top layer.
GBuffer.WorldNormal = SavedTopLayerNormal;
// Recompute some values unaffected by anistropy for the top layer
N = GBuffer.WorldNormal;
R = 2 * dot(V, N) * N - V;
NoV = saturate(dot(N, V));
R = GetOffSpecularPeakReflectionDir(N, R, ClearCoatRoughness);
// TODO EnvBRDF should have a mask param
#if USE_ENERGY_CONSERVATION
Color.rgb *= EnergyTerms.E * (1 - ClearCoat);
#else
// Hack: Ensures when clear coat is >0, grazing angle does not get too much energy,
// but preserve response at normal incidence
float2 AB = PreIntegratedGF.SampleLevel(PreIntegratedGFSampler, float2(NoV, GBuffer.Roughness), 0).rg;
Color.rgb *= SpecularColor * AB.x + AB.y * saturate(50 * SpecularColor.g) * (1 - ClearCoat);
#endif
// F_Schlick
const float CoatF0 = 0.04f;
#if USE_ENERGY_CONSERVATION
float F = ComputeGGXSpecEnergyTerms(ClearCoatRoughness, NoV, CoatF0).E.x;
#else
float F = EnvBRDF(CoatF0, ClearCoatRoughness, NoV).x;
#endif
F *= ClearCoat;
float LayerAttenuation = (1 - F);
Color.rgb *= LayerAttenuation;
Color.a = F;
#if !RAY_TRACED_REFLECTIONS
Color.rgb += ReflectionInput.rgb * F;
Color.a *= 1 - ReflectionInput.a;
#endif
Color.a *= SpecularOcclusion;
float3 TopLayerR = 2 * dot( V, N ) * N - V;
Color.rgb += View.PreExposure * GatherRadiance(Color.a, TranslatedWorldPosition, TopLayerR, ClearCoatRoughness, BentNormal, IndirectIrradiance, GBuffer.ShadingModelID, NumCulledReflectionCaptures, DataStartIndex);
#if RAY_TRACED_REFLECTIONS
Color.rgb = SavedColor.rgb + Color.rgb * SavedColor.a; // Compose default clear coat reflection over regular refelction (using Premultiplied alpha where SaveColor.a=transmittance)
#endif
}
else
{
#if USE_ENERGY_CONSERVATION
Color.rgb *= EnergyTerms.E;
#else
Color.rgb *= EnvBRDF( SpecularColor, GBuffer.Roughness, NoV );
#endif
}
// Transform NaNs to black, transform negative colors to black.
return -min(-Color.rgb, 0.0);
}
```
### GatherRadiance()
```c++
float3 GatherRadiance(float CompositeAlpha, float3 TranslatedWorldPosition, float3 RayDirection, float Roughness, float3 BentNormal, float IndirectIrradiance, uint ShadingModelID, uint NumCulledReflectionCaptures, uint CaptureDataStartIndex)
{
// Indirect occlusion from DFAO, which should be applied to reflection captures and skylight specular, but not SSR
float IndirectSpecularOcclusion = 1.0f;
float3 ExtraIndirectSpecular = 0;
#if SUPPORT_DFAO_INDIRECT_OCCLUSION
float IndirectDiffuseOcclusion;
GetDistanceFieldAOSpecularOcclusion(BentNormal, RayDirection, Roughness, ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE, IndirectSpecularOcclusion, IndirectDiffuseOcclusion, ExtraIndirectSpecular);
// Apply DFAO to IndirectIrradiance before mixing with indirect specular
IndirectIrradiance *= IndirectDiffuseOcclusion;
#endif
const bool bCompositeSkylight = true;
return CompositeReflectionCapturesAndSkylightTWS(
CompositeAlpha,
TranslatedWorldPosition,
RayDirection,
Roughness,
IndirectIrradiance,
IndirectSpecularOcclusion,
ExtraIndirectSpecular,
NumCulledReflectionCaptures,
CaptureDataStartIndex,
0,
bCompositeSkylight);
}
```
```c++
float3 CompositeReflectionCapturesAndSkylightTWS(
float CompositeAlpha,
float3 TranslatedWorldPosition,
float3 RayDirection,
float Roughness,
float IndirectIrradiance,
float IndirectSpecularOcclusion,
float3 ExtraIndirectSpecular,
uint NumCapturesAffectingTile,
uint CaptureDataStartIndex,
int SingleCaptureIndex,
bool bCompositeSkylight,
uint EyeIndex)
{
float Mip = ComputeReflectionCaptureMipFromRoughness(Roughness, View.ReflectionCubemapMaxMip);
float4 ImageBasedReflections = float4(0, 0, 0, CompositeAlpha);
float2 CompositedAverageBrightness = float2(0.0f, 1.0f);
#if REFLECTION_COMPOSITE_USE_BLENDED_REFLECTION_CAPTURES
// Accumulate reflections from captures affecting this tile, applying largest captures first so that the smallest ones display on top
LOOP
for (uint TileCaptureIndex = 0; TileCaptureIndex < NumCapturesAffectingTile; TileCaptureIndex++)
{
BRANCH
if (ImageBasedReflections.a < 0.001)
{
break;
}
uint CaptureIndex = 0;
#ifdef REFLECTION_COMPOSITE_NO_CULLING_DATA
CaptureIndex = TileCaptureIndex; // Go from 0 to NumCapturesAffectingTile as absolute index in capture array
#else
#if (INSTANCED_STEREO || MOBILE_MULTI_VIEW)
BRANCH
if (EyeIndex == 0)
{
#endif
CaptureIndex = GetCulledLightDataGrid(CaptureDataStartIndex + TileCaptureIndex);
#if (INSTANCED_STEREO || MOBILE_MULTI_VIEW)
}
else
{
CaptureIndex = GetCulledLightDataGridISR(CaptureDataStartIndex + TileCaptureIndex);
}
#endif
#endif
FLWCVector3 CaptureWorldPosition = MakeLWCVector3(GetReflectionTilePosition(CaptureIndex).xyz, GetReflectionPositionAndRadius(CaptureIndex).xyz);
float3 CaptureTranslatedWorldPosition = LWCToFloat(LWCAdd(CaptureWorldPosition, ResolvedView.PreViewTranslation));
float CaptureRadius = GetReflectionPositionAndRadius(CaptureIndex).w;
float4 CaptureProperties = GetReflectionCaptureProperties(CaptureIndex);
float3 CaptureVector = TranslatedWorldPosition - CaptureTranslatedWorldPosition;
float CaptureVectorLength = sqrt(dot(CaptureVector, CaptureVector));
float NormalizedDistanceToCapture = saturate(CaptureVectorLength / CaptureRadius);
BRANCH
if (CaptureVectorLength < CaptureRadius)
{
float3 ProjectedCaptureVector = RayDirection;
float4 CaptureOffsetAndAverageBrightness = GetReflectionCaptureOffsetAndAverageBrightness(CaptureIndex);
// Fade out based on distance to capture
float DistanceAlpha = 0;
#define PROJECT_ONTO_SHAPE 1
#if PROJECT_ONTO_SHAPE
#if REFLECTION_COMPOSITE_HAS_BOX_CAPTURES
#if REFLECTION_COMPOSITE_HAS_SPHERE_CAPTURES
// Box
BRANCH if (CaptureProperties.b > 0)
#endif
{
ProjectedCaptureVector = GetLookupVectorForBoxCapture(RayDirection, TranslatedWorldPosition, float4(CaptureTranslatedWorldPosition, CaptureRadius),
GetReflectionBoxTransform(CaptureIndex), GetReflectionBoxScales(CaptureIndex), CaptureOffsetAndAverageBrightness.xyz, DistanceAlpha);
}
#endif
#if REFLECTION_COMPOSITE_HAS_SPHERE_CAPTURES
// Sphere
#if REFLECTION_COMPOSITE_HAS_BOX_CAPTURES
else
#endif
{
ProjectedCaptureVector = GetLookupVectorForSphereCapture(RayDirection, TranslatedWorldPosition, float4(CaptureTranslatedWorldPosition, CaptureRadius), NormalizedDistanceToCapture, CaptureOffsetAndAverageBrightness.xyz, DistanceAlpha);
}
#endif
#else
DistanceAlpha = 1.0;
#endif //PROJECT_ONTO_SHAPE
float CaptureArrayIndex = CaptureProperties.g;
{
float4 Sample = ReflectionStruct.ReflectionCubemap.SampleLevel(ReflectionStruct.ReflectionCubemapSampler, float4(ProjectedCaptureVector, CaptureArrayIndex), Mip);
Sample.rgb *= CaptureProperties.r;
Sample *= DistanceAlpha;
// Under operator (back to front)
ImageBasedReflections.rgb += Sample.rgb * ImageBasedReflections.a * IndirectSpecularOcclusion;
ImageBasedReflections.a *= 1 - Sample.a;
float AverageBrightness = CaptureOffsetAndAverageBrightness.w;
CompositedAverageBrightness.x += AverageBrightness * DistanceAlpha * CompositedAverageBrightness.y;
CompositedAverageBrightness.y *= 1 - DistanceAlpha;
}
}
}
#else
float3 ProjectedCaptureVector = RayDirection;
FLWCVector3 SingleCaptureWorldPosition = MakeLWCVector3(GetReflectionTilePosition(SingleCaptureIndex).xyz, GetReflectionPositionAndRadius(SingleCaptureIndex).xyz);
float3 SingleCaptureTranslatedWorldPosition = LWCToFloat(LWCAdd(SingleCaptureWorldPosition, ResolvedView.PreViewTranslation));
float SingleCaptureRadius = GetReflectionPositionAndRadius(SingleCaptureIndex).w;
float4 SingleCaptureOffsetAndAverageBrightness = GetReflectionCaptureOffsetAndAverageBrightness(SingleCaptureIndex);
float SingleCaptureBrightness = GetReflectionCaptureProperties(SingleCaptureIndex).x;
float SingleCaptureArrayIndex = GetReflectionCaptureProperties(SingleCaptureIndex).y;
#define APPROXIMATE_CONTINUOUS_SINGLE_CAPTURE_PARALLAX 0
#if APPROXIMATE_CONTINUOUS_SINGLE_CAPTURE_PARALLAX
float3 CaptureVector = TranslatedWorldPosition - SingleCaptureTranslatedWorldPosition;
float CaptureVectorLength = sqrt(dot(CaptureVector, CaptureVector));
float NormalizedDistanceToCapture = saturate(CaptureVectorLength / SingleCaptureRadius);
float UnusedDistanceAlpha = 0;
ProjectedCaptureVector = GetLookupVectorForSphereCapture(RayDirection, TranslatedWorldPosition, float4(SingleCaptureTranslatedWorldPosition, SingleCaptureRadius), NormalizedDistanceToCapture, SingleCaptureOffsetAndAverageBrightness.xyz, UnusedDistanceAlpha);
float x = saturate(NormalizedDistanceToCapture);
float DistanceAlpha = 1 - x * x * (3 - 2 * x);
// Lerp between sphere parallax corrected and infinite based on distance to shape
ProjectedCaptureVector = lerp(RayDirection, normalize(ProjectedCaptureVector), DistanceAlpha);
#endif
float4 Sample = TextureCubeArraySampleLevel(ReflectionStruct.ReflectionCubemap, ReflectionStruct.ReflectionCubemapSampler, ProjectedCaptureVector, SingleCaptureArrayIndex, Mip);
Sample.rgb *= SingleCaptureBrightness;
ImageBasedReflections = float4(Sample.rgb, 1 - Sample.a);
float AverageBrightness = SingleCaptureOffsetAndAverageBrightness.w;
CompositedAverageBrightness.x += AverageBrightness * CompositedAverageBrightness.y;
CompositedAverageBrightness.y = 0;
#endif
// Apply indirect lighting scale while we have only accumulated reflection captures
ImageBasedReflections.rgb *= View.PrecomputedIndirectSpecularColorScale;
CompositedAverageBrightness.x *= Luminance( View.PrecomputedIndirectSpecularColorScale );
#if ENABLE_SKY_LIGHT
BRANCH
if (ReflectionStruct.SkyLightParameters.y > 0 && bCompositeSkylight)
{
float SkyAverageBrightness = 1.0f;
#if REFLECTION_COMPOSITE_SUPPORT_SKYLIGHT_BLEND
float3 SkyLighting = GetSkyLightReflectionSupportingBlend(RayDirection, Roughness, SkyAverageBrightness);
#else
float3 SkyLighting = GetSkyLightReflection(RayDirection, Roughness, SkyAverageBrightness);
#endif
// Normalize for static skylight types which mix with lightmaps, material ambient occlusion as well as diffuse/specular occlusion.
bool bNormalize = ReflectionStruct.SkyLightParameters.z < 1 && ALLOW_STATIC_LIGHTING;
FLATTEN
if (bNormalize)
{
ImageBasedReflections.rgb += ImageBasedReflections.a * SkyLighting * IndirectSpecularOcclusion;
CompositedAverageBrightness.x += SkyAverageBrightness * CompositedAverageBrightness.y;
}
else
{
ExtraIndirectSpecular += SkyLighting * IndirectSpecularOcclusion;
}
}
#endif
#if ALLOW_STATIC_LIGHTING
ImageBasedReflections.rgb *= ComputeMixingWeight(IndirectIrradiance, CompositedAverageBrightness.x, Roughness);
#endif
ImageBasedReflections.rgb += ImageBasedReflections.a * ExtraIndirectSpecular;
return ImageBasedReflections.rgb;
}
```
## DiffuseIndirectComposite
位于IndirectLightRendering.cpp的`RenderDiffuseIndirectAndAmbientOcclusion()`