feat: added htrace

This commit is contained in:
Chris
2026-01-02 23:27:08 -05:00
parent dfff4e6f1d
commit 20f1fbb211
203 changed files with 17634 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
#pragma kernel CheckerboardClassification
#pragma kernel IndirectArguments
#include "../Includes/HCommonSSGI.hlsl"
#pragma multi_compile _ _GBUFFER_NORMALS_OCT
H_TEXTURE(_SampleCount);
RWStructuredBuffer<uint> _RayCounter;
RWStructuredBuffer<uint> _RayCounter_Output;
RWStructuredBuffer<uint2> _IndirectCoords_Output;
RWStructuredBuffer<uint> _IndirectArguments_Output;
uint _RayTracedCounter;
groupshared uint SharedRayAllocator;
groupshared uint SharedAllocationStartOffset;
groupshared uint2 SharedAllocatedRays[8 * 8];
// ------------------------ CHECKERBOARD CLASSIFICATION -------------------------
[numthreads(8, 8, 1)]
void CheckerboardClassification(uint3 pixCoord : SV_DispatchThreadID, uint2 groupThreadID : SV_GroupThreadID, uint groupIndex : SV_GroupIndex, uint2 groupID : SV_GroupID)
{
if (groupIndex == 0) SharedRayAllocator = 0;
GroupMemoryBarrierWithGroupSync();
uint IndirectCoordOffsetVR = _ScreenSize.x * _ScreenSize.y * pixCoord.z / _HScaleFactorSSGI;
uint RayCounterOffsetVR = 2 * pixCoord.z;
float Samplecount = H_LOAD(_SampleCount, pixCoord.xy).x;
bool CullCheckerboard = false;
if (((pixCoord.x + pixCoord.y) % 2 == 0 && uint(_FrameCount) % 2 == 0) || Samplecount <= 1)
CullCheckerboard = true;
if (((pixCoord.x + pixCoord.y) % 2 != 0 && uint(_FrameCount) % 2 != 0) || Samplecount <= 1)
CullCheckerboard = true;
if (CullCheckerboard)
{
// uint Index = 0;
// InterlockedAdd(_RayCounter_Output[0 + RayCounterOffsetVR], 1, Index);
// _IndirectCoords_Output[Index + IndirectCoordOffsetVR] = pixCoord.xy;
uint SharedTexelOffset;
InterlockedAdd(SharedRayAllocator, 1, SharedTexelOffset);
SharedAllocatedRays[SharedTexelOffset] = pixCoord.xy;
}
GroupMemoryBarrierWithGroupSync();
uint ThreadIndex = groupThreadID.y * 8 + groupThreadID.x;
if (ThreadIndex == 0)
{
InterlockedAdd(_RayCounter_Output[0 + RayCounterOffsetVR], SharedRayAllocator, SharedAllocationStartOffset);
}
GroupMemoryBarrierWithGroupSync();
if (ThreadIndex < SharedRayAllocator)
{
_IndirectCoords_Output[SharedAllocationStartOffset + ThreadIndex + IndirectCoordOffsetVR] = SharedAllocatedRays[ThreadIndex];
}
}
// ------------------------ INDIRECT ARGUMENTS GENERATION -------------------------
[numthreads(1, 1, 1)]
void IndirectArguments(uint3 pixCoord : SV_DispatchThreadID, uint2 groupThreadID : SV_GroupThreadID, uint2 groupID : SV_GroupID)
{
uint IndirectArgumentsOffsetVR = 3 * pixCoord.z;
uint RayCounterOffsetVR = 2 * pixCoord.z;
uint RayCounterBuffer = _RayCounter[0 + RayCounterOffsetVR];
_IndirectArguments_Output[0 + IndirectArgumentsOffsetVR] = (RayCounterBuffer + 63) / 64;
_IndirectArguments_Output[1 + IndirectArgumentsOffsetVR] = 1;
_IndirectArguments_Output[2 + IndirectArgumentsOffsetVR] = 1;
_RayCounter[0 + RayCounterOffsetVR] = 0;
_RayCounter[1 + RayCounterOffsetVR] = RayCounterBuffer;
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 412aeb9af3abb33439c8084a878f56c2
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HCheckerboardingSSGI.compute
uploadId: 840002

View File

@@ -0,0 +1,116 @@
#pragma kernel Debug
#include "../Includes/HCommonSSGI.hlsl"
#include "../Includes/HFallbackSSGI.hlsl"
#pragma multi_compile _ _GBUFFER_NORMALS_OCT
H_TEXTURE(_SampleCountSSGI);
H_TEXTURE(_HTraceBufferGI);
H_RW_TEXTURE(float4, _Debug_Output);
uint _DebugSwitch;
uint _BuffersSwitch;
float _IndirectLightingIntensity;
void DebugFinal(uint3 pixCoord)
{
if (_DebugSwitch == 1)
{
if (_BuffersSwitch == 0) // Multi
{
uint2 AuadSize = _ScreenSize.xy / 2;
uint2 AuadIndex = pixCoord.xy / AuadSize; // (0,0), (1,0), (0,1), (1,1)
uint2 LocalCoord = pixCoord.xy % AuadSize;
uint2 SampleCoord = LocalCoord * 2;
float4 Output = float4(0, 0, 0, 1);
if (HBUFFER_DEPTH(SampleCoord.xy) <= UNITY_RAW_FAR_CLIP_VALUE)
{
_Debug_Output[H_COORD(pixCoord.xy)] = Output;
return;
}
if (AuadIndex.x == 0 && AuadIndex.y == 0) // Bottom Left
{
Output = float4(HBUFFER_NORMAL_WS(SampleCoord).xyz, 1);
}
else if (AuadIndex.x == 1 && AuadIndex.y == 0) // Bottom Right
{
float2 motionVector = HBUFFER_MOTION_VECTOR(SampleCoord).xy;
float motionMask = HBUFFER_MOTION_MASK(SampleCoord).x;
Output = float4(motionVector * 5, motionMask * 0.05, 1);
}
else if (AuadIndex.x == 0 && AuadIndex.y == 1) // Top Left
{
Output = float4(H_LINEAR_EYE_DEPTH(H_LOAD(g_HTraceDepthPyramidSSGI, SampleCoord).x).xxx / 20.0f, 1);
}
else if (AuadIndex.x == 1 && AuadIndex.y == 1) // Top Right
{
Output = float4(HBUFFER_DIFFUSE(SampleCoord).xyz, 1);
}
_Debug_Output[H_COORD(pixCoord.xy)] = Output;
return;
}
if (HBUFFER_DEPTH(pixCoord.xy) <= UNITY_RAW_FAR_CLIP_VALUE)
{
_Debug_Output[H_COORD(pixCoord.xy)] = 0;
return;
}
if (_BuffersSwitch == 1) // Depth
{
_Debug_Output[H_COORD(pixCoord.xy)] = float4(H_LINEAR_EYE_DEPTH(H_LOAD(g_HTraceDepthPyramidSSGI, pixCoord.xy).x).xxx / 20.0f, 1);
}
if (_BuffersSwitch == 2) // Diffuse
{
_Debug_Output[H_COORD(pixCoord.xy)] = float4(HBUFFER_DIFFUSE(pixCoord.xy).xyz, 1);
}
if (_BuffersSwitch == 3) // Normal
{
_Debug_Output[H_COORD(pixCoord.xy)] = float4(HBUFFER_NORMAL_WS(pixCoord.xy).xyz, 1);
}
if (_BuffersSwitch == 4) // Motion Mask
{
_Debug_Output[H_COORD(pixCoord.xy)] = float4(HBUFFER_MOTION_MASK(pixCoord.xy).xxx, 1);
}
if (_BuffersSwitch == 5) // Motion Vectors
{
_Debug_Output[H_COORD(pixCoord.xy)] = float4(HBUFFER_MOTION_VECTOR(pixCoord.xy).xy * 5, 0, 1);
}
}
if (HBUFFER_DEPTH(pixCoord.xy) <= UNITY_RAW_FAR_CLIP_VALUE)
{
float3 Sky = 0;
if (_DebugSwitch == 2)
Sky = EvaluateFallbackSky(H_GET_VIEW_VECTOR_WS((pixCoord.xy + 0.5f) * _ScreenSize.zw));
_Debug_Output[H_COORD(pixCoord.xy)] = float4(Sky, 1);
return;
}
if (_DebugSwitch == 2)
{
_Debug_Output[H_COORD(pixCoord.xy)] = HBUFFER_COLOR(pixCoord.xy);
}
if (_DebugSwitch == 3)
{
_Debug_Output[H_COORD(pixCoord.xy)] = H_LOAD(_HTraceBufferGI, pixCoord.xy) * _IndirectLightingIntensity;
}
if (_DebugSwitch == 4)
{
float Samplecount = H_LOAD(_SampleCountSSGI, pixCoord.xy / _HScaleFactorSSGI).x;
float3 Output = Samplecount < 1 ? float3(1,0,0) : Samplecount.xxx / 16.0f;
_Debug_Output[H_COORD(pixCoord.xy)] = float4(Output, 1);
}
}
[numthreads(8, 8, 1)]
void Debug(uint3 pixCoord : SV_DispatchThreadID)
{
DebugFinal(pixCoord);
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 163108b35413d864fa7afffcb87d9861
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HDebugSSGI.compute
uploadId: 840002

View File

@@ -0,0 +1,343 @@
#pragma kernel TemporalAccumulation
#pragma kernel TemporalStabilization
#pragma kernel PointDistributionFill
#pragma kernel SpatialFilter
#pragma kernel SpatialFilter1 SpatialFilter = SpatialFilter1
#pragma kernel SpatialFilter2 SpatialFilter = SpatialFilter2 SECOND_PASS
#include "../Includes/HReservoirSSGI.hlsl"
#pragma multi_compile _ _GBUFFER_NORMALS_OCT
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl"
#pragma multi_compile _ USE_SPATIAL_OCCLUSION
#pragma multi_compile _ USE_TEMPORAL_INVALIDITY
#pragma multi_compile _ INTERPOLATION_OUTPUT
#define TEMPORAL_DENOISING_SAMPLECOUNT 16
#define TEMPORAL_STABILIZATION_SAMPLECOUNT 16
H_TEXTURE(_Radiance);
H_TEXTURE(_NormalDepth);
H_TEXTURE(_AmbientOcclusion);
H_TEXTURE(_SpatialGuidance);
H_TEXTURE(_RadianceReprojected);
H_TEXTURE(_TemporalInvalidity);
H_TEXTURE(_SamplecountReprojected);
H_TEXTURE(_SpatialOcclusion);
H_RW_TEXTURE(float4, _Radiance_TemporalOutput);
H_RW_TEXTURE(float3, _Radiance_SpatialOutput);
H_RW_TEXTURE(float3, _Radiance_Output);
H_RW_TEXTURE(float, _Samplecount_Output);
H_RW_TEXTURE(uint2, _RadianceNormalDepth_Output);
StructuredBuffer<float3> _PointDistribution;
RWStructuredBuffer<float3> _PointDistribution_Output;
float _FilterRadius;
float _FilterAdaptivity;
// ------------------------ TEMPORAL FUNCTIONS -------------------------
float3 DirectClipToAABB(float3 History, float3 Min, float3 Max)
{
float3 Center = 0.5 * (Max + Min);
float3 Extents = 0.5 * (Max - Min);
float3 Offset = History - Center;
float3 Vunit = Offset.xyz / Extents.xyz;
float3 AbsUnit = abs(Vunit);
float MaxUnit = max(max(AbsUnit.x, AbsUnit.y), AbsUnit.z);
if (MaxUnit > 1.0) return Center + (Offset / MaxUnit);
else return History;
}
// ------------------------ POINT DISTRIBUTION BUFFER --------------------
[numthreads(128, 1, 1)]
void PointDistributionFill(uint pixCoord : SV_DispatchThreadID)
{
// _PointDistribution_Output[pixCoord.x] = HSampleDiskCubic(GetLDSequenceSampleFloat(pixCoord.x, 0), GetLDSequenceSampleFloat(pixCoord.x, 1));
static const float3 k_PoissonDiskSamples[8] =
{
// https://www.desmos.com/calculator/abaqyvswem
float3( -1.00 , 0.00 , 1.0 ),
float3( 0.00 , 1.00 , 1.0 ),
float3( 1.00 , 0.00 , 1.0 ),
float3( 0.00 , -1.00 , 1.0 ),
float3( -0.25 * sqrt(2.0) , 0.25 * sqrt(2.0) , 0.5 ),
float3( 0.25 * sqrt(2.0) , 0.25 * sqrt(2.0) , 0.5 ),
float3( 0.25 * sqrt(2.0) , -0.25 * sqrt(2.0) , 0.5 ),
float3( -0.25 * sqrt(2.0) , -0.25 * sqrt(2.0) , 0.5 )
};
int i = pixCoord;
uint JitterIndex = (uint(_FrameCount) % 8) + i;
float2 SampleJitter = SampleUnitDisk(JitterIndex) * 0.15;
// 8 offsets with jitter
if (pixCoord.x < 8)
{
float3 Sample = k_PoissonDiskSamples[i];
Sample.xy += SampleJitter;
_PointDistribution_Output[i] = Sample;
}
// 8 offsets without jitter
if (pixCoord.x >= 8 && pixCoord.x < 16)
{
_PointDistribution_Output[i] = k_PoissonDiskSamples[i - 8];
}
}
// ------------------------ TEMPORAL ACCUMULATION ------------------------
[numthreads(8, 8, 1)]
void TemporalAccumulation(uint3 pixCoord : SV_DispatchThreadID)
{
float3 Radiance = H_LOAD(_Radiance, pixCoord.xy).xyz;
float3 RadianceHistory = H_LOAD(_RadianceReprojected, pixCoord.xy).xyz;
float3 Moment1 = Radiance;
float3 Moment2 = Radiance * Radiance;
float WeightTotal = 1.0;
float CenterLuminance = Luminance(Radiance);
float MaxLuma = -9999; float MinLuma = 9999;
float3 MaxLumaSample, MinLumaSample;
const static int2 SampleOffsets[8] = {int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1), int2(1, -1), int2(-1, 1), int2(-1, -1), int2(1, 1)};
for (int i = 0; i < 8; i++)
{
int2 Offset = SampleOffsets[i];
float Weight = exp(-3.0 * float(Offset.x * Offset.x + Offset.y * Offset.y) / 4.0f);
float3 Sample = H_LOAD(_Radiance, pixCoord.xy + Offset * 1).xyz;
Moment1 += Sample * Weight;
Moment2 += Sample * Sample * Weight;
WeightTotal += Weight;
float SampleLuma = Luminance(Sample);
if (MaxLuma < SampleLuma) { MaxLuma = SampleLuma; MaxLumaSample = Sample; }
if (MinLuma > SampleLuma) { MinLuma = SampleLuma; MinLumaSample = Sample; }
}
Moment1 /= WeightTotal;
Moment2 /= WeightTotal;
float3 StdDev = sqrt(max(0.0, Moment2 - Moment1 * Moment1));
float2 TemporalInvalidity = 1;
if (USE_TEMPORAL_INVALIDITY)
{
TemporalInvalidity = H_LOAD(_TemporalInvalidity, pixCoord.xy).xy;
TemporalInvalidity.x = pow(TemporalInvalidity.x, 5);
TemporalInvalidity.y = TemporalInvalidity.y < 0.95f ? 0 : TemporalInvalidity.y;
}
float ClampBoxSize = 0.5;
float ClampBoxMultiplier = lerp(1, 5, TemporalInvalidity.x);
float3 Min = lerp(Radiance, Moment1, ClampBoxSize * ClampBoxSize) - StdDev * ClampBoxSize * ClampBoxMultiplier;
float3 Max = lerp(Radiance, Moment1, ClampBoxSize * ClampBoxSize) + StdDev * ClampBoxSize * ClampBoxMultiplier;
RadianceHistory = DirectClipToAABB(RadianceHistory, Min, Max);
float SamplecountReprojected = H_LOAD(_SamplecountReprojected, pixCoord.xy).x;
float Samplecount = min(TEMPORAL_DENOISING_SAMPLECOUNT, SamplecountReprojected + 1);
float TemporalWeight = 1.0f - (1.0f / float(Samplecount));
if (ENABLE_RCRS_FILTER)
{
if (CenterLuminance >= MinLuma && CenterLuminance <= MaxLuma) { Radiance = Radiance;}
else if (CenterLuminance > MaxLuma) { Radiance = MaxLumaSample; }
else { Radiance = MinLumaSample; }
}
if (ENABLE_EXPOSURE_CONTROL)
{
float ExposurePrevious = HGetPreviousExposureMultiplier;
float ExposureCurrent = HGetCurrentExposureMultiplier;
float ExposureRatio = (ExposurePrevious * ExposureCurrent) != 0.0 ? ExposureCurrent / ExposurePrevious : 100.0;
if (max(ExposureRatio, 1.0 / ExposureRatio) > 2.0)
TemporalWeight = 0;
}
Radiance = lerp(Radiance, RadianceHistory, TemporalWeight * TemporalInvalidity.y * ENABLE_TEMPORAL_DENOISING);
_Radiance_TemporalOutput[H_COORD(pixCoord.xy)] = float4(Radiance, 0);
_Radiance_SpatialOutput[H_COORD(pixCoord.xy)] = Radiance;
_Samplecount_Output[H_COORD(pixCoord.xy)] = Samplecount;
}
// ------------------------ TEMPORAL STABILIZATION ------------------------
[numthreads(8, 8, 1)]
void TemporalStabilization(uint3 pixCoord : SV_DispatchThreadID)
{
float3 Radiance = H_LOAD(_Radiance, pixCoord.xy).xyz;
float4 RadianceSamplecountHistory = H_LOAD(_RadianceReprojected, pixCoord.xy);
float3 Moment1 = Radiance;
float3 Moment2 = Radiance * Radiance;
float WeightTotal = 1.0;
const static int2 SampleOffsets[8] = {int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1), int2(1, -1), int2(-1, 1), int2(-1, -1), int2(1, 1)};
for (int i = 0; i < 8; i++)
{
int2 Offset = SampleOffsets[i];
float Weight = exp(-3.0 * float(Offset.x * Offset.x + Offset.y * Offset.y) / 4.0f);
float3 Sample = H_LOAD(_Radiance, pixCoord.xy + Offset * 1).xyz;
Moment1 += Sample * Weight;
Moment2 += Sample * Sample * Weight;
WeightTotal += Weight;
}
Moment1 /= WeightTotal;
Moment2 /= WeightTotal;
float3 StdDev = sqrt(max(0.0, Moment2 - Moment1 * Moment1));
float2 TemporalInvalidity = 1;
if (USE_TEMPORAL_INVALIDITY)
{
TemporalInvalidity = H_LOAD(_TemporalInvalidity, pixCoord.xy / _HScaleFactorSSGI).xy;
TemporalInvalidity.x = pow(TemporalInvalidity.x, 5);
TemporalInvalidity.y = TemporalInvalidity.y < 0.95f ? 0 : TemporalInvalidity.y;
}
float ClampBoxSize = 0.5;
float ClampBoxMultiplier = lerp(3, 6, TemporalInvalidity.x * 2);
float3 Min = lerp(Radiance, Moment1, ClampBoxSize * ClampBoxSize) - StdDev * ClampBoxSize * ClampBoxMultiplier;
float3 Max = lerp(Radiance, Moment1, ClampBoxSize * ClampBoxSize) + StdDev * ClampBoxSize * ClampBoxMultiplier;
RadianceSamplecountHistory.xyz = DirectClipToAABB(RadianceSamplecountHistory.xyz, Min, Max);
float Samplecount = min(TEMPORAL_STABILIZATION_SAMPLECOUNT, RadianceSamplecountHistory.w + 1);
float TemporalWeight = 1.0f - (1.0f / float(Samplecount));
if (ENABLE_EXPOSURE_CONTROL)
{
float ExposurePrevious = HGetPreviousExposureMultiplier;
float ExposureCurrent = HGetCurrentExposureMultiplier;
float ExposureRatio = (ExposurePrevious * ExposureCurrent) != 0.0 ? ExposureCurrent / ExposurePrevious : 100.0;
if (max(ExposureRatio, 1.0 / ExposureRatio) > 2.0)
TemporalWeight = 0;
}
Radiance = lerp(Radiance, RadianceSamplecountHistory.xyz, TemporalWeight * TemporalInvalidity.y * ENABLE_TEMPORAL_STABILIZATION);
_Radiance_TemporalOutput[H_COORD(pixCoord.xy)] = float4(Radiance.xyz, Samplecount.x);
}
// ------------------------ SPATIAL FILTER ------------------------
[numthreads(8, 8, 1)]
void SpatialFilter(uint3 pixCoord : SV_DispatchThreadID)
{
uint2 pixCoordUnscaled = GetUnscaledCoords(pixCoord.xy);
int2 pixCoordMax = _ScreenSize.xy / _HScaleFactorSSGI.xx - 1;
float2 pixCoordNDC = (float2(pixCoordUnscaled) + 0.5f) * _ScreenSize.zw;
uint NormalDepthPacked = asuint(H_LOAD(_NormalDepth, pixCoord.xy).x);
float4 NormalDepth = UnpackNormalDepth(NormalDepthPacked);
if (NormalDepth.w <= UNITY_RAW_FAR_CLIP_VALUE) { return; }
float3 Radiance = H_LOAD(_Radiance, pixCoord.xy).xyz;
Radiance = SpatialDenoisingTonemap(Radiance);
float DepthCetnerLinear = H_LINEAR_EYE_DEPTH(NormalDepth.w);
float3 NormalCenterVS = H_TRANSFORM_WORLD_TO_VIEW_NORMAL(NormalDepth.xyz);
float3 PositionCenterWS = H_COMPUTE_POSITION_WS(pixCoordNDC, NormalDepth.w, H_MATRIX_I_VP);
float3 PositionCenterVS = ComputeFastViewSpacePosition(pixCoordNDC, NormalDepth.w, DepthCetnerLinear);
float4 NormalPlaneVS = float4(NormalCenterVS.xyz, dot(PositionCenterVS, NormalCenterVS.xyz));
float SpatialOcclusionCenter = H_LOAD(_SpatialOcclusion, pixCoord.xy).x;
uint2 SpatialGuidancePacked = asuint(H_LOAD(_SpatialGuidance, pixCoord.xy).xy);
float AdaptiveFilterScale = UnpackAmbientOcclusion(SpatialGuidancePacked.y);
float AdaptivePlaneWeight = lerp(500.0f, 100.0f, AdaptiveFilterScale);
uint SpatialGuidance = SpatialGuidancePacked.x;
float FilterRadius = _FilterRadius / 2.0f;
#ifdef SECOND_PASS
FilterRadius = _FilterRadius;
#endif
float MinFilterRadius = lerp(0.01f, 0.1f, H_LOAD(_AmbientOcclusion, pixCoord.xy).x);
FilterRadius = max(MinFilterRadius, lerp(FilterRadius, FilterRadius * AdaptiveFilterScale, _FilterAdaptivity));
float DistanceToPoint = length(H_GET_ABSOLUTE_POSITION_WS(PositionCenterWS) - H_GET_CAMERA_POSITION_WS());
float3x3 OrthoBasis = HGetLocalFrame(NormalDepth.xyz);
float RadiusScale = lerp(5.0f, 50.0f, saturate(DistanceToPoint / 500.0f));
float Radius = DistanceToPoint * FilterRadius / RadiusScale;
float Sigma = 0.9f * Radius;
float WeightAccumulated = 1;
float2 PerPixelJitter;
PerPixelJitter.x = HInterleavedGradientNoise(pixCoord.xy, 0);
PerPixelJitter.y = HInterleavedGradientNoise(pixCoord.yx, 1);
PerPixelJitter = (PerPixelJitter * 2 - 1) * 0.15;
UNITY_UNROLL
for (int i = 0; i < 8; i++)
{
float GuidanceAdaptivity = 1 - (0.75 * ((SpatialGuidance >> i) & 0x1));
float2 Point = (_PointDistribution[i + 8].xy + PerPixelJitter) * Radius * GuidanceAdaptivity;
float3 PositionPointWS = PositionCenterWS + OrthoBasis[0] * Point.x + OrthoBasis[1] * Point.y;
float2 SampleCoordNDC = H_COMPUTE_NDC_Z(PositionPointWS, H_MATRIX_VP).xy;
int2 SampleCoord = SampleCoordNDC * _ScreenSize.xy / _HScaleFactorSSGI;
int2 Overshoot = max(SampleCoord - pixCoordMax, 0);
SampleCoord -= 2 * Overshoot;
uint NormalDepthPacked = asuint(H_LOAD(_NormalDepth, SampleCoord).x);
float4 NormalDepthSample = UnpackNormalDepth(NormalDepthPacked);
float SpatialOcclusionSample = H_LOAD(_SpatialOcclusion, SampleCoord).x;
float3 PositionSampleVS = ComputeFastViewSpacePosition(SampleCoordNDC, NormalDepthSample.w, H_LINEAR_EYE_DEPTH(NormalDepthSample.w));
float PlaneWeight = ProbePlaneWeighting(NormalPlaneVS, PositionSampleVS, DepthCetnerLinear, AdaptivePlaneWeight);
float NormalWeight = saturate(dot(NormalDepth.xyz, NormalDepthSample.xyz));
float GaussianWeight = GaussianWeighting(length(Point), Sigma);
float OcclusionWeight = 1.0f;
if (USE_SPATIAL_OCCLUSION)
{ OcclusionWeight = exp2(-max(5, 10 * (1 - SpatialOcclusionCenter)) * abs(SpatialOcclusionCenter - SpatialOcclusionSample)); }
float SampleWeight = NormalWeight * PlaneWeight * OcclusionWeight * GaussianWeight * ENABLE_SPATIAL_DENOISING;
WeightAccumulated += SampleWeight;
float3 RadianceSample = H_LOAD(_Radiance, SampleCoord).xyz;
RadianceSample = SpatialDenoisingTonemap(RadianceSample);
Radiance += RadianceSample * SampleWeight;
}
Radiance /= WeightAccumulated;
Radiance = SpatialDenoisingTonemapInverse(Radiance);
if (AnyIsNaN(Radiance) || AnyIsInf(Radiance))
Radiance = 0;
// Radiance = AdaptiveFilterScale;
#if SECOND_PASS
if (INTERPOLATION_OUTPUT) _RadianceNormalDepth_Output[H_COORD(pixCoord.xy)] = uint2(PackToR11G11B10f(Radiance), PackNormalDepth(NormalDepth.xyz, NormalDepth.w));
else _Radiance_Output[H_COORD(pixCoord.xy)] = Radiance;
#else
_Radiance_Output[H_COORD(pixCoord.xy)] = Radiance;
#endif
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 4e9dcd36d21eeca4a93de42f47c188ce
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HDenoiserSSGI.compute
uploadId: 840002

View File

@@ -0,0 +1,115 @@
#pragma kernel GenerateDepthPyramid
#include "../Includes/HCommonSSGI.hlsl"
H_RW_TEXTURE(float, _DepthPyramid_OutputMIP0);
H_RW_TEXTURE(float, _DepthPyramid_OutputMIP1);
H_RW_TEXTURE(float, _DepthPyramid_OutputMIP2);
H_RW_TEXTURE(float, _DepthPyramid_OutputMIP3);
H_RW_TEXTURE(float, _DepthPyramid_OutputMIP4);
groupshared float MipDepthLDS[8][8];
float DepthFilter(float4 DepthSamples)
{
float DepthFiltered = 0;
// Change filtering to max near sky pixels, to grab more skylight
if (any(DepthSamples <= 0))
DepthFiltered = max(max(DepthSamples.x, DepthSamples.y), max(DepthSamples.z, DepthSamples.w));
DepthFiltered = min(min(DepthSamples.x, DepthSamples.y), min(DepthSamples.z, DepthSamples.w));
return DepthFiltered;
}
// ------------------------ MIP LEVEL 0-4 GENERATIONS ------------------------
[numthreads(8, 8, 1)]
void GenerateDepthPyramid(uint3 pixCoord : SV_DispatchThreadID, uint2 groupThreadID : SV_GroupThreadID)
{
const uint2 originalCoord = pixCoord.xy;
pixCoord *= 2;
float4 Depth;
Depth.x = any(pixCoord.xy + uint2(0,0) >= uint2(_ScreenSize.xy)) ? 0 : HBUFFER_DEPTH(pixCoord.xy + uint2(0,0)).x;
Depth.y = any(pixCoord.xy + uint2(1,0) >= uint2(_ScreenSize.xy)) ? 0 : HBUFFER_DEPTH(pixCoord.xy + uint2(1,0)).x;
Depth.z = any(pixCoord.xy + uint2(0,1) >= uint2(_ScreenSize.xy)) ? 0 : HBUFFER_DEPTH(pixCoord.xy + uint2(0,1)).x;
Depth.w = any(pixCoord.xy + uint2(1,1) >= uint2(_ScreenSize.xy)) ? 0 : HBUFFER_DEPTH(pixCoord.xy + uint2(1,1)).x;
Depth.x = Depth.x >= 0.9999f ? 0 : Depth.x;
Depth.y = Depth.y >= 0.9999f ? 0 : Depth.y;
Depth.z = Depth.z >= 0.9999f ? 0 : Depth.z;
Depth.w = Depth.w >= 0.9999f ? 0 : Depth.w;
if (HBUFFER_RENDER_LAYER_MASK(pixCoord.xy + uint2(0,0)) & _ExcludeCastingLayerMaskSSGI) Depth.x = -1;
if (HBUFFER_RENDER_LAYER_MASK(pixCoord.xy + uint2(1,0)) & _ExcludeCastingLayerMaskSSGI) Depth.y = -1;
if (HBUFFER_RENDER_LAYER_MASK(pixCoord.xy + uint2(0,1)) & _ExcludeCastingLayerMaskSSGI) Depth.z = -1;
if (HBUFFER_RENDER_LAYER_MASK(pixCoord.xy + uint2(1,1)) & _ExcludeCastingLayerMaskSSGI) Depth.w = -1;
// Write to MIP0
_DepthPyramid_OutputMIP0[H_COORD(pixCoord.xy + uint2(0, 0))] = Depth.x;
_DepthPyramid_OutputMIP0[H_COORD(pixCoord.xy + uint2(1, 0))] = Depth.y;
_DepthPyramid_OutputMIP0[H_COORD(pixCoord.xy + uint2(0, 1))] = Depth.z;
_DepthPyramid_OutputMIP0[H_COORD(pixCoord.xy + uint2(1, 1))] = Depth.w;
float DepthMIP1 = DepthFilter(Depth.xyzw);
// Write to MIP1
_DepthPyramid_OutputMIP1[H_COORD(originalCoord)] = DepthMIP1;
MipDepthLDS[groupThreadID.x][groupThreadID.y] = DepthMIP1;
GroupMemoryBarrierWithGroupSync();
// Write to MIP2
[branch]
if (all((groupThreadID.xy % int2(2, 2)) == 0))
{
float4 Depth;
Depth.x = MipDepthLDS[groupThreadID.x + 0][groupThreadID.y + 0];
Depth.y = MipDepthLDS[groupThreadID.x + 1][groupThreadID.y + 0];
Depth.z = MipDepthLDS[groupThreadID.x + 0][groupThreadID.y + 1];
Depth.w = MipDepthLDS[groupThreadID.x + 1][groupThreadID.y + 1];
float DepthMIP2 = DepthFilter(Depth.xyzw);
_DepthPyramid_OutputMIP2[H_COORD(originalCoord / 2)] = DepthMIP2;
MipDepthLDS[groupThreadID.x][groupThreadID.y] = DepthMIP2;
}
GroupMemoryBarrierWithGroupSync();
// Write to MIP3
[branch]
if (all((groupThreadID.xy % int2(4, 4)) == 0))
{
float4 Depth;
Depth.x = MipDepthLDS[groupThreadID.x + 0][groupThreadID.y + 0];
Depth.y = MipDepthLDS[groupThreadID.x + 2][groupThreadID.y + 0];
Depth.z = MipDepthLDS[groupThreadID.x + 0][groupThreadID.y + 2];
Depth.w = MipDepthLDS[groupThreadID.x + 2][groupThreadID.y + 2];
float DepthMIP3 = DepthFilter(Depth.xyzw);
_DepthPyramid_OutputMIP3[H_COORD(originalCoord / 4)] = DepthMIP3;
MipDepthLDS[groupThreadID.x][groupThreadID.y] = DepthMIP3;
}
GroupMemoryBarrierWithGroupSync();
// Write to MIP4
[branch]
if (all((groupThreadID.xy % int2(8, 8)) == 0))
{
float4 Depth;
Depth.x = MipDepthLDS[groupThreadID.x + 0][groupThreadID.y + 0];
Depth.y = MipDepthLDS[groupThreadID.x + 4][groupThreadID.y + 0];
Depth.z = MipDepthLDS[groupThreadID.x + 0][groupThreadID.y + 4];
Depth.w = MipDepthLDS[groupThreadID.x + 4][groupThreadID.y + 4];
float DepthMIP4 = DepthFilter(Depth.xyzw);
_DepthPyramid_OutputMIP4[H_COORD(originalCoord / 8)] = DepthMIP4;
}
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 902b874ab8f513d4cbe65d1bfb2e9d24
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HDepthPyramid.compute
uploadId: 840002

View File

@@ -0,0 +1,215 @@
#pragma kernel Interpolation
#include "../Includes/HReservoirSSGI.hlsl"
#pragma multi_compile _ _GBUFFER_NORMALS_OCT
#define INTERPOLATION_LANCZOS 0
#define INTERPOLATION_SAMPLECOUNT 5
#define NORMAL_REJECTION 1
H_TEXTURE(_RadianceNormalDepth);
H_RW_TEXTURE(float3, _Radiance_Output);
H_RW_TEXTURE(uint, _NormalDepth_HistoryOutput);
// ------------------------ INTERPOLATION STRUCTS -----------------------
struct CenterPixelData
{
float4 Plane;
float3 Normal;
float DepthLinear;
};
// ------------------------ LANCZOS-SPECIFIC FUNCTIONS -------------------------
float4 ProcessSample(float2 Offset, float4 DirectionLength, float2 LobeClip, float4 RadianceSample)
{
float2 RotatedOffset = float2(dot(Offset, DirectionLength.xy), dot(Offset, float2(-DirectionLength.y, DirectionLength.x))) * DirectionLength.zw;
float DistanceDouble = min(dot(RotatedOffset, RotatedOffset), LobeClip.y);
float Window = 0.4 * DistanceDouble - 1.0;
float Base = LobeClip.x * DistanceDouble - 1.0;
Window *= Window;
Base *= Base;
Window = 1.5625 * Window - 0.5625;
float Weight = Window * Base * RadianceSample.w;
float4 AccumulatedData;
AccumulatedData.xyz = RadianceSample.xyz * Weight;
AccumulatedData.w = Weight;
return AccumulatedData;
}
void GatherData(inout float2 Direction, inout float Length, float Weight, float SampleA, float SampleB, float SampleC, float SampleD, float SampleE)
{
float LengthX = max(abs(SampleD.x - SampleC.x), abs(SampleC.x - SampleB.x));
float DirectionX = SampleD.x - SampleB.x;
Direction.x += DirectionX * Weight;
LengthX = clamp(abs(DirectionX)/ LengthX, 0.0, 1.0);
LengthX *= LengthX;
Length += LengthX * Weight;
float LengthY = max(abs(SampleE.x - SampleC.x), abs(SampleC.x - SampleA.x));
float DirectionY = SampleE.x - SampleA.x;
Direction.y += DirectionY * Weight;
LengthY = clamp(abs(DirectionY) / LengthY, 0.0, 1.0);
LengthY *= LengthY;
Length += LengthY * Weight;
}
void AdaptiveLanczosData(float Samples[12], float2 PixelCoord, inout float4 DirLength, inout float2 LobeClip)
{
float Length = 0;
float2 Direction = 0;
GatherData(Direction, Length, (1.0 -PixelCoord.x) * (1.0 -PixelCoord.y), Samples[0], Samples[5], Samples[4], Samples[9], Samples[3]);
GatherData(Direction, Length, PixelCoord.x * (1.0 -PixelCoord.y), Samples[1], Samples[4], Samples[9], Samples[8], Samples[6]);
GatherData(Direction, Length, (1.0 -PixelCoord.x) * PixelCoord.y, Samples[4], Samples[2], Samples[3], Samples[6], Samples[11]);
GatherData(Direction, Length, PixelCoord.x * PixelCoord.y, Samples[9], Samples[3], Samples[6], Samples[7], Samples[10]);
float2 DirectionDouble = Direction * Direction;
float DirectionRadial = DirectionDouble.x + DirectionDouble.y;
bool IsNearZero = DirectionRadial < (1.0 / 32768.0);
DirectionRadial = rsqrt(DirectionRadial);
DirectionRadial = IsNearZero ? 1.0 : DirectionRadial;
Direction.x = IsNearZero ? 1.0 : Direction.x;
Direction *= float2(DirectionRadial, DirectionRadial);
Length = Length * 0.5;
Length *= Length;
float Stretch = dot(Direction,Direction) / (max(abs(Direction.x), abs(Direction.y)));
float2 LengthDouble = float2(1.0 + (Stretch -1.0) * Length, 1.0 - 0.5 * Length);
float NegativeLobe = 0.5 - 0.29 * Length;
float Clip = 1.0 / NegativeLobe;
DirLength = float4(Direction.xy, LengthDouble.xy);
LobeClip = float2(NegativeLobe, Clip);
}
float4 InterpolationSample(CenterPixelData CenterData, int2 SampleCoord, inout float LumaSample)
{
uint2 RadianceNormalDepthPacked = asuint(H_LOAD(_RadianceNormalDepth, SampleCoord).xy);
float3 RadianceSample = UnpackFromR11G11B10f(RadianceNormalDepthPacked.x);
float4 NormalDepthSample = UnpackNormalDepth(RadianceNormalDepthPacked.y);
float3 WorldPos = ComputeFastViewSpacePosition((SampleCoord * _HScaleFactorSSGI + 0.5) / _ScreenSize.xy, NormalDepthSample.w, H_LINEAR_EYE_DEPTH(NormalDepthSample.w));
float PlaneWeight = ProbePlaneWeighting(CenterData.Plane, WorldPos, CenterData.DepthLinear, 5000);
float NormalWeight = 1;
if (NORMAL_REJECTION)
NormalWeight = saturate(dot(CenterData.Normal, NormalDepthSample.xyz));
LumaSample = Luminance(RadianceSample) * PlaneWeight * NormalWeight;
return float4(RadianceSample, PlaneWeight * NormalWeight);
}
// ------------------------ INTERPOLATION -----------------------
[numthreads(8, 8, 1)]
void Interpolation(uint3 pixCoord : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, uint groupID : SV_GroupID)
{
const float DepthCenter = HBUFFER_DEPTH(pixCoord.xy).x;
if (DepthCenter <= 1e-7) { return; }
const uint RadiancePacked = asuint(H_LOAD(_RadianceNormalDepth, floor(pixCoord.xy / _HScaleFactorSSGI)).x);
float3 RadianceCenter = UnpackFromR11G11B10f(RadiancePacked);
const float3 NormalCenterWS = HBUFFER_NORMAL_WS(pixCoord.xy).xyz;
const float3 NormalCenterVS = H_TRANSFORM_WORLD_TO_VIEW_NORMAL(NormalCenterWS);
const float3 WorldPosCenter = ComputeFastViewSpacePosition((pixCoord.xy + 0.5f) * _ScreenSize.zw, DepthCenter, H_LINEAR_EYE_DEPTH(DepthCenter));
const int2 SampleOffsets[9] = {int2(0, 0), int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1), int2(1, -1), int2(-1, 1), int2(-1, -1), int2(1, 1)};
CenterPixelData CenterData;
CenterData.Normal = NormalCenterWS;
CenterData.Plane = float4(NormalCenterVS, dot(WorldPosCenter, NormalCenterVS));
CenterData.DepthLinear = H_LINEAR_EYE_DEPTH(DepthCenter);
float3 InterpolatedRadiance = 0;
uint Count = 0;
if (INTERPOLATION_LANCZOS)
{
float2 LanczosOffset = float2(pixCoord.xy) / _HScaleFactorSSGI; LanczosOffset -= floor(LanczosOffset);
int2 LanczosOffsetRounded = -floor(LanczosOffset );
int2 DownscaledRes = floor(pixCoord.xy / _HScaleFactorSSGI);
float4 Samples[12];
float LumaSamples[12];
Samples[0] = InterpolationSample(CenterData, DownscaledRes + int2( 0,-1) + LanczosOffsetRounded, LumaSamples[0] );
Samples[1] = InterpolationSample(CenterData, DownscaledRes + int2( 1,-1) + LanczosOffsetRounded, LumaSamples[1] );
Samples[2] = InterpolationSample(CenterData, DownscaledRes + int2(-1, 1) + LanczosOffsetRounded, LumaSamples[2] );
Samples[3] = InterpolationSample(CenterData, DownscaledRes + int2( 0, 1) + LanczosOffsetRounded, LumaSamples[3] );
Samples[4] = InterpolationSample(CenterData, DownscaledRes + int2( 0, 0) + LanczosOffsetRounded, LumaSamples[4] );
Samples[5] = InterpolationSample(CenterData, DownscaledRes + int2(-1, 0) + LanczosOffsetRounded, LumaSamples[5] );
Samples[6] = InterpolationSample(CenterData, DownscaledRes + int2( 1, 1) + LanczosOffsetRounded, LumaSamples[6] );
Samples[7] = InterpolationSample(CenterData, DownscaledRes + int2( 2, 1) + LanczosOffsetRounded, LumaSamples[7] );
Samples[8] = InterpolationSample(CenterData, DownscaledRes + int2( 2, 0) + LanczosOffsetRounded, LumaSamples[8] );
Samples[9] = InterpolationSample(CenterData, DownscaledRes + int2( 1, 0) + LanczosOffsetRounded, LumaSamples[9] );
Samples[10] = InterpolationSample(CenterData, DownscaledRes + int2( 1, 2) + LanczosOffsetRounded, LumaSamples[10]);
Samples[11] = InterpolationSample(CenterData, DownscaledRes + int2( 0, 2) + LanczosOffsetRounded, LumaSamples[11]);
float4 DirLength; float2 LobeClip;
AdaptiveLanczosData(LumaSamples, LanczosOffset, DirLength, LobeClip);
float3 MinNeighbourhood = min(min(Samples[4].xyz, Samples[9].xyz), min(Samples[3].xyz, Samples[6].xyz));
float3 MaxNeighbourhood = max(max(Samples[4].xyz, Samples[9].xyz), max(Samples[3].xyz, Samples[6].xyz));
float4 AccumulatedData = 0;
AccumulatedData += ProcessSample(float2( 0,-1) -LanczosOffset, DirLength, LobeClip, Samples[0]);
AccumulatedData += ProcessSample(float2( 1,-1) -LanczosOffset, DirLength, LobeClip, Samples[1]);
AccumulatedData += ProcessSample(float2(-1, 1) -LanczosOffset, DirLength, LobeClip, Samples[2]);
AccumulatedData += ProcessSample(float2( 0, 1) -LanczosOffset, DirLength, LobeClip, Samples[3]);
AccumulatedData += ProcessSample(float2( 0, 0) -LanczosOffset, DirLength, LobeClip, Samples[4]);
AccumulatedData += ProcessSample(float2(-1, 0) -LanczosOffset, DirLength, LobeClip, Samples[5]);
AccumulatedData += ProcessSample(float2( 1, 1) -LanczosOffset, DirLength, LobeClip, Samples[6]);
AccumulatedData += ProcessSample(float2( 2, 1) -LanczosOffset, DirLength, LobeClip, Samples[7]);
AccumulatedData += ProcessSample(float2( 2, 0) -LanczosOffset, DirLength, LobeClip, Samples[8]);
AccumulatedData += ProcessSample(float2( 1, 0) -LanczosOffset, DirLength, LobeClip, Samples[9]);
AccumulatedData += ProcessSample(float2( 1, 2) -LanczosOffset, DirLength, LobeClip, Samples[10]);
AccumulatedData += ProcessSample(float2( 0, 2) -LanczosOffset, DirLength, LobeClip, Samples[11]);
AccumulatedData.w = max(AccumulatedData.w, 0.1f);
AccumulatedData.xyz = AccumulatedData.w > 0 ? AccumulatedData.xyz / AccumulatedData.w : RadianceCenter;
InterpolatedRadiance = min(MaxNeighbourhood, max(MinNeighbourhood, AccumulatedData.xyz ));
}
else
{
static const int2 SampleOffsets[9] = {int2(0, 0), int2(1, 0), int2(0, 1), int2(-1, 0), int2(0, -1), int2(1, -1), int2(-1, 1), int2(-1, -1), int2(1, 1)};
float2 pixCoordDownscaled = float2(pixCoord.xy) / _HScaleFactorSSGI;
float2 pixCoordDownscaledSnapped = floor(pixCoordDownscaled + 0.5 / _HScaleFactorSSGI); // or round(pixCoordDownscaled) ?
float SigmaScale = pow(abs(_HScaleFactorSSGI - 1.0), 0.2);
float Sigma = lerp(0.3, 0.65, SigmaScale);
float AccumulatedWeight = 0;
for (int i = 0; i < 9; i++)
{
float2 SampleCoord = pixCoordDownscaledSnapped + SampleOffsets[i];
int2 SampleCoordSnapped = clamp(SampleCoord, 0 , _ScreenSize.xy / _HScaleFactorSSGI - 1);
float Unused;
float4 Sample = InterpolationSample(CenterData, SampleCoordSnapped, Unused);
Sample.xyz = SpatialDenoisingTonemap(Sample.xyz);
float2 Delta = (pixCoordDownscaled - SampleCoord);
float DistanceSq = dot(Delta, Delta);
float Weight = exp(-DistanceSq / (2.0 * Sigma * Sigma));
Sample.w *= Weight;
InterpolatedRadiance += Sample.xyz * Sample.w;
AccumulatedWeight += Sample.w;
}
InterpolatedRadiance = AccumulatedWeight > 0 ? SpatialDenoisingTonemapInverse((InterpolatedRadiance) / AccumulatedWeight) : RadianceCenter;
}
_Radiance_Output[H_COORD(pixCoord.xy)] = InterpolatedRadiance;
_NormalDepth_HistoryOutput[H_COORD(pixCoord.xy)] = PackNormalDepth(NormalCenterWS, DepthCenter);
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: d9e32df3caf2788448cd612608bce346
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HInterpolationSSGI.compute
uploadId: 840002

View File

@@ -0,0 +1,231 @@
#pragma kernel TraceSSGI
#pragma kernel MaskExclude
#pragma multi_compile _ PROBE_VOLUMES_L1 PROBE_VOLUMES_L2
#pragma multi_compile _ FALLBACK_SKY FALLBACK_APV
#pragma multi_compile _ CHECKERBOARDING
// Tracing keywords
#pragma multi_compile _ REFINE_INTERSECTION
#pragma multi_compile _ HALF_STEP_VALIDATION
#pragma multi_compile _ FULL_RESOLUTION_DEPTH
#pragma multi_compile LINEAR_THICKNESS UNIFORM_THICKNESS
#define FALLBACK_STAGE 1
#include "../Includes/HRayMarchingSSGI.hlsl"
#include "../Includes/HFallbackSSGI.hlsl"
#include "../Headers/HDRP files/RaytracingSampling.hlsl"
#pragma multi_compile _ _GBUFFER_NORMALS_OCT
int _IndexXR;
int _RayCount;
float _SkyFallbackIntensity;
H_TEXTURE(_SampleCount);
H_RW_TEXTURE(float3, _Radiance_Output);
H_RW_TEXTURE(float, _AmbientOcclusion_Output);
RWStructuredBuffer<uint2> _TracingCoords;
RWStructuredBuffer<uint> _RayCounter;
// ------------------------ SSGI RENDER -------------------------
[numthreads(8, 8, 1)]
void TraceSSGI(uint3 pixCoord : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, uint groupID : SV_GroupID)
{
if (CHECKERBOARDING)
{
uint RayCounterOffsetVR = 0;
uint IndirectCoordOffsetVR = 0;
uint RayIndex = groupID * 64 + groupIndex;
if (RayIndex >= _RayCounter[1 + RayCounterOffsetVR])
return;
pixCoord.xy = _TracingCoords[RayIndex + IndirectCoordOffsetVR];
}
else
{
}
uint2 pixCoordUnscaled = GetUnscaledCoords(pixCoord.xy);
float2 pixCoordNDC = (float2(pixCoordUnscaled) + 0.5f) * _ScreenSize.zw;
float DepthCenter = HBUFFER_DEPTH(pixCoordUnscaled);
float3 NormalCenterWS = HBUFFER_NORMAL_WS(pixCoordUnscaled);
float3 PositionCenterWS = H_COMPUTE_POSITION_WS(pixCoordNDC, DepthCenter, H_MATRIX_I_VP);
float3 ViewDir = normalize(_WorldSpaceCameraPos - H_GET_ABSOLUTE_POSITION_WS(PositionCenterWS) );
if (DepthCenter <= UNITY_RAW_FAR_CLIP_VALUE)
{
_Reservoir_Output[H_COORD(pixCoord.xy)] = 0;
_AmbientOcclusion_Output[H_COORD(pixCoord.xy)] = 0;
return;
}
float3 Diffuse = GetReservoirDiffuse(pixCoordUnscaled);
uint ReservoirUpdateRandom = Hash3(uint3(pixCoord.xy, _FrameCount));
TemporalReservoir RadianceRes = (TemporalReservoir)0;
OcclusionReservoir OcclusionRes = (OcclusionReservoir)0;
const float StepJitter = Jitter((pixCoord.xy) * 2 - 1);
int FrameStep = 1; if (CHECKERBOARDING) { FrameStep = 2;}
float AmbientOcclusion = 0, AmbientOcclusionInvalidity = 0;
float3 DirectionsAccumulated = 0, BentNormal = 0, Radiance = 0;
// Tracing
for (int i = 0; i < _RayCount; i++)
{
int SampleIndex = ((floor(uint(_FrameCount) / FrameStep) % 16) * _RayCount + i);
float2 SampleRandom;
SampleRandom.x = GetBNDSequenceSample(pixCoord.xy, SampleIndex, 0);
SampleRandom.y = GetBNDSequenceSample(pixCoord.xy, SampleIndex, 1);
float3 RayDirectionWS = HSampleHemisphereCosine(SampleRandom.x, SampleRandom.y, NormalCenterWS);
DirectionsAccumulated += RayDirectionWS;
float3 RayOriginBiasedWS, RayStartPositionNDC, RayEndPositionNDC;
GetRayOriginAndDirectionNDC(_RayLength, DepthCenter, pixCoordNDC, PositionCenterWS, RayDirectionWS, NormalCenterWS, RayOriginBiasedWS, RayStartPositionNDC, RayEndPositionNDC);
float4 HitData = RayMarch(RayStartPositionNDC, RayEndPositionNDC, StepJitter);
float3 HitRadiance = 0;
float OcclusionDistance = 0, HitOcclusion = 0, HitDistance = 0;
if (HitData.w)
{
bool MovingHitPoint;
HitRadiance = UnpackColorHit(asuint(H_LOAD(_Color, HitData.xy * _ScreenSize.xy).x), MovingHitPoint);
float3 HitPosition = H_COMPUTE_POSITION_WS(HitData.xy, HitData.z, H_MATRIX_I_VP);
HitDistance = distance(PositionCenterWS, HitPosition);
//RayDirectionWS = normalize(HitPosition - PositionCenterWS);
float HitSurface = H_LOAD(g_HTraceDepthPyramidSSGI, HitData.xy * _ScreenSize.xy).x;
float HitSurfaceLinear = H_LINEAR_EYE_DEPTH(HitSurface);
float HitDepthLinear = H_LINEAR_EYE_DEPTH(HitData.z);
// Don't run hit validation on skybox hits
if (HitSurface > UNITY_RAW_FAR_CLIP_VALUE)
{
if (abs(HitDepthLinear - HitSurfaceLinear) > HitDistance + 0.05f * max(HitSurfaceLinear, 0.25f))
HitRadiance = 0;
if (dot(HBUFFER_NORMAL_WS(HitData.xy * _ScreenSize.xy), RayDirectionWS) > _BackfaceLighting)
HitRadiance = 0;
}
HitRadiance *= ExponentialFalloff(HitDistance, _RayLength);
if (HitDistance < 0.5)
AmbientOcclusion++;
float HitSamplecount = H_LOAD(_SampleCount, HitData.xy * _ScreenSize.xy / _HScaleFactorSSGI).x;
if (HitSamplecount < 2)
AmbientOcclusionInvalidity++;
if (HitDistance < 1.0)
{
OcclusionDistance = HitDistance;
HitOcclusion = MovingHitPoint ? 1 : 0;
}
}
Radiance += HitRadiance;
BentNormal += RayDirectionWS * (1 - HitData.w);
ReservoirUpdate(HitRadiance, RayDirectionWS, NormalCenterWS, HitDistance, HitData.w, Luminance(HitRadiance * Diffuse), 1, RadianceRes, ReservoirUpdateRandom);
ReservoirUpdate(HitOcclusion, RayDirectionWS, OcclusionDistance, HitOcclusion, 1, OcclusionRes, ReservoirUpdateRandom);
}
// Fallback
if (FALLBACK_APV || FALLBACK_SKY)
{
float3 FallbackRadiance = 0;
if (length(DirectionsAccumulated) > 0)
BentNormal /= length(DirectionsAccumulated);
float BentNormalOcclusion = length(BentNormal);
BentNormal = BentNormalOcclusion > 0 ? BentNormal / BentNormalOcclusion : NormalCenterWS;
// APV Fallback
#ifdef FALLBACK_APV
if (_EnableProbeVolumes)
{
if (PROBE_VOLUMES_L1 || PROBE_VOLUMES_L2)
{
float3 BakedAPV = EvaluateFallbackAPV(_APVParams, PositionCenterWS, BentNormal, H_GET_VIEW_DIRECTION_WS(PositionCenterWS), pixCoord.xy);
FallbackRadiance = BakedAPV * RadianceRes.M * BentNormalOcclusion;
}
}
#endif
// Sky Fallback
if (FALLBACK_SKY)
{
float3 Sky = EvaluateFallbackSky(BentNormal);
FallbackRadiance = Sky * RadianceRes.M * BentNormalOcclusion * _SkyFallbackIntensity;
}
FallbackRadiance = max(FallbackRadiance, 0);
ReservoirUpdate(FallbackRadiance, NormalCenterWS, false, Luminance(FallbackRadiance * Diffuse), 1 / float(_RayCount), RadianceRes, ReservoirUpdateRandom);
}
RadianceRes.W = RadianceRes.Wsum / max(RadianceRes.M * Luminance(RadianceRes.Color * Diffuse), 1e-7);
OcclusionRes.W = OcclusionRes.Wsum / max(OcclusionRes.M * OcclusionRes.Occlusion, 1e-7);
RadianceRes.OriginNormal = NormalCenterWS;
if (RadianceRes.HitFound == false) // && OcclusionRes.Distance > 0 && OcclusionRes.Occlusion > 0)
{
RadianceRes.Direction = OcclusionRes.Direction;
RadianceRes.Distance = OcclusionRes.Distance;
}
float InvalidityEncodingSign = AmbientOcclusionInvalidity > 0 ? -1 : 1;
_AmbientOcclusion_Output[H_COORD(pixCoord.xy)] = (1 - (AmbientOcclusion / float(_RayCount))) * InvalidityEncodingSign;
_Reservoir_Output[H_COORD(pixCoord.xy)] = PackTemporalReservoir(RadianceRes);
bool MovingHitPoint;
float3 HitRadiance = UnpackColorHit(asuint(H_LOAD(_Color, pixCoord.xy).x), MovingHitPoint);
//_Radiance_Output[H_COORD(pixCoord.xy)] =RadianceRes.Color * RadianceRes.W;
}
// ------------------------ MASK EXCLUDE [HDRP ONLY] -----------------------
[numthreads(8, 8, 1)]
void MaskExclude(uint3 pixCoord : SV_DispatchThreadID)
{
if (HBUFFER_RENDER_LAYER_MASK(pixCoord.xy) & _ExcludeReceivingLayerMaskSSGI)
{
float3 Radiance = 0;
#ifdef FALLBACK_APV
if (_EnableProbeVolumes)
{
if (PROBE_VOLUMES_L1 || PROBE_VOLUMES_L2)
{
float DepthCenter = HBUFFER_DEPTH(pixCoord.xy);
float3 NormalCenterWS = HBUFFER_NORMAL_WS(pixCoord.xy);
float3 PositionCenterWS = H_COMPUTE_POSITION_WS((pixCoord.xy + 0.5f) * _ScreenSize.zw, DepthCenter, H_MATRIX_I_VP);
Radiance = EvaluateFallbackAPV(_APVParams, PositionCenterWS, NormalCenterWS, H_GET_VIEW_DIRECTION_WS(PositionCenterWS), pixCoord.xy);
}
}
#elif FALLBACK_SKY
float3 NormalCenterWS = HBUFFER_NORMAL_WS(pixCoord.xy);
Radiance = EvaluateFallbackSky(NormalCenterWS);
#endif
_Radiance_Output[H_COORD(pixCoord.xy)] = Radiance;
}
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: a19906341f84b914a88dd6966db40d78
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HRenderSSGI.compute
uploadId: 840002

View File

@@ -0,0 +1,498 @@
#pragma kernel TemporalResampling
#pragma kernel FireflySuppression
#pragma kernel SpatialResampling SpatialResamplingValidation = SpatialResampling
#pragma kernel SpatialValidation SpatialResamplingValidation = SpatialValidation VALIDATION_STAGE
#include "../Includes/HRayMarchingSSGI.hlsl"
#include "../Headers/HSpaceTransforms.hlsl"
#include "../Headers/HDRP files/RaytracingSampling.hlsl"
#pragma multi_compile _ _GBUFFER_NORMALS_OCT
#pragma multi_compile _ CHECKERBOARDING
#pragma multi_compile _ VALIDATE_SPATIAL_OCCLUSION
#pragma multi_compile _ VALIDATE_TEMPORAL_LIGHTING
#pragma multi_compile _ VALIDATE_TEMPORAL_OCCLUSION
// Tracing keywords
#pragma multi_compile _ REFINE_INTERSECTION
#pragma multi_compile _ HALF_STEP_VALIDATION
#pragma multi_compile _ FULL_RESOLUTION_DEPTH
#pragma multi_compile LINEAR_THICKNESS UNIFORM_THICKNESS
#ifdef VALIDATION_STAGE
#define SECOND_SPATIAL_PASS 1
#else
#define SECOND_SPATIAL_PASS 0
#endif
#ifdef CHECKERBOARDING
#define RESTIR_HISTORY_MAX_LENGTH 32
#define RESTIR_HISTORY_MIN_LENGTH 1
#else
#define RESTIR_HISTORY_MAX_LENGTH 32
#define RESTIR_HISTORY_MIN_LENGTH 0
#endif
H_TEXTURE(_SpatialGuidance);
H_TEXTURE(_SampleCount);
H_TEXTURE (_AmbientOcclusion);
H_TEXTURE(_AmbientOcclusionReprojected);
H_TEXTURE(_SpatialOcclusion);
H_TEXTURE(_TemporalInvalidity);
H_RW_TEXTURE(float3, _Radiance_Output);
H_RW_TEXTURE(float2, _TemporalInvalidity_Output);
H_RW_TEXTURE(float, _AmbientOcclusion_Output);
H_RW_TEXTURE(float, _SpatialOcclusion_Output);
H_RW_TEXTURE(uint2, _SpatialGuidance_Output);
H_RW_TEXTURE(uint, _NormalDepth_HistoryOutput);
StructuredBuffer<float3> _PointDistribution;
float _FilterRadius;
float _FilterAdaptivity;
int _DenoiseFallback;
int _RayCount;
// ------------------------ TEMPORAL RESAMPLING + VALIDATION -------------------------
[numthreads(8, 8, 1)]
void TemporalResampling(uint3 pixCoord : SV_DispatchThreadID)
{
uint2 pixCoordUnscaled = GetUnscaledCoords(pixCoord.xy);
float DepthCenter = HBUFFER_DEPTH(pixCoordUnscaled);
if (DepthCenter <= UNITY_RAW_FAR_CLIP_VALUE)
{
_ReservoirLuminance_Output[H_COORD(pixCoord.xy)] = -1;
_NormalDepth_HistoryOutput[H_COORD(pixCoord.xy)] = 0;
_ReservoirSpatial_Output[H_COORD(pixCoord.xy)] = 0;
_ReservoirTemporal_Output[H_COORD(pixCoord.xy)] = 0;
_AmbientOcclusion_Output[H_COORD(pixCoord.xy)] = 0;
_TemporalInvalidity_Output[H_COORD(pixCoord.xy)] = 0;
return;
}
float3 Diffuse = GetReservoirDiffuse(pixCoordUnscaled);
uint ReservoirUpdateRandom = Hash3(uint3(pixCoord.xy, _FrameCount));
// Load and unpack current and reprojected reservoirs
TemporalReservoir TemporalRes = (TemporalReservoir)0;
TemporalReservoir TemporalReprojectedRes = (TemporalReservoir)0;
UnpackTemporalReservoir(asuint(H_LOAD(_Reservoir, pixCoord.xy)), Diffuse, TemporalRes);
UnpackTemporalReservoir(asuint(H_LOAD(_ReservoirReprojected, pixCoord.xy)), Diffuse, TemporalReprojectedRes);
float MaxHistoryClamp = RESTIR_HISTORY_MAX_LENGTH * max((uint(_RayCount) / 4), 1);
MaxHistoryClamp = clamp(MaxHistoryClamp, 0, 100);
// Backup current Color and W for replacement
float4 OriginalColorW = float4(TemporalRes.Color, TemporalRes.W);
float SamplecountReprojected = H_LOAD(_SampleCount, pixCoord.xy).x;
bool CullCheckerboard = true;
float AmbientOcclusion = H_LOAD(_AmbientOcclusion, pixCoord.xy).x;
float AmbientOcclusionInvalidity = sign(AmbientOcclusion) < 0 ? 0 : 1;
// Checkerboarding logic
if (ENABLE_TEMPORAL_RESTIR)
{
if (CHECKERBOARDING)
{
CullCheckerboard = false;
if (((pixCoord.x + pixCoord.y) % 2 == 0 && uint(_FrameCount) % 2 == 0) || SamplecountReprojected <= 1) CullCheckerboard = true;
if (((pixCoord.x + pixCoord.y) % 2 != 0 && uint(_FrameCount) % 2 != 0) || SamplecountReprojected <= 1) CullCheckerboard = true;
if (CullCheckerboard == false)
{
TemporalRes.Color = TemporalReprojectedRes.Color;
TemporalRes.Wsum = TemporalReprojectedRes.Wsum;
TemporalRes.W = TemporalReprojectedRes.W;
TemporalRes.M = TemporalReprojectedRes.M;
TemporalRes.OriginNormal = TemporalReprojectedRes.OriginNormal;
TemporalRes.Direction = TemporalReprojectedRes.Direction;
TemporalRes.Distance = TemporalReprojectedRes.Distance;
TemporalRes.HitFound = TemporalReprojectedRes.HitFound;
}
else
{ ReservoirUpdate(TemporalReprojectedRes, TemporalRes, ReservoirUpdateRandom); }
}
else
{ ReservoirUpdate(TemporalReprojectedRes, TemporalRes, ReservoirUpdateRandom); }
}
bool HitIsMoving = false;
float3 NormalCenterWS = HBUFFER_NORMAL_WS(pixCoordUnscaled);
float2 pixCoordNDC = (float2(pixCoordUnscaled) + 0.5f) / _ScreenSize.xy;
float3 PositionCenterWS = H_COMPUTE_POSITION_WS(pixCoordNDC, DepthCenter, H_MATRIX_I_VP);
bool IsOcclusionValidation = false;
if (!TemporalRes.HitFound && TemporalRes.Distance > 0 && TemporalRes.Distance < 1)
IsOcclusionValidation = true;
bool ReplaceRadiance = false;
if (VALIDATE_TEMPORAL_OCCLUSION)
{
// If the distance is in [0,1] and a hit wasn't found then we store occlusion data
bool IsOcclusionValidation = false;
if (!TemporalRes.HitFound && TemporalRes.Distance > 0 && TemporalRes.Distance < 1)
IsOcclusionValidation = true;
float StepJitter = Jitter((pixCoord.xy) * 2 - 1);
float3 RayOriginBiasedWS, RayStartPositionNDC, RayEndPositionNDC;
GetRayOriginAndDirectionNDC(_RayLength, DepthCenter, pixCoordNDC, PositionCenterWS, TemporalRes.Direction, NormalCenterWS, RayOriginBiasedWS, RayStartPositionNDC, RayEndPositionNDC);
float4 HitData = RayMarchValidation(RayStartPositionNDC, RayEndPositionNDC, StepJitter);
if (HitData.w)
{
float3 HitPosition = H_COMPUTE_POSITION_WS(HitData.xy, HitData.z, H_MATRIX_I_VP);
float HitDistance = distance(PositionCenterWS, HitPosition);
HitIsMoving = HBUFFER_MOTION_MASK(HitData.xy * _ScreenSize.xy);
// We did hit something, but it was the wrong spot
if (abs(HitDistance - TemporalRes.Distance) / (TemporalRes.Distance + TemporalRes.Distance) > 0.2f)
{
// Clamp history (smoother reaction)
if (TemporalRes.HitFound && HitDistance < TemporalRes.Distance ) // && HitDistance > 0 && TemporalRes.Distance > 0)
MaxHistoryClamp = 0;
// Replace radiance (faster reaction) if it was occlusion validation
if (IsOcclusionValidation && HitDistance < 1) // && dot(normalize(PositionCenterWS - HitPosition), TemporalRes.Direction) < 0)
ReplaceRadiance = true;
}
}
else if (IsOcclusionValidation)
{
// If we didn't hit anything but should have (because it was occlusion validation).
ReplaceRadiance = true;
}
}
bool SampleIsOffscreen = false;
if (VALIDATE_TEMPORAL_LIGHTING && TemporalRes.HitFound)
{
float LightingInvalidity = 1.0f;
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
bool MovingHitPoint;
float2 HitCoordNDC = H_COMPUTE_NDC_Z(PositionCenterWS + TemporalRes.Direction * TemporalRes.Distance, H_MATRIX_VP).xy;
float3 HitRadiance = UnpackColorHit(asuint(H_LOAD(_Color, floor(HitCoordNDC * _ScreenSize.xy) + int2(x,y)).x), MovingHitPoint);
HitRadiance *= ExponentialFalloff(TemporalRes.Distance, _RayLength);
if (any(HitCoordNDC < 0) || any(HitCoordNDC > 1))
SampleIsOffscreen = true;
float3 LuminanceCurrent = (HitRadiance);
float3 LuminanceHistory = (TemporalRes.Color);
float LightingDifference = length(abs(LuminanceHistory - LuminanceCurrent) / max(1e-3, LuminanceHistory + LuminanceCurrent));
LightingInvalidity = min(LightingInvalidity, LightingDifference);
}
}
if (!SampleIsOffscreen)
MaxHistoryClamp *= (1.0f - LightingInvalidity);
}
if (ENABLE_EXPOSURE_CONTROL)
{
float ExposurePrevious = HGetPreviousExposureMultiplier;
float ExposureCurrent = HGetCurrentExposureMultiplier;
float ExposureRatio = (ExposurePrevious * ExposureCurrent) != 0.0 ? ExposureCurrent / ExposurePrevious : 100.0;
if (max(ExposureRatio, 1.0 / ExposureRatio) > 2.0)
MaxHistoryClamp = MaxHistoryClamp * HGetInversePreviousExposureMultiplier * ExposureCurrent;
}
TemporalRes.W = TemporalRes.Wsum / max(TemporalRes.M * Luminance(TemporalRes.Color * Diffuse), 1e-8);
TemporalRes.M = clamp(TemporalRes.M, RESTIR_HISTORY_MIN_LENGTH, max(RESTIR_HISTORY_MIN_LENGTH, MaxHistoryClamp));
if (ReplaceRadiance || SampleIsOffscreen || (AmbientOcclusionInvalidity == 0 && !TemporalRes.HitFound))
{
MaxHistoryClamp = 0;
if (CullCheckerboard == true)
{
// Tried setting W and Color to 0, looks similar in certain cases, but worse in others
TemporalRes.W = OriginalColorW.w;
TemporalRes.Color = OriginalColorW.xyz;
}
}
float Samplecount = min(8, SamplecountReprojected + 1);
float TemporalWeight = 1.0f - (1.0f / float(Samplecount));
float2 TemporalInvalidity = float2(1,1);
if (VALIDATE_TEMPORAL_OCCLUSION || VALIDATE_TEMPORAL_LIGHTING)
{
TemporalInvalidity.x = MaxHistoryClamp / RESTIR_HISTORY_MAX_LENGTH;
TemporalInvalidity.y = MaxHistoryClamp < (RESTIR_HISTORY_MAX_LENGTH && HitIsMoving) ? 0 : 1;
if (TemporalRes.HitFound == false)
{ TemporalInvalidity.x *= AmbientOcclusionInvalidity; }
float2 TemporalInvalidityReprojected = H_LOAD(_TemporalInvalidity, pixCoord.xy).xy;
TemporalInvalidity = lerp(TemporalInvalidity, TemporalInvalidityReprojected, CullCheckerboard ? TemporalWeight : 1);
_TemporalInvalidity_Output[H_COORD(pixCoord.xy)] = TemporalInvalidity;
}
float AmbientOcclusionReprojected = H_LOAD(_AmbientOcclusionReprojected, pixCoord.xy).x;
AmbientOcclusion = lerp(abs(AmbientOcclusion), AmbientOcclusionReprojected, CullCheckerboard ? TemporalWeight * pow(TemporalInvalidity.y, 2) * AmbientOcclusionInvalidity : 1);
// Transfer data from temporal to spatial reservoir
SpatialReservoir SpatialRes;
SpatialRes.Color = TemporalRes.Color;
SpatialRes.W = TemporalRes.W;
SpatialRes.M = TemporalRes.M;
SpatialRes.HitFound = TemporalRes.HitFound;
SpatialRes.Occlusion = H_LOAD(_SpatialOcclusion, pixCoord.xy).x;
SpatialRes.Direction = TemporalRes.Direction;
SpatialRes.Distance = TemporalRes.Distance;
SpatialRes.Normal = NormalCenterWS;
SpatialRes.Depth = DepthCenter;
_ReservoirSpatial_Output[H_COORD(pixCoord.xy)] = PackSpatialReservoir(SpatialRes);
_ReservoirTemporal_Output[H_COORD(pixCoord.xy)] = PackTemporalReservoir(TemporalRes);
_ReservoirLuminance_Output[H_COORD(pixCoord.xy)] = Luminance(TemporalRes.Color * TemporalRes.W);
_AmbientOcclusion_Output[H_COORD(pixCoord.xy)] = AmbientOcclusion;
_NormalDepth_HistoryOutput[H_COORD(pixCoord.xy)] = PackNormalDepth(NormalCenterWS, DepthCenter);
// _Radiance_Output[H_COORD(pixCoord.xy)] = TemporalRes.Color * TemporalRes.W;
// if (Luminance(TemporalRes.Color * TemporalRes.W) > 0.01)
// _Radiance_Output[H_COORD(pixCoord.xy)] = lerp(float3(1,0,0), TemporalRes.Color * TemporalRes.W, saturate(MaxHistoryClamp / RESTIR_HISTORY_MAX_LENGTH));
}
// ------------------------ FIREFLY SUPPRESSION -------------------------
[numthreads(8, 8, 1)]
void FireflySuppression(uint3 pixCoord : SV_DispatchThreadID)
{
float LumaCenter = H_LOAD(_ReservoirLuminance, pixCoord.xy).x;
float2 Moments;
Moments.x = 0;
Moments.y = 0;
float TotalWeight = 0;
UNITY_UNROLL
for (int x = -3; x <= 3; x++)
{
UNITY_UNROLL
for (int y = -3; y <= 3; y++)
{
int2 SampleCoord = pixCoord.xy + int2(x, y);
SampleCoord = clamp(SampleCoord, 0, _ScreenSize.xy / _HScaleFactorSSGI - 1);
float LumaSample = H_LOAD(_ReservoirLuminance, pixCoord.xy + int2(x, y)).x;
// Sky pixels are marked as -1
if (LumaSample < 0)
continue;
Moments.x += LumaSample;
Moments.y += LumaSample * LumaSample;
TotalWeight += 1;
}
}
Moments.x /= TotalWeight;
Moments.y /= TotalWeight;
float Variance = (max(0.0, Moments.y - Moments.x * Moments.x));
float StdDev = sqrt(Variance);
float SamplecountReprojected = H_LOAD(_SampleCount, pixCoord.xy).x;
// Avoid applying firefly suppression for the first 4 frames of temporal accumulation
float SamplecountScale = clamp(4 - SamplecountReprojected, 1, 2);
float LumaThreshold = Moments.x + SamplecountScale * StdDev;
// If Luma is above threshold - unpack the reservoir, clamp W and pack it back
if (LumaCenter > LumaThreshold)
{
uint4 ReservoirMW = _ReservoirSpatial_Output[H_COORD(pixCoord.xy)];
float W = f16tof32(ReservoirMW.y >> 16);
W *= (LumaThreshold / max(LumaCenter, 1e-5));
uint PackedW = f32tof16(W);
PackedW = PackedW << 16;
ReservoirMW.y &= 0x0000FFFF;
ReservoirMW.y |= PackedW;
_ReservoirSpatial_Output[H_COORD(pixCoord.xy)] = ReservoirMW;
}
}
// ------------------------ SPATIAL RESAMPLING + VALIDATION -------------------------
[numthreads(8, 8, 1)]
void SpatialResamplingValidation(uint3 pixCoord : SV_DispatchThreadID)
{
uint2 pixCoordUnscaled = GetUnscaledCoords(pixCoord.xy);
int2 pixCoordMax = _ScreenSize.xy / _HScaleFactorSSGI.xx - 1;
float2 pixCoordNDC = (float2(pixCoordUnscaled) + 0.5f) * _ScreenSize.zw;
float3 Diffuse = GetReservoirDiffuse(pixCoordUnscaled);
uint ReservoirUpdateRandom = Hash3(uint3(pixCoord.xy, _FrameCount));
SpatialReservoir Reservoir = (SpatialReservoir)0;
UnpackSpatialReservoir(asuint(H_LOAD(_Reservoir, pixCoord.xy)), Diffuse, Reservoir);
if (Reservoir.Depth <= UNITY_RAW_FAR_CLIP_VALUE) { return; }
float DepthCetnerLinear = H_LINEAR_EYE_DEPTH(Reservoir.Depth);
float3 NormalCenterVS = H_TRANSFORM_WORLD_TO_VIEW_NORMAL(Reservoir.Normal);
float3 PositionCenterWS = H_COMPUTE_POSITION_WS(pixCoordNDC, Reservoir.Depth, H_MATRIX_I_VP);
float3 PositionCenterVS = ComputeFastViewSpacePosition(pixCoordNDC, Reservoir.Depth, DepthCetnerLinear);
float4 NormalPlaneVS = float4(NormalCenterVS.xyz, dot(PositionCenterVS, NormalCenterVS.xyz));
float SamplecountReprojected = H_LOAD(_SampleCount, pixCoord.xy).x;
float AmbientOcclusion = H_LOAD(_AmbientOcclusion, pixCoord.xy).x;
#if SECOND_SPATIAL_PASS
uint2 SpatialGuidancePacked = asuint(H_LOAD(_SpatialGuidance, pixCoord.xy).xy);
float AmbientOcclusionFilterScale = UnpackAmbientOcclusion(SpatialGuidancePacked.y);
uint SpatialGuidance = SpatialGuidancePacked.x;
float FilterRadius = _FilterRadius;
uint BufferOffset = 0;
#else
float AmbientOcclusionFilterScale = H_LOAD(_AmbientOcclusion, pixCoord.xy).x;
if (Luminance(Reservoir.Color * Reservoir.W) < 0.001)
AmbientOcclusionFilterScale = AmbientOcclusionFilterScale > 0.4 ? 1 : AmbientOcclusionFilterScale;
float FilterRadius = _FilterRadius / 2.0f;
uint SpatialGuidance = 0;
uint BufferOffset = 8;
#endif
float AdaptiveFilterScale;
if (_DenoiseFallback) AdaptiveFilterScale = min(saturate(Reservoir.Distance / 2 + !Reservoir.HitFound), AmbientOcclusionFilterScale);
else AdaptiveFilterScale = min(saturate(Reservoir.Distance / 2), AmbientOcclusionFilterScale);
float MinFilterRadius = lerp(0.01f, 0.1f, AmbientOcclusion);
float AdaptivePlaneWeight = lerp(500.0f, 100.0f, AdaptiveFilterScale);
FilterRadius = max(MinFilterRadius, lerp(FilterRadius, FilterRadius * AdaptiveFilterScale, _FilterAdaptivity));
float3x3 OrthoBasis = HGetLocalFrame(Reservoir.Normal);
float DistanceToPoint = length(H_GET_ABSOLUTE_POSITION_WS(PositionCenterWS) - H_GET_CAMERA_POSITION_WS());
float RadiusScale = lerp(5.0f, 50.0f, saturate(DistanceToPoint / 500.0f));
float Radius = DistanceToPoint * FilterRadius / RadiusScale;
float Sigma = 0.9f * Radius;
float WeightTotal = 1;
float2 TemporalInvalidity = 1, TemporalInvalidityAccumulated = 1;
if (VALIDATE_TEMPORAL_OCCLUSION || VALIDATE_TEMPORAL_LIGHTING)
{ TemporalInvalidity = H_LOAD(_TemporalInvalidity, pixCoord.xy).xy; TemporalInvalidityAccumulated = TemporalInvalidity; }
UNITY_UNROLL
for (int i = 0; i < 8; i++)
{
float GuidanceAdaptivity = 1 - (0.75 * ((SpatialGuidance >> i) & 0x1));
float2 Point = _PointDistribution[i + BufferOffset].xy * Radius * GuidanceAdaptivity;
float3 PositionPointWS = PositionCenterWS + OrthoBasis[0] * Point.x + OrthoBasis[1] * Point.y;
float2 SampleCoordNDC = H_COMPUTE_NDC_Z(PositionPointWS, H_MATRIX_VP).xy;
int2 SampleCoord = SampleCoordNDC * _ScreenSize.xy / _HScaleFactorSSGI;
int2 Overshoot = max(SampleCoord - pixCoordMax, 0);
SampleCoord = SampleCoord - 2 * Overshoot;
SpatialReservoir SampleReservoir = (SpatialReservoir)0;
uint4 SampleReservoirPacked = asuint(H_LOAD(_Reservoir, SampleCoord));
UnpackSpatialReservoir(SampleReservoirPacked, Diffuse, SampleReservoir);
float3 PositionSampleVS = ComputeFastViewSpacePosition(SampleCoordNDC, SampleReservoir.Depth, H_LINEAR_EYE_DEPTH(SampleReservoir.Depth));
float PlaneWeight = ProbePlaneWeighting(NormalPlaneVS, PositionSampleVS, DepthCetnerLinear, AdaptivePlaneWeight);
float NormalWeight = saturate(dot(Reservoir.Normal, SampleReservoir.Normal));
float GaussianWeight = GaussianWeighting(length(Point), Sigma);
float OcclusionWeight = 1.0f;
if (VALIDATE_SPATIAL_OCCLUSION)
{ OcclusionWeight = exp2(-10 * abs(Reservoir.Occlusion - SampleReservoir.Occlusion)); }
float SampleWeight = PlaneWeight * NormalWeight * OcclusionWeight;
SpatialGuidance |= (uint(round(1 - SampleWeight)) << i);
SampleWeight *= GaussianWeight * ENABLE_SPATIAL_RESTIR;
SampleReservoir.Wsum *= SampleWeight;
SampleReservoir.M *= SampleWeight;
ReservoirUpdate(SampleReservoir, Reservoir, ReservoirUpdateRandom);
WeightTotal += NormalWeight * PlaneWeight * GaussianWeight;
if (VALIDATE_TEMPORAL_OCCLUSION || VALIDATE_TEMPORAL_LIGHTING)
{ TemporalInvalidityAccumulated += H_LOAD(_TemporalInvalidity, SampleCoord).xy * NormalWeight * PlaneWeight * GaussianWeight; }
}
Reservoir.W = Reservoir.Wsum / max(Reservoir.M * Luminance(Reservoir.Color * Diffuse), 1e-8);
float3 FinalColor = Reservoir.Color * Reservoir.W;
if (SECOND_SPATIAL_PASS)
{
if (VALIDATE_SPATIAL_OCCLUSION) // && Reservoir.HitFound)
{
float SpatialOcclusion = 1;
const float3 RayDirectionWS = Reservoir.Direction;
float StepJitter = Jitter((pixCoord.xy) * 2 - 1);
float3 RayOriginBiasedWS, RayStartPositionNDC, RayEndPositionNDC;
GetRayOriginAndDirectionNDC(min(3, Reservoir.Distance), Reservoir.Depth, pixCoordNDC, PositionCenterWS, RayDirectionWS, Reservoir.Normal, RayOriginBiasedWS, RayStartPositionNDC, RayEndPositionNDC);
float4 HitData = RayMarchValidation(RayStartPositionNDC, RayEndPositionNDC, StepJitter);
if (HitData.w)
{
float3 HitPosition = H_COMPUTE_POSITION_WS(HitData.xy, HitData.z, H_MATRIX_I_VP);
float HitDistance = distance(PositionCenterWS, HitPosition);
if (abs(HitDistance - Reservoir.Distance) / (Reservoir.Distance + Reservoir.Distance) > 0.2)
{
if (HitDistance < Reservoir.Distance)
{
SpatialOcclusion = saturate(HitDistance / 3.0f);
if (Reservoir.HitFound)
{
if (dot(HBUFFER_NORMAL_WS(HitData.xy * _ScreenSize.xy), RayDirectionWS) > _BackfaceLighting)
{
FinalColor *= saturate(HitDistance / 1.0f);
}
}
}
}
}
float Samplecount = min(16, SamplecountReprojected + 1);
float TemporalWeight = 1.0f - (1.0f / float(Samplecount));
float SpatialOcclusionReprojected = H_LOAD(_SpatialOcclusion, pixCoord.xy).x;
SpatialOcclusion = lerp(SpatialOcclusion, SpatialOcclusionReprojected, TemporalWeight * pow(TemporalInvalidity.y, 2));
_SpatialOcclusion_Output[H_COORD(pixCoord.xy)] = SpatialOcclusion;
}
_Radiance_Output[H_COORD(pixCoord.xy)] = FinalColor;
}
else
{
_SpatialGuidance_Output[H_COORD(pixCoord.xy)] = uint2(SpatialGuidance, PackAmbientOcclusion(AdaptiveFilterScale));
_Reservoir_Output[H_COORD(pixCoord.xy)] = PackSpatialReservoir(Reservoir);
}
if (VALIDATE_TEMPORAL_OCCLUSION || VALIDATE_TEMPORAL_LIGHTING)
{ _TemporalInvalidity_Output[H_COORD(pixCoord.xy)] = TemporalInvalidityAccumulated / WeightTotal; }
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 97f6e36d045d93d4ea8ed934f8bc4a03
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HRestirSSGI.compute
uploadId: 840002

View File

@@ -0,0 +1,438 @@
#pragma kernel TemporalReprojection
#pragma kernel ColorReprojection
#pragma kernel CopyHistory
#pragma kernel LuminanceMomentsGeneration
#pragma kernel LuminanceMomentsClear
#include "../Includes/HReservoirSSGI.hlsl"
#include "../Includes/HFallbackSSGI.hlsl"
#pragma multi_compile _ _GBUFFER_NORMALS_OCT
#pragma multi_compile _ REPROJECT_COLOR
#pragma multi_compile _ AUTOMATIC_BRIGHTNESS_CLAMP
#pragma multi_compile _ REPROJECT_TEMPORAL_INVALIDITY
#pragma multi_compile _ REPROJECT_SPATIAL_OCCLUSION
#pragma multi_compile _ FULL_RESOLUTION_REPROJECTION
H_TEXTURE(_HTraceBufferGI);
H_TEXTURE(_Color_History);
H_TEXTURE(_NormalDepth_History);
H_TEXTURE(_Radiance_History);
H_TEXTURE(_Samplecount_History);
H_TEXTURE(_SpatialOcclusion_History);
H_TEXTURE(_TemporalInvalidity_History);
H_TEXTURE(_AmbientOcclusion_History);
H_RW_TEXTURE(float, _AmbientOcclusion_Output);
H_RW_TEXTURE(float2, _TemporalInvalidity_Output);
H_RW_TEXTURE(float, _Samplecount_Output);
H_RW_TEXTURE(float, _SpatialOcclusion_Output);
H_RW_TEXTURE(float4, _Radiance_Output);
H_RW_TEXTURE(uint, _ReprojectedColor_Output);
groupshared uint GroupMoment1;
groupshared uint GroupMoment2;
RWStructuredBuffer<uint> _LuminanceMoments;
float _BrightnessClamp;
float _MaxDeviation;
// ------------------------ REPROJECTION STRUCTS -----------------------
struct CurrentFrameData
{
float3 Normal;
float3 WorldPos;
float DepthRaw;
float DepthLinear;
float2 RejectionPower;
};
struct PreviousFrameData
{
float3 Normal;
float3 WorldPos;
float DepthLinear;
};
// ------------------------ REPROJECTION STRUCTS -----------------------
float Disocclusion(CurrentFrameData CurrentData, PreviousFrameData PreviousData)
{
float Diosocclusion = 1;
float PlaneDistance = abs(dot(PreviousData.WorldPos - CurrentData.WorldPos, CurrentData.Normal));
float RelativeDepthDifference = PlaneDistance / CurrentData.DepthLinear;
if (exp2(-CurrentData.RejectionPower.x * (RelativeDepthDifference * RelativeDepthDifference)) < 0.1f)
Diosocclusion = 0.0f;
if (abs((PreviousData.DepthLinear - CurrentData.DepthLinear) / CurrentData.DepthLinear) >= CurrentData.RejectionPower.y)
Diosocclusion = 0.0f;
if (dot(PreviousData.Normal, CurrentData.Normal) < 0.5)
Diosocclusion = 0.0f;
return Diosocclusion;
}
// ------------------------ COLOR REPROJECTION -----------------------
[numthreads(8, 8, 1)]
void ColorReprojection(uint3 pixCoord : SV_DispatchThreadID)
{
bool MovingPixel = HBUFFER_MOTION_MASK(pixCoord.xy);
float2 pixCoordNDC = (pixCoord.xy + 0.5f) / _ScreenSize.xy;
float3 Sky = EvaluateFallbackSky(H_GET_VIEW_VECTOR_WS(pixCoordNDC));
float LumaThreshold = _BrightnessClamp;
if (AUTOMATIC_BRIGHTNESS_CLAMP)
{
uint2 MomentsPacked;
MomentsPacked.x = _LuminanceMoments[0];
MomentsPacked.y = _LuminanceMoments[1];
float2 Moments;
float Scale = 4294967295.0f / (30.0f * 30.0f);
Moments.x = float(MomentsPacked.x) / Scale;
Moments.y = float(MomentsPacked.y) / Scale;
float Variance = (max(0.0, Moments.y - Moments.x * Moments.x));
float StdDev = sqrt(Variance);
LumaThreshold = Moments.x + _MaxDeviation * StdDev;
}
CurrentFrameData CurrentData;
CurrentData.DepthRaw = HBUFFER_DEPTH(pixCoord.xy);
CurrentData.Normal = HBUFFER_NORMAL_WS(pixCoord.xy);
CurrentData.DepthLinear = H_LINEAR_EYE_DEPTH(CurrentData.DepthRaw);
CurrentData.WorldPos = H_COMPUTE_POSITION_WS(pixCoordNDC, CurrentData.DepthRaw, H_MATRIX_I_VP);
const float DepthAligment = 1.0f - dot(-normalize(H_GET_ABSOLUTE_POSITION_WS((CurrentData.WorldPos)) - _WorldSpaceCameraPos), CurrentData.Normal);
CurrentData.RejectionPower.x = 100000.0f * (MovingPixel ? 0.0001 * CurrentData.DepthLinear * CurrentData.DepthLinear : 1);
CurrentData.RejectionPower.y = lerp(1e-2f, 1e-1f, pow(DepthAligment, 8)) * (MovingPixel ? 50 / CurrentData.DepthLinear : 1);
if (!REPROJECT_COLOR)
{
float3 ReprojectedColor = CurrentData.DepthRaw <= UNITY_RAW_FAR_CLIP_VALUE ? Sky : HBUFFER_COLOR(pixCoord.xy).xyz;
float ReprojectedColorLuma = Luminance(ReprojectedColor);
if (ReprojectedColorLuma > LumaThreshold)
ReprojectedColor *= (LumaThreshold / ReprojectedColorLuma);
_ReprojectedColor_Output[H_COORD(pixCoord.xy)] = PackColorHit(ReprojectedColor, MovingPixel);
//return;
}
float2 ReprojectionCoord = float2(pixCoord.xy) - HBUFFER_MOTION_VECTOR(pixCoord.xy) * floor(_ScreenSize.xy);
ReprojectionCoord /= (HRenderScale.xy / HRenderScalePrevious.xy);
float UVx = frac(ReprojectionCoord.x);
float UVy = frac(ReprojectionCoord.y);
float4 ReprojectionWeights;
ReprojectionWeights.x = (1.0f - UVx) * (1.0f - UVy);
ReprojectionWeights.y = (UVx) * (1.0f - UVy);
ReprojectionWeights.z = (1.0f - UVx) * (UVy);
ReprojectionWeights.w = (UVx) * (UVy);
const uint2 ReprojectionOffsets[4] = {uint2(0, 0), uint2(1, 0), uint2(0, 1), uint2(1, 1)};
UNITY_UNROLL
for (int i = 0; i < 4; i++)
{
int2 SampleCoord = ReprojectionCoord + ReprojectionOffsets[i];
uint HistoryNormalDepthPacked = asuint(H_LOAD(_NormalDepth_History, SampleCoord).x);
float4 HistoryNormalDepth = UnpackNormalDepth(HistoryNormalDepthPacked);
SampleCoord /= (HRenderScalePrevious.xy / HRenderScale.xy);
PreviousFrameData PreviousData;
PreviousData.Normal = HistoryNormalDepth.xyz;
PreviousData.WorldPos = H_COMPUTE_POSITION_WS((SampleCoord + 0.5f) * _ScreenSize.zw, HistoryNormalDepth.w, H_MATRIX_PREV_I_VP);
PreviousData.DepthLinear = H_LINEAR_EYE_DEPTH(PreviousData.WorldPos, UNITY_MATRIX_V);
ReprojectionWeights[i] *= Disocclusion(CurrentData, PreviousData);
if (CurrentData.DepthRaw <= UNITY_RAW_FAR_CLIP_VALUE)
ReprojectionWeights[i] = 1;
if (any(SampleCoord.xy >= _ScreenSize.xy))
ReprojectionWeights[i] = 0;
}
float WeightsTotal = ReprojectionWeights.x + ReprojectionWeights.y + ReprojectionWeights.z + ReprojectionWeights.w;
float ReprojectedSamplecount = 0;
float3 ReprojectedRadiance = 0;
float3 ReprojectedColor = 0;
if (WeightsTotal > 0.15f)
{
ReprojectionWeights = WeightsTotal > 0 ? ReprojectionWeights / WeightsTotal : 0;
float BestWeight = 0;
uint2 BestOffset = 0;
UNITY_UNROLL
for (int i = 0; i < 4; i++)
{
float4 RadianceSamplecountHistory = H_LOAD(_Radiance_History, ReprojectionCoord + ReprojectionOffsets[i]) * ReprojectionWeights[i];
ReprojectedSamplecount += RadianceSamplecountHistory.w;
ReprojectedRadiance += RadianceSamplecountHistory.xyz;
// Select best reprojection for color
if (ReprojectionWeights[i] > BestWeight)
{
BestWeight = ReprojectionWeights[i];
BestOffset = ReprojectionOffsets[i];
}
}
if (REPROJECT_COLOR)
{
ReprojectedColor = CurrentData.DepthRaw <= UNITY_RAW_FAR_CLIP_VALUE ? Sky : H_LOAD(_Color_History, ReprojectionCoord + BestOffset).xyz;
float ReprojectedColorLuma = Luminance(ReprojectedColor);
if (ReprojectedColorLuma > LumaThreshold)
ReprojectedColor *= (LumaThreshold / ReprojectedColorLuma);
}
}
_Radiance_Output[H_COORD(pixCoord.xy)] = float4(ReprojectedRadiance.xyz, ReprojectedSamplecount.x);
_ReprojectedColor_Output[H_COORD(pixCoord.xy)] = PackColorHit(ReprojectedColor, MovingPixel);
}
// ------------------------ TEMPORAL REPROJECTION -----------------------
[numthreads(8, 8, 1)]
void TemporalReprojection(uint3 pixCoord : SV_DispatchThreadID)
{
uint2 pixCoordUnscaled = GetUnscaledCoords(pixCoord.xy);
float2 pixCoordNDC = (float2(pixCoordUnscaled) + 0.5f) / _ScreenSize.xy;
float3 Sky = EvaluateFallbackSky(H_GET_VIEW_VECTOR_WS(pixCoordNDC));
float LumaThreshold = _BrightnessClamp;
if (AUTOMATIC_BRIGHTNESS_CLAMP)
{
uint2 MomentsPacked;
MomentsPacked.x = _LuminanceMoments[0];
MomentsPacked.y = _LuminanceMoments[1];
float2 Moments;
float Scale = 4294967295.0f / (30.0f * 30.0f);
Moments.x = float(MomentsPacked.x) / Scale;
Moments.y = float(MomentsPacked.y) / Scale;
float Variance = (max(0.0, Moments.y - Moments.x * Moments.x));
float StdDev = sqrt(Variance);
LumaThreshold = Moments.x + _MaxDeviation * StdDev;
}
CurrentFrameData CurrentData;
CurrentData.DepthRaw = HBUFFER_DEPTH(pixCoordUnscaled);
CurrentData.Normal = HBUFFER_NORMAL_WS(pixCoordUnscaled);
CurrentData.DepthLinear = H_LINEAR_EYE_DEPTH(CurrentData.DepthRaw);
CurrentData.WorldPos = H_COMPUTE_POSITION_WS(pixCoordNDC, CurrentData.DepthRaw, H_MATRIX_I_VP);
bool MovingPixel = HBUFFER_MOTION_MASK(pixCoordUnscaled);
float DepthAligment = 1.0f - dot(-normalize(H_GET_ABSOLUTE_POSITION_WS((CurrentData.WorldPos)) - _WorldSpaceCameraPos), CurrentData.Normal);
CurrentData.RejectionPower.x = 100000.0f * (MovingPixel ? 0.0001 * CurrentData.DepthLinear * CurrentData.DepthLinear : 1);
CurrentData.RejectionPower.y = lerp(1e-2f, 1e-1f, pow(DepthAligment, 8)) * (MovingPixel ? 50 / CurrentData.DepthLinear : 1);
float2 MotionVectors = HBUFFER_MOTION_VECTOR(pixCoordUnscaled);
float2 ReprojectionCoord = float2(pixCoord.xy) - MotionVectors * floor(_ScreenSize.xy / _HScaleFactorSSGI);
ReprojectionCoord /= (_HPreviousScaleFactorSSGI.xx / _HScaleFactorSSGI.xx);
ReprojectionCoord /= (HRenderScale.xy / HRenderScalePrevious.xy);
float UVx = frac(ReprojectionCoord.x);
float UVy = frac(ReprojectionCoord.y);
float4 ReprojectionWeights;
ReprojectionWeights.x = (1.0f - UVx) * (1.0f - UVy);
ReprojectionWeights.y = (UVx) * (1.0f - UVy);
ReprojectionWeights.z = (1.0f - UVx) * (UVy);
ReprojectionWeights.w = (UVx) * (UVy);
const uint2 ReprojectionOffsets[4] = {uint2(0, 0), uint2(1, 0), uint2(0, 1), uint2(1, 1)};
UNITY_UNROLL
for (int i = 0; i < 4; i++)
{
int2 SampleCoord = ReprojectionCoord + ReprojectionOffsets[i];
uint HistoryNormalDepthPacked = asuint(H_LOAD(_NormalDepth_History, SampleCoord).x);
float4 HistoryNormalDepth = UnpackNormalDepth(HistoryNormalDepthPacked);
SampleCoord /= (HRenderScalePrevious.xy / HRenderScale.xy);
PreviousFrameData PreviousData;
PreviousData.Normal = HistoryNormalDepth.xyz;
PreviousData.WorldPos = H_COMPUTE_POSITION_WS((SampleCoord * _HPreviousScaleFactorSSGI + 0.5f) * _ScreenSize.zw, HistoryNormalDepth.w, H_MATRIX_PREV_I_VP);
PreviousData.DepthLinear = H_LINEAR_EYE_DEPTH(PreviousData.WorldPos, UNITY_MATRIX_V);
ReprojectionWeights[i] *= Disocclusion(CurrentData, PreviousData);
if (CurrentData.DepthRaw <= UNITY_RAW_FAR_CLIP_VALUE)
ReprojectionWeights[i] = 1;
if (any(SampleCoord.xy >= _ScreenSize.xy / _HPreviousScaleFactorSSGI))
ReprojectionWeights[i] = 0;
}
float WeightsTotal = ReprojectionWeights.x + ReprojectionWeights.y + ReprojectionWeights.z + ReprojectionWeights.w;
TemporalReservoir Reservoir = (TemporalReservoir)0;
float3 Diffuse = GetReservoirDiffuse(pixCoordUnscaled);
float Samplecount = 0;
float ReprojectedAmbientOcclusion = 1;
float ReprojectedSpatialOcclusion = 1;
float2 ReprojectedTemporalInvalidity = 1;
float3 ReprojectedColor = 0;
float3 ReprojectedRadiance = 0;
if (WeightsTotal > 0.15f)
{
ReprojectionWeights /= WeightsTotal;
float BestWeight = 0;
uint2 BestOffset = 0;
ReprojectedSpatialOcclusion = 0;
ReprojectedTemporalInvalidity = 0;
ReprojectedAmbientOcclusion = 0;
uint ReservoirUpdateRandom = Hash3(uint3(pixCoord.xy, _FrameCount));
UNITY_UNROLL
for (int i = 0; i < 4; i++)
{
// Reproject Spatial Occlusion
if (REPROJECT_TEMPORAL_INVALIDITY) { ReprojectedTemporalInvalidity += H_LOAD(_TemporalInvalidity_History, ReprojectionCoord + ReprojectionOffsets[i]).xy * ReprojectionWeights[i]; }
if (REPROJECT_SPATIAL_OCCLUSION) { ReprojectedSpatialOcclusion += H_LOAD(_SpatialOcclusion_History, ReprojectionCoord + ReprojectionOffsets[i]).x * ReprojectionWeights[i]; }
ReprojectedAmbientOcclusion += H_LOAD(_AmbientOcclusion_History, ReprojectionCoord + ReprojectionOffsets[i]).x * ReprojectionWeights[i];
ReprojectedRadiance += H_LOAD(_Radiance_History, ReprojectionCoord + ReprojectionOffsets[i]).xyz * ReprojectionWeights[i];
Samplecount += H_LOAD(_Samplecount_History, ReprojectionCoord + ReprojectionOffsets[i]).x * ReprojectionWeights[i];
// Reproject ReSTIR reservoir
TemporalReservoir HistoryReservoir = (TemporalReservoir)0;
UnpackTemporalReservoir(asuint(H_LOAD(_Reservoir, ReprojectionCoord + ReprojectionOffsets[i])), Diffuse, HistoryReservoir);
HistoryReservoir.Wsum *= ReprojectionWeights[i];
HistoryReservoir.M *= ReprojectionWeights[i];
ReservoirUpdate(HistoryReservoir, Reservoir, ReservoirUpdateRandom);
// Select best reprojection for color
if (ReprojectionWeights[i] > BestWeight)
{
BestWeight = ReprojectionWeights[i];
BestOffset = ReprojectionOffsets[i];
}
}
// Reproject Color
if (FULL_RESOLUTION_REPROJECTION && REPROJECT_COLOR)
{
// Use original reprojection coord to avoid reading Color buffer in wrong location (scaled by _HScaleFactorSSGI / _HPreviousScaleFactorSSGI)
float2 ReprojectionCoord = float2(pixCoord.xy) - MotionVectors * floor(_ScreenSize.xy / _HScaleFactorSSGI);
ReprojectionCoord /= (HRenderScale.xy / HRenderScalePrevious.xy);
ReprojectedColor = CurrentData.DepthRaw <= UNITY_RAW_FAR_CLIP_VALUE ? Sky : H_LOAD(_Color_History, ReprojectionCoord + BestOffset).xyz;
float ReprojectedColorLuma = Luminance(ReprojectedColor);
if (ReprojectedColorLuma > LumaThreshold)
ReprojectedColor *= (LumaThreshold / ReprojectedColorLuma);
}
}
Reservoir.W = Reservoir.Wsum / max(Reservoir.M * Luminance(Reservoir.Color * Diffuse), 1e-8);
_Samplecount_Output[H_COORD(pixCoord.xy)] = Samplecount;
_Radiance_Output[H_COORD(pixCoord.xy)] = float4(ReprojectedRadiance.xyz, 0);
_Reservoir_Output[H_COORD(pixCoord.xy)] = PackTemporalReservoir(Reservoir);
_AmbientOcclusion_Output[H_COORD(pixCoord.xy)] = ReprojectedAmbientOcclusion;
if (!REPROJECT_COLOR)
{
ReprojectedColor = CurrentData.DepthRaw <= UNITY_RAW_FAR_CLIP_VALUE ? Sky : HBUFFER_COLOR(pixCoord.xy).xyz;
float ReprojectedColorLuma = Luminance(ReprojectedColor);
if (ReprojectedColorLuma > LumaThreshold)
ReprojectedColor *= (LumaThreshold / ReprojectedColorLuma);
}
if (FULL_RESOLUTION_REPROJECTION) { _ReprojectedColor_Output[H_COORD(pixCoordUnscaled)] = PackColorHit(ReprojectedColor, MovingPixel); }
if (REPROJECT_SPATIAL_OCCLUSION) { _SpatialOcclusion_Output[H_COORD(pixCoord.xy)] = ReprojectedSpatialOcclusion; }
if (REPROJECT_TEMPORAL_INVALIDITY) { _TemporalInvalidity_Output[H_COORD(pixCoord.xy)] = ReprojectedTemporalInvalidity; }
}
// ------------------------ HISTORY COPYING -----------------------
[numthreads(8, 8, 1)]
void CopyHistory(uint3 pixCoord : SV_DispatchThreadID)
{
float3 IndirectLighting = H_LOAD(_HTraceBufferGI, pixCoord.xy).xyz;
float3 DiffuseColor = HBUFFER_DIFFUSE(pixCoord.xy).xyz;
float Metallic = H_LOAD(g_HTraceGBuffer1, pixCoord.xy).x;
// No Indirect Intensity multiplier here to avoid overblown lighting through the temporal loop
float3 FinalIndirectLighting = IndirectLighting * DiffuseColor * (1 - Metallic);
_Radiance_Output[H_COORD(pixCoord.xy)] = float4(HBUFFER_COLOR(pixCoord.xy).xyz + FinalIndirectLighting, 0);
}
// ------------------------ LUMINANCE MOMENTS GENERATION ------------------------
[numthreads(8, 8, 1)]
void LuminanceMomentsGeneration(uint3 pixCoord : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex)
{
if (groupIndex == 0)
{ GroupMoment1 = 0; GroupMoment2 = 0;}
GroupMemoryBarrierWithGroupSync();
// We don't reproject this buffer even if it's from the prev. frame because it's fine for this operation
float2 pixCoordNDC = float2(pixCoord.xy + 0.5) * _ScreenSize.zw;
float3 Color = HBUFFER_DEPTH(pixCoord.xy).x <= UNITY_RAW_FAR_CLIP_VALUE ? EvaluateFallbackSky(H_GET_VIEW_VECTOR_WS(pixCoordNDC)) : H_LOAD(_Color_History, pixCoord.xy).xyz;
float Luma = Luminance(Color);
Luma = clamp(Luma, 0.0, 30);
float Scale = 4294967295.0f / (30.0f * 30.0f);
uint Moment1 = uint(Luma * Scale / 64.0f);
uint Moment2 = uint(Luma * Luma * Scale / 64.0f);
InterlockedAdd(GroupMoment1, Moment1);
InterlockedAdd(GroupMoment2, Moment2);
GroupMemoryBarrierWithGroupSync();
UNITY_BRANCH
if (groupIndex == 0)
{
float2 Moments = 0;
Moments.x = float(GroupMoment1);
Moments.y = float(GroupMoment2);
GroupMoment1 = uint(Moments.x / (_ScreenSize.x * _ScreenSize.y / 64));
GroupMoment2 = uint(Moments.y / (_ScreenSize.x * _ScreenSize.y / 64));
uint Index = 0;
InterlockedAdd(_LuminanceMoments[0], GroupMoment1, Index);
InterlockedAdd(_LuminanceMoments[1], GroupMoment2, Index);
}
}
// ------------------------ LUMINANCE MOMENTS CLEAR ------------------------
[numthreads(1, 1, 1)]
void LuminanceMomentsClear(uint3 pixCoord : SV_DispatchThreadID, uint2 groupThreadID : SV_GroupThreadID)
{
_LuminanceMoments[0] = 0;
_LuminanceMoments[1] = 0;
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: c91b570672b56b14ca13f188b77120ab
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 336896
packageName: 'HTrace: Screen Space Global Illumination URP'
packageVersion: 1.2.0
assetPath: Assets/HTraceSSGI/Resources/HTraceSSGI/Computes/HTemporalReprojectionSSGI.compute
uploadId: 840002