2025-03-20 18:06:50 +08:00
|
|
|
|
---
|
|
|
|
|
title: ToonReflection
|
|
|
|
|
date: 2025-03-20 17:04:16
|
|
|
|
|
excerpt:
|
|
|
|
|
tags:
|
|
|
|
|
rating: ⭐
|
|
|
|
|
---
|
|
|
|
|
# 反射功能相关Pass
|
2025-03-21 20:01:46 +08:00
|
|
|
|
- ReflectionIndirect(None)
|
|
|
|
|
- [[#ReflectionEnvironmentAndSky]]
|
|
|
|
|
- DiffuseIndirectAndAO(Lumen/SSR)
|
|
|
|
|
- LumenReflections
|
|
|
|
|
- [[#DiffuseIndirectComposite]]
|
|
|
|
|
|
2025-03-25 00:13:22 +08:00
|
|
|
|
PS. DiffuseIndirectAndAO Pass会根据 ViewPipelineState.DiffuseIndirectMethod是否为Lumen,出现在Light Pass之前(GI方法为Lumen)或者之后(GI方法为非Lumen)。
|
2025-03-21 20:01:46 +08:00
|
|
|
|
## ReflectionEnvironmentAndSky
|
2025-03-21 23:40:59 +08:00
|
|
|
|
位于IndirectLightRendering.cpp的RenderDeferredReflectionsAndSkyLighting() => `AddSkyReflectionPass()`
|
2025-03-21 20:01:46 +08:00
|
|
|
|
|
2025-03-21 23:40:59 +08:00
|
|
|
|
当`DiffuseIndirectMethod = EDiffuseIndirectMethod::Lumen`(也就是开启Lumen GI),如果反射方法为Lumen或者SSR则不会执行后续逻辑。
|
|
|
|
|
|
|
|
|
|
不开启Lumen GI,反射方法为:
|
|
|
|
|
- Lumen:`RenderLumenReflections()`
|
2025-03-24 17:49:25 +08:00
|
|
|
|
- RT Reflection:`RenderRayTracingReflections()`
|
2025-03-21 23:40:59 +08:00
|
|
|
|
- SSR:`ScreenSpaceRayTracing::RenderScreenSpaceReflections()`
|
|
|
|
|
|
2025-03-23 21:51:19 +08:00
|
|
|
|
`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()
|
|
|
|
|
|
2025-03-23 23:12:48 +08:00
|
|
|
|
几种反射方式的大致执行逻辑:
|
|
|
|
|
- LumenReflection
|
|
|
|
|
1. 输出FRDGTextureRef ReflectionsColor。
|
2025-03-24 17:49:25 +08:00
|
|
|
|
- SSR与RT
|
2025-03-23 23:12:48 +08:00
|
|
|
|
1. 输出结果到IScreenSpaceDenoiser::FReflectionsInputs DenoiserInputs的FRDGTextureRef Color。
|
|
|
|
|
2. 执行对应的降噪算法。
|
|
|
|
|
3. 结果赋予给FRDGTextureRef ReflectionsColor。
|
|
|
|
|
- 执行完上述反射方法后,最后执行`AddSkyReflectionPass()`
|
|
|
|
|
|
2025-03-24 16:18:29 +08:00
|
|
|
|
FReflectionEnvironmentSkyLightingPS位于/Engine/Private/ReflectionEnvironmentPixelShader.usf的`ReflectionEnvironmentSkyLighting()`。
|
2025-03-23 23:12:48 +08:00
|
|
|
|
|
2025-03-24 16:18:29 +08:00
|
|
|
|
### 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);
|
|
|
|
|
}
|
|
|
|
|
```
|
2025-03-24 17:49:25 +08:00
|
|
|
|
|
|
|
|
|
### GatherRadiance()
|
2025-03-25 00:13:22 +08:00
|
|
|
|
GatherRadiance()主要计算了SkyLightTexture(天空盒贴图)以及ReflectionCapture(Box、Sphere反射球),最终根据之前计算的Color.a进行合成。
|
2025-03-24 17:49:25 +08:00
|
|
|
|
```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);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2025-03-25 00:13:22 +08:00
|
|
|
|
主要逻辑位于CompositeReflectionCapturesAndSkylightTWS():
|
2025-03-24 17:49:25 +08:00
|
|
|
|
```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
|
2025-03-25 00:13:22 +08:00
|
|
|
|
//ReflectionCapture Blend,其顺序为大范围在前
|
2025-03-24 17:49:25 +08:00
|
|
|
|
LOOP
|
|
|
|
|
for (uint TileCaptureIndex = 0; TileCaptureIndex < NumCapturesAffectingTile; TileCaptureIndex++)
|
|
|
|
|
{
|
|
|
|
|
BRANCH
|
2025-03-25 00:13:22 +08:00
|
|
|
|
if (ImageBasedReflections.a < 0.001)//如果Alpha小于0.001则停止循环,结束合成计算。
|
2025-03-24 17:49:25 +08:00
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 00:13:22 +08:00
|
|
|
|
//计算CaptureIndex
|
2025-03-24 17:49:25 +08:00
|
|
|
|
uint CaptureIndex = 0;
|
|
|
|
|
#ifdef REFLECTION_COMPOSITE_NO_CULLING_DATA
|
|
|
|
|
CaptureIndex = TileCaptureIndex; // Go from 0 to NumCapturesAffectingTile as absolute index in capture array
|
|
|
|
|
#else
|
2025-03-25 00:13:22 +08:00
|
|
|
|
#if (INSTANCED_STEREO || MOBILE_MULTI_VIEW)//VR或者移动端多View
|
2025-03-24 17:49:25 +08:00
|
|
|
|
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
|
2025-03-25 00:13:22 +08:00
|
|
|
|
if (CaptureVectorLength < CaptureRadius)//当前像素处于ReflectionCapture范围内
|
2025-03-24 17:49:25 +08:00
|
|
|
|
{
|
|
|
|
|
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
|
2025-03-25 00:13:22 +08:00
|
|
|
|
//盒形反射球
|
2025-03-24 17:49:25 +08:00
|
|
|
|
#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
|
2025-03-25 00:13:22 +08:00
|
|
|
|
|
|
|
|
|
//球形反射球
|
2025-03-24 17:49:25 +08:00
|
|
|
|
#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;
|
|
|
|
|
|
|
|
|
|
{
|
2025-03-25 00:13:22 +08:00
|
|
|
|
//采样对应的ReflectionCubeMap,Sample.a为根据距离计算的Alpha。最后结果累加到ImageBasedReflections中。
|
2025-03-24 17:49:25 +08:00
|
|
|
|
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
|
2025-03-25 00:13:22 +08:00
|
|
|
|
//非ReflectionCapture Blend
|
2025-03-24 17:49:25 +08:00
|
|
|
|
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;
|
|
|
|
|
|
2025-03-25 00:13:22 +08:00
|
|
|
|
// 不支持Blend的,结果大致为SkyLightCubeMap * View.SkyLightColor。支持Blend的,lerp(Reflection, BlendDestinationReflection * View.SkyLightColor.rgb, ReflectionStruct.SkyLightParameters.w);
|
2025-03-24 17:49:25 +08:00
|
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2025-03-21 20:01:46 +08:00
|
|
|
|
## DiffuseIndirectComposite
|
2025-03-25 00:13:22 +08:00
|
|
|
|
位于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()`。
|