292 lines
13 KiB
Plaintext
292 lines
13 KiB
Plaintext
#pragma kernel SpatialFilterHalf SPATIAL_FILTER=SpatialFilterHalf HALF_RES
|
|
#pragma kernel SpatialFilter SPATIAL_FILTER=SpatialFilter
|
|
#pragma kernel TemporalFilterHalf TEMPORAL_FILTER=TemporalFilterHalf HALF_RES
|
|
#pragma kernel TemporalFilter TEMPORAL_FILTER=TemporalFilter
|
|
#pragma kernel CopyHistory COPY_HISTORY=CopyHistory
|
|
|
|
// Common includes
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonLighting.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
|
|
|
|
// HDRP includes
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/NormalBuffer.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/TemporalAntialiasing.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Denoising/BilateralFilter.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/Denoising/DenoisingUtils.hlsl"
|
|
|
|
// #pragma only_renderers d3d11
|
|
// #pragma enable_d3d11_debug_symbols
|
|
|
|
// Tile size of this compute shaders
|
|
#define SSGI_FILTER_TILE_SIZE 8
|
|
|
|
// Noisy buffer input
|
|
TEXTURE2D_X(_InputNoisyBuffer0);
|
|
TEXTURE2D_X(_InputNoisyBuffer1);
|
|
|
|
// Constant buffer that should hold all the scalar values require
|
|
CBUFFER_START(UnityScreenSpaceGlobalIllumination)
|
|
float2 _DepthPyramidFirstMipLevelOffset;
|
|
float4 _HalfScreenSize;
|
|
int _IndirectDiffuseSpatialFilter;
|
|
float _HistoryValidity;
|
|
float _PixelSpreadAngleTangent;
|
|
CBUFFER_END
|
|
|
|
// Denoised output buffer
|
|
RW_TEXTURE2D_X(float4, _OutputFilteredBuffer0);
|
|
RW_TEXTURE2D_X(float4, _OutputFilteredBuffer1);
|
|
|
|
#define SPATIAL_FILTER_SIGMA_RATIO 0.9
|
|
#define DEPTH_WEIGHT_MULTIPLIER 100.0f
|
|
#define NORMAL_WEIGHT_MULTIPLIER 5.0
|
|
|
|
// Number of samples to compelte the accumulation loop
|
|
#define NUM_SAMPLE_LOOP 8.0
|
|
#define EXPONENTIAL_ACCUMULATION_FACTOR (NUM_SAMPLE_LOOP/(NUM_SAMPLE_LOOP + 1.0f))
|
|
|
|
[numthreads(SSGI_FILTER_TILE_SIZE, SSGI_FILTER_TILE_SIZE, 1)]
|
|
void SPATIAL_FILTER(uint3 dispatchThreadId : SV_DispatchThreadID , uint2 groupThreadId : SV_GroupThreadID , uint2 groupId : SV_GroupID)
|
|
{
|
|
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
|
|
|
|
// Fetch the current pixel coordinates
|
|
int2 centerCoord = dispatchThreadId.xy;
|
|
|
|
#if HALF_RES
|
|
// Get the posinputs of the current version of the pixel
|
|
float depth = LOAD_TEXTURE2D_X(_DepthTexture, asuint(_DepthPyramidFirstMipLevelOffset) + centerCoord).r;
|
|
NormalData normalData;
|
|
DecodeFromNormalBuffer(centerCoord * 2, normalData);
|
|
#else
|
|
// Get the posinputs of the current version of the pixel
|
|
float depth = LOAD_TEXTURE2D_X(_DepthTexture, centerCoord).r;
|
|
NormalData normalData;
|
|
DecodeFromNormalBuffer(centerCoord, normalData);
|
|
#endif
|
|
|
|
// If the current point we are processing is a background point or the whole history should be discarded for an other reason, we invalidate the history
|
|
if (depth == UNITY_RAW_FAR_CLIP_VALUE)
|
|
{
|
|
_OutputFilteredBuffer0[COORD_TEXTURE2D_X(centerCoord)] = LOAD_TEXTURE2D_X(_InputNoisyBuffer0, centerCoord);
|
|
_OutputFilteredBuffer1[COORD_TEXTURE2D_X(centerCoord)] = LOAD_TEXTURE2D_X(_InputNoisyBuffer1, centerCoord);
|
|
return;
|
|
}
|
|
|
|
// Convert the depth into linear for weight evaluation
|
|
float centerz01 = Linear01Depth(depth, _ZBufferParams);
|
|
|
|
// Initialize the accumulation buffers
|
|
float4 colorSum0 = 0.0;
|
|
float3 colorSum1 = 0.0;
|
|
float wSum = 0.0;
|
|
|
|
const float sigma = _IndirectDiffuseSpatialFilter * SPATIAL_FILTER_SIGMA_RATIO;
|
|
|
|
// Loop through the neighboord and do our filter
|
|
for (int y = -_IndirectDiffuseSpatialFilter; y <= _IndirectDiffuseSpatialFilter; ++y)
|
|
{
|
|
for (int x = -_IndirectDiffuseSpatialFilter; x <= _IndirectDiffuseSpatialFilter; ++x)
|
|
{
|
|
// This could probably be avoided, but the shader is badwidth bound anyway, so..
|
|
float r = sqrt(x * x + y * y);
|
|
|
|
// Compute the absolute tap coord
|
|
int2 tapCoord = centerCoord + int2(x, y);
|
|
|
|
// We should not tap outside of the screen (given that its a unit, if we go below zero we wrap around)
|
|
#if HALF_RES
|
|
if (tapCoord.x >= _HalfScreenSize.x
|
|
|| tapCoord.x < 0
|
|
|| tapCoord.y >= _HalfScreenSize.y
|
|
|| tapCoord.y < 0)
|
|
continue;
|
|
#else
|
|
if (tapCoord.x >= _ScreenSize.x
|
|
|| tapCoord.x < 0
|
|
|| tapCoord.y >= _ScreenSize.y
|
|
|| tapCoord.y < 0)
|
|
continue;
|
|
#endif
|
|
|
|
// Read the depth of the tap pixel
|
|
#if HALF_RES
|
|
float tapDepth = LOAD_TEXTURE2D_X(_DepthTexture, asuint(_DepthPyramidFirstMipLevelOffset) + tapCoord).r;
|
|
NormalData normalDataTap;
|
|
DecodeFromNormalBuffer(tapCoord * 2, normalDataTap);
|
|
#else
|
|
float tapDepth = LOAD_TEXTURE2D_X(_DepthTexture, tapCoord).r;
|
|
NormalData normalDataTap;
|
|
DecodeFromNormalBuffer(tapCoord, normalDataTap);
|
|
#endif
|
|
if (tapDepth == UNITY_RAW_FAR_CLIP_VALUE)
|
|
continue;
|
|
|
|
// Convert the tapped depth to linear for weight evaluation
|
|
float tapz01 = Linear01Depth(tapDepth, _ZBufferParams);
|
|
|
|
// Compute the depth for this pixel
|
|
float depthWeight = max(0.0, 1.0 - abs(tapz01 - centerz01) * DEPTH_WEIGHT_MULTIPLIER);
|
|
|
|
const float normalCloseness = sqr(sqr(max(0.0, dot(normalDataTap.normalWS, normalData.normalWS))));
|
|
const float normalError = 1.0 - normalCloseness;
|
|
const float normalWeight = max(0.0, (1.0 - normalError * NORMAL_WEIGHT_MULTIPLIER));
|
|
|
|
// Compute the weight (skip computation for the center)
|
|
const float w = r ? gaussian(r, sigma) * depthWeight * normalWeight : 1.0;
|
|
|
|
// Accumuate this value
|
|
colorSum0 += LOAD_TEXTURE2D_X(_InputNoisyBuffer0, tapCoord).xyzw * w;
|
|
colorSum1 += LOAD_TEXTURE2D_X(_InputNoisyBuffer1, tapCoord).xyz * w;
|
|
wSum += w;
|
|
}
|
|
}
|
|
|
|
// Output the result to the buffer and propagate the w channel as is.
|
|
// TODO: We could save bandwidth by doing this using a 111110 texture and storing the w in a different texture
|
|
_OutputFilteredBuffer0[COORD_TEXTURE2D_X(centerCoord)] = float4(colorSum0 / wSum);
|
|
_OutputFilteredBuffer1[COORD_TEXTURE2D_X(centerCoord)] = float4(colorSum1 / wSum, LOAD_TEXTURE2D_X(_InputNoisyBuffer1, centerCoord).w);
|
|
}
|
|
|
|
// History buffer input
|
|
TEXTURE2D_X(_HistoryBuffer0);
|
|
TEXTURE2D_X(_HistoryBuffer1);
|
|
// Depth buffer of the previous frame, this is either the full res depth or half res based on the variant of the shader
|
|
TEXTURE2D_X(_HistoryDepthTexture);
|
|
|
|
[numthreads(SSGI_FILTER_TILE_SIZE, SSGI_FILTER_TILE_SIZE, 1)]
|
|
void TEMPORAL_FILTER(uint3 dispatchThreadId : SV_DispatchThreadID
|
|
, uint2 groupThreadId : SV_GroupThreadID
|
|
, uint2 groupId : SV_GroupID)
|
|
{
|
|
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
|
|
|
|
int2 centerCoord = dispatchThreadId.xy;
|
|
|
|
// Read the color as early as possible
|
|
float4 color0 = LOAD_TEXTURE2D_X(_InputNoisyBuffer0, centerCoord);
|
|
float4 color1 = LOAD_TEXTURE2D_X(_InputNoisyBuffer1, centerCoord);
|
|
|
|
#if HALF_RES
|
|
// We need the full res coordinate for the inputs
|
|
uint2 fullResCoord = centerCoord * 2;
|
|
|
|
// Get the posinputs of the current version of the pixel
|
|
float depth = LOAD_TEXTURE2D_X(_DepthTexture, asuint(_DepthPyramidFirstMipLevelOffset) + centerCoord).r;
|
|
PositionInputs posInputs = GetPositionInput(fullResCoord, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, GetWorldToViewMatrix());
|
|
#else
|
|
// Get the posinputs of the current version of the pixel
|
|
float depth = LOAD_TEXTURE2D_X(_DepthTexture, centerCoord).r;
|
|
PositionInputs posInputs = GetPositionInput(centerCoord, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, GetWorldToViewMatrix());
|
|
#endif
|
|
|
|
// Initialize the output buffer in case of an early exit.
|
|
_OutputFilteredBuffer0[COORD_TEXTURE2D_X(centerCoord)] = color0;
|
|
_OutputFilteredBuffer1[COORD_TEXTURE2D_X(centerCoord)] = float4(color1.xyz, 1.0);
|
|
|
|
// If the current point we are processing is a background point or the whole history should be discarded for an other reason, we invalidate the history
|
|
if (depth == UNITY_RAW_FAR_CLIP_VALUE)
|
|
return;
|
|
|
|
// Decode the velocity of the pixel
|
|
float2 velocity = float2(0.0, 0.0);
|
|
#if HALF_RES
|
|
DecodeMotionVector(LOAD_TEXTURE2D_X(_CameraMotionVectorsTexture, (float2)fullResCoord), velocity);
|
|
#else
|
|
DecodeMotionVector(LOAD_TEXTURE2D_X(_CameraMotionVectorsTexture, (float2)centerCoord), velocity);
|
|
#endif
|
|
|
|
// Compute the pixel coordinate for the history tapping
|
|
int2 historyTapCoord = (int2)((posInputs.positionNDC - velocity) * _ScreenSize.xy);
|
|
#if HALF_RES
|
|
historyTapCoord *= 0.5;
|
|
// If the pixel was outside of the screen during the previous frame, invalidate the history
|
|
if (historyTapCoord.x >= _HalfScreenSize.x || historyTapCoord.x < 0
|
|
|| historyTapCoord.y >= _HalfScreenSize.y || historyTapCoord.y < 0)
|
|
return;
|
|
#else
|
|
// If the pixel was outside of the screen during the previous frame, invalidate the history
|
|
if (historyTapCoord.x >= _ScreenSize.x || historyTapCoord.x < 0
|
|
|| historyTapCoord.y >= _ScreenSize.y || historyTapCoord.y < 0)
|
|
return;
|
|
#endif
|
|
|
|
// Fetch the depth of the history pixel. If the history position was a background point, invalidate the history
|
|
float historyDepth = LOAD_TEXTURE2D_X(_HistoryDepthTexture, historyTapCoord).r;
|
|
|
|
// If the history was a background pixel, skip it
|
|
bool invalidHistory = false;
|
|
if (historyDepth == UNITY_RAW_FAR_CLIP_VALUE || _HistoryValidity == 0.0)
|
|
invalidHistory = true;
|
|
|
|
// Compute the world space position (from previous frame)
|
|
float3 historyPositionWS = ComputeWorldSpacePosition(posInputs.positionNDC - velocity, historyDepth, UNITY_MATRIX_PREV_I_VP);
|
|
|
|
// Real the normal data for this pixel
|
|
NormalData normalData;
|
|
DecodeFromNormalBuffer(centerCoord, normalData);
|
|
|
|
// Compute the max reprojection distance. This is evaluated as the max between a fixed radius value and an approximation of the footprint of the pixel.
|
|
float maxRadius = ComputeMaxReprojectionWorldRadius(posInputs.positionWS, normalData.normalWS, _PixelSpreadAngleTangent);
|
|
|
|
// Is it too far from the current position?
|
|
if (length(historyPositionWS - posInputs.positionWS) > maxRadius)
|
|
invalidHistory = true;
|
|
|
|
// Fetch history data
|
|
float4 history0 = LOAD_TEXTURE2D_X(_HistoryBuffer0, historyTapCoord);
|
|
float4 history1 = LOAD_TEXTURE2D_X(_HistoryBuffer1, historyTapCoord);
|
|
float sampleCount = history1.w;
|
|
|
|
// Accumulation factor that tells us how much we need to keep the history data
|
|
float accumulationFactor = 0.0;
|
|
|
|
// If the history is invalid
|
|
if (invalidHistory || sampleCount == 0.0)
|
|
{
|
|
// We only take the current value
|
|
accumulationFactor = 0.0;
|
|
|
|
// To avoid nan values
|
|
history0 = 0.0;
|
|
history1 = float4(0.501960784f, 0.501960784f, 0.0, 0.0);
|
|
sampleCount = 1.0;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise we compute the accumulation factor
|
|
accumulationFactor = sampleCount >= NUM_SAMPLE_LOOP ? EXPONENTIAL_ACCUMULATION_FACTOR : (sampleCount / (sampleCount + 1.0));
|
|
|
|
// Update the sample count
|
|
sampleCount = min(sampleCount + 1.0, NUM_SAMPLE_LOOP);
|
|
|
|
// If the history pixel was moving, we descide to throw partially the history
|
|
// TODO: Expose this as a parameter
|
|
if (color1.w > 0.0)
|
|
{
|
|
sampleCount = 5.0;
|
|
accumulationFactor = sampleCount / (sampleCount + 1.0);
|
|
}
|
|
}
|
|
|
|
// Do the accumulation based on the values we computed and store the new sample count in the w channel
|
|
_OutputFilteredBuffer0[COORD_TEXTURE2D_X(centerCoord)] = float4(color0 * (1.0 - accumulationFactor) + history0 * accumulationFactor);
|
|
_OutputFilteredBuffer1[COORD_TEXTURE2D_X(centerCoord)] = float4(color1.xyz * (1.0 - accumulationFactor) + history1.xyz * accumulationFactor, sampleCount);
|
|
}
|
|
|
|
[numthreads(SSGI_FILTER_TILE_SIZE, SSGI_FILTER_TILE_SIZE, 1)]
|
|
void COPY_HISTORY(uint3 dispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
|
|
|
|
if (any(dispatchThreadId.xy > uint2(_ScreenSize.xy)))
|
|
return; // Out of bounds, discard
|
|
|
|
_OutputFilteredBuffer0[COORD_TEXTURE2D_X(dispatchThreadId.xy)] = LOAD_TEXTURE2D_X(_InputNoisyBuffer0, dispatchThreadId.xy);
|
|
_OutputFilteredBuffer1[COORD_TEXTURE2D_X(dispatchThreadId.xy)] = LOAD_TEXTURE2D_X(_InputNoisyBuffer1, dispatchThreadId.xy);
|
|
}
|