feat: added htrace
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user