--- title: ToonReflection date: 2025-03-20 17:04:16 excerpt: tags: rating: ⭐ --- # 反射功能相关Pass - ReflectionIndirect(None) - [[#ReflectionEnvironmentAndSky]] - DiffuseIndirectAndAO(Lumen/SSR) - LumenReflections - [[#DiffuseIndirectComposite]] PS. DiffuseIndirectAndAO Pass会根据 ViewPipelineState.DiffuseIndirectMethod是否为Lumen,出现在Light Pass之前(GI方法为Lumen)或者之后(GI方法为非Lumen)。 ## 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 - Denoiser:IScreenSpaceDenoiser::DenoiseReflections() - TemporalFilter:AddTemporalAAPass() 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、MaxOpticalDepth,MeanExtinction,最终计算出体积云阴影。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() GatherRadiance()主要计算了SkyLightTexture(天空盒贴图)以及ReflectionCapture(Box、Sphere反射球),最终根据之前计算的Color.a进行合成。 ```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); } ``` 主要逻辑位于CompositeReflectionCapturesAndSkylightTWS(): ```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 //ReflectionCapture Blend,其顺序为大范围在前 LOOP for (uint TileCaptureIndex = 0; TileCaptureIndex < NumCapturesAffectingTile; TileCaptureIndex++) { BRANCH if (ImageBasedReflections.a < 0.001)//如果Alpha小于0.001则停止循环,结束合成计算。 { break; } //计算CaptureIndex 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)//VR或者移动端多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)//当前像素处于ReflectionCapture范围内 { 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; { //采样对应的ReflectionCubeMap,Sample.a为根据距离计算的Alpha。最后结果累加到ImageBasedReflections中。 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 //非ReflectionCapture Blend 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; // 不支持Blend的,结果大致为SkyLightCubeMap * View.SkyLightColor。支持Blend的,lerp(Reflection, BlendDestinationReflection * View.SkyLightColor.rgb, ReflectionStruct.SkyLightParameters.w); #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()` `RenderDiffuseIndirectAndAmbientOcclusion()`主要执行了: 1. 进行判断是否跳过当前View的计算。(主要是判断是否开启Lumen,以及传入的bCompositeRegularLumenOnly变量) 2. SetupCommonDiffuseIndirectParameters() 3. 计算GI - ScreenSpaceGI - RTGI - Lumen - Lumen Reflection:输出结果到OutTextures.Textures[3]。 - SSR:输出结果到OutTextures.Textures[3]。 - 其他Reflection:结果输出为黑色。 4. 使用降噪器对SSGI与RTGI进行降噪 5. 渲染AO - 禁用 - RTAO - SSAO - 其他没有实现的会谈报错提醒 6. 将渲染的AO结果赋予SceneTextures.ScreenSpaceAO 7. RenderHairStrandsAmbientOcclusion() 8. 位于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 - Denoiser:IScreenSpaceDenoiser::DenoiseReflections() - TemporalFilter:AddTemporalAAPass() 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()`。