Files
UnrealEngine/Engine/Shaders/Private/HeterogeneousVolumes/HeterogeneousVolumesComposite.usf
2025-05-18 13:04:45 +08:00

229 lines
6.5 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "HeterogeneousVolumesAdaptiveVolumetricShadowMapSampling.ush"
#ifndef UPSAMPLE_MODE
#define UPSAMPLE_MODE 0
#endif // UPSAMPLE_MODE
#define UPSAMPLE_MODE_NEAREST 1
#define UPSAMPLE_MODE_BILINEAR 2
#ifndef FILTER_MODE
#define FILTER_MODE 0
#endif // FILTER_MODE
#define FILTER_MODE_BILATERAL 1
#define FILTER_MODE_GAUSSIAN_3X3 2
#define FILTER_MODE_GAUSSIAN_5x5 3
Texture2D SceneDepthTexture;
Texture2D<float4> HeterogeneousVolumeRadiance;
Texture2D<float> HeterogeneousVolumeHoldout;
RWTexture2D<float4> RWColorTexture;
int DownsampleFactor;
int bUseAVSM;
float SampleTransmittance(uint2 ViewPixelCoord)
{
float DeviceZ = SceneDepthTexture.Load(int3(ViewPixelCoord, 0)).r;
#if HAS_INVERTED_Z_BUFFER
DeviceZ = max(0.000000000001, DeviceZ);
#endif // HAS_INVERTED_Z_BUFFER
float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(float4(ViewPixelCoord + View.TemporalAAJitter.xy, DeviceZ, 1));
return AVSM_SampleTransmittance(TranslatedWorldPosition, ResolvedView.TranslatedWorldCameraOrigin);
}
RWTexture2D<float4> RWUpsampledTexture;
SamplerState TextureSampler;
uint2 UpsampledResolution;
uint2 UpsampledViewRect;
uint2 UpsampledViewRectMin;
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
void HeterogeneousVolumesUpsampleCS(
uint2 GroupThreadId : SV_GroupThreadID,
uint2 DispatchThreadId : SV_DispatchThreadID
)
{
if (any(DispatchThreadId.xy >= UpsampledViewRect))
{
return;
}
int2 UpsampledPixelCoord = DispatchThreadId.xy + UpsampledViewRectMin;
#if UPSAMPLE_MODE == UPSAMPLE_MODE_BILINEAR
float2 UV = (UpsampledPixelCoord + 0.5 * DownsampleFactor) / float2(UpsampledResolution);
UV = clamp(UV, 0, (UpsampledViewRect + UpsampledViewRectMin - 1) / float2(UpsampledResolution));
float4 Result = Texture2DSample(HeterogeneousVolumeRadiance, TextureSampler, UV);
#else
int2 DownsampledPixelCoord = UpsampledPixelCoord / DownsampleFactor;
float4 Result = HeterogeneousVolumeRadiance[DownsampledPixelCoord];
#endif
RWUpsampledTexture[UpsampledPixelCoord] = Result;
}
int FilterWidth;
RWTexture2D<float4> RWFilteredTexture;
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
void HeterogeneousVolumesFilterCS(
uint2 GroupThreadId : SV_GroupThreadID,
uint2 DispatchThreadId : SV_DispatchThreadID
)
{
if (any(DispatchThreadId.xy >= UpsampledViewRect))
{
return;
}
int2 PixelCoord = DispatchThreadId.xy + UpsampledViewRectMin;
float4 Radiance = HeterogeneousVolumeRadiance[PixelCoord];
float HeterogeneousVolumeTransmittance = Radiance.a;
// Bilaterial Filter
#if FILTER_MODE == FILTER_MODE_BILATERAL
{
Radiance = 0.0;
float WeightSum = 0.0;
uint FilterHalfWidth = FilterWidth / 2;
float FilterRadius2 = max(2.0 * FilterWidth * FilterWidth, 1.0);
for (int X = 0; X < FilterWidth; ++X)
{
for (int Y = 0; Y < FilterWidth; ++Y)
{
int2 Offset = int2(X, Y) - FilterHalfWidth;
int2 SampleCoord = PixelCoord + Offset;
SampleCoord = clamp(SampleCoord, 0, int2(UpsampledViewRect + UpsampledViewRectMin) - 1);
float FilterWeight = max(FilterRadius2 - dot(Offset, Offset), 0.0);
float4 Value = HeterogeneousVolumeRadiance[SampleCoord];
float3 RadianceDelta = HeterogeneousVolumeRadiance[PixelCoord].rgb - Value.rgb;
float RadianceWeight = saturate(1.0 - dot(RadianceDelta, RadianceDelta));
float TauDelta = HeterogeneousVolumeTransmittance - Value.a;
float TauWeight = 1.0 - TauDelta * TauDelta;
Radiance += Value * FilterWeight * RadianceWeight * TauWeight;
WeightSum += FilterWeight * RadianceWeight * TauWeight;
}
}
Radiance /= WeightSum;
}
// Gaussian 3x3 Filter
#elif FILTER_MODE == FILTER_MODE_GAUSSIAN_3X3
{
float Gaussian3x3Weights[] = {
1.0, 2.0, 1.0,
2.0, 4.0, 2.0,
1.0, 2.0, 1.0
};
Radiance = 0.0;
float WeightSum = 0.0;
uint FilterHalfWidth = 1;
for (int X = 0; X < 3; ++X)
{
for (int Y = 0; Y < 3; ++Y)
{
int2 OffsetCoord = int2(X, Y);
int2 SampleCoord = PixelCoord + OffsetCoord - FilterHalfWidth;
SampleCoord = clamp(SampleCoord, 0, int2(UpsampledViewRect + UpsampledViewRectMin) - 1);
float FilterWeight = Gaussian3x3Weights[OffsetCoord.y * 3 + OffsetCoord.x];
Radiance += HeterogeneousVolumeRadiance[SampleCoord] * FilterWeight;
WeightSum += FilterWeight;
}
}
Radiance /= WeightSum;
}
// Gaussian 5x5 Filter
#elif FILTER_MODE == FILTER_MODE_GAUSSIAN_5x5
{
float Gaussian5x5Weights[] = {
1.0, 4.0, 7.0, 4.0, 1.0,
4.0, 16.0, 26.0, 16.0, 4.0,
7.0, 26.0, 41.0, 26.0, 7.0,
4.0, 16.0, 26.0, 16.0, 4.0,
1.0, 4.0, 7.0, 4.0, 1.0,
};
Radiance = 0.0;
float WeightSum = 0.0;
uint FilterHalfWidth = 2;
for (int X = 0; X < 5; ++X)
{
for (int Y = 0; Y < 5; ++Y)
{
int2 OffsetCoord = int2(X, Y);
int2 SampleCoord = PixelCoord + OffsetCoord - FilterHalfWidth;
SampleCoord = clamp(SampleCoord, 0, int2(UpsampledViewRect + UpsampledViewRectMin) - 1);
float FilterWeight = Gaussian5x5Weights[OffsetCoord.y * 5 + OffsetCoord.x];
Radiance += HeterogeneousVolumeRadiance[SampleCoord] * FilterWeight;
WeightSum += FilterWeight;
}
}
Radiance /= WeightSum;
}
#endif
RWFilteredTexture[PixelCoord] = Radiance;
}
int2 GetDownsampledResolution(int2 Resolution, int Factor)
{
return (Resolution + Factor - 1) / Factor;
}
int2 GetScaledViewRect()
{
return GetDownsampledResolution(View.ViewSizeAndInvSize.xy, DownsampleFactor);
}
[numthreads(THREADGROUP_SIZE_2D, THREADGROUP_SIZE_2D, 1)]
void HeterogeneousVolumesCompositeCS(
uint2 GroupThreadId : SV_GroupThreadID,
uint2 DispatchThreadId : SV_DispatchThreadID
)
{
ResolvedView = ResolveView();
if (any(DispatchThreadId >= View.ViewSizeAndInvSize.xy))
{
return;
}
uint2 ViewPixelCoord = DispatchThreadId.xy + View.ViewRectMin.xy;
uint2 PixelCoord = ViewPixelCoord / DownsampleFactor;
// Cache Sample values
float4 Radiance = HeterogeneousVolumeRadiance[PixelCoord];
float HeterogeneousVolumeTransmittance = bUseAVSM ? SampleTransmittance(ViewPixelCoord) : Radiance.a;
//float HeterogeneousVolumeTransmittance = Radiance.a;
float4 Result;
Result.rgb = Radiance.rgb + RWColorTexture[ViewPixelCoord].rgb * HeterogeneousVolumeTransmittance;
#if SUPPORT_PRIMITIVE_ALPHA_HOLDOUT
if (View.bPrimitiveAlphaHoldoutEnabled)
{
float SurfaceOpacity = RWColorTexture[ViewPixelCoord].a * HeterogeneousVolumeTransmittance;
float VolumeOpacity = HeterogeneousVolumeHoldout[PixelCoord];
Result.a = SurfaceOpacity + VolumeOpacity;
}
#else
Result.a = RWColorTexture[ViewPixelCoord].a * HeterogeneousVolumeTransmittance;
#endif
RWColorTexture[ViewPixelCoord] = Result;
}