You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
14 KiB
307 lines
14 KiB
#pragma kernel TemporalAccumulationFullRes TEMPORAL_ACCUMULATION=TemporalAccumulationFullRes
|
|
#pragma kernel TemporalAccumulationHalfRes TEMPORAL_ACCUMULATION=TemporalAccumulationHalfRes HALF_RESOLUTION
|
|
#pragma kernel CopyHistory
|
|
#pragma kernel BilateralFilterH_FR BILATERAL_FILTER=BilateralFilterH_FR
|
|
#pragma kernel BilateralFilterV_FR BILATERAL_FILTER=BilateralFilterV_FR FINAL_PASS
|
|
#pragma kernel BilateralFilterH_HR BILATERAL_FILTER=BilateralFilterH_HR HALF_RESOLUTION
|
|
#pragma kernel BilateralFilterV_HR BILATERAL_FILTER=BilateralFilterV_HR FINAL_PASS HALF_RESOLUTION
|
|
|
|
#pragma only_renderers d3d11 xboxseries ps5
|
|
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
|
|
#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/RenderPipeline/Raytracing/Shaders/RaytracingSampling.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.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"
|
|
#define BILATERAL_ROUGHNESS
|
|
#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"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/ShaderVariablesRaytracing.cs.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Raytracing/Shaders/RayTracingCommon.hlsl"
|
|
|
|
// Tile size of this compute
|
|
#define REFLECTION_FILTER_TILE_SIZE 8
|
|
|
|
// #pragma enable_d3d11_debug_symbols
|
|
|
|
// Thereshold at which we decide to reject the reflection history
|
|
#define REFLECTION_HISTORY_REJECTION_THRESHOLD 0.75
|
|
// Threshold at which we go from accumulation to clampiong
|
|
#define ROUGHNESS_ACCUMULATION_THRESHOLD 0.5
|
|
|
|
// Input textures
|
|
TEXTURE2D_X(_DenoiseInputTexture);
|
|
TEXTURE2D_X(_HistoryBuffer);
|
|
TEXTURE2D_X(_HistoryDepthTexture);
|
|
// Value that tells us if the current history should be discarded based on scene-level data
|
|
float _HistoryValidity;
|
|
// Current inverse resolution of the history buffer
|
|
float2 _HistoryBufferSize;
|
|
// Resolution at which the effect is rendered (Half the _Screensize if half res)
|
|
float4 _CurrentEffectResolution;
|
|
float _PixelSpreadAngleTangent;
|
|
int _AffectSmoothSurfaces;
|
|
int _SingleReflectionBounce;
|
|
// Contains history buffer size in xy and the uv scale in zw
|
|
float4 _HistorySizeAndScale;
|
|
|
|
// Output texture
|
|
RW_TEXTURE2D_X(float4, _DenoiseOutputTextureRW);
|
|
RW_TEXTURE2D_X(float, _SampleCountTextureRW);
|
|
|
|
[numthreads(REFLECTION_FILTER_TILE_SIZE, REFLECTION_FILTER_TILE_SIZE, 1)]
|
|
void TEMPORAL_ACCUMULATION(uint3 dispatchThreadId : SV_DispatchThreadID, uint2 groupThreadId : SV_GroupThreadID, uint2 groupId : SV_GroupID)
|
|
{
|
|
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
|
|
|
|
// Fetch the current pixel coordinate
|
|
uint2 mainCoord = groupId * REFLECTION_FILTER_TILE_SIZE + groupThreadId;
|
|
|
|
// The source coordinate for the normal and depth depends if we are in half res or not
|
|
#ifdef HALF_RESOLUTION
|
|
uint2 geometryCoords = ComputeSourceCoordinates(mainCoord, _RayTracingCheckerIndex);
|
|
#else
|
|
uint2 geometryCoords = mainCoord;
|
|
#endif
|
|
|
|
// Fetch the depth
|
|
float depth = LOAD_TEXTURE2D_X(_DepthTexture, geometryCoords).x;
|
|
|
|
// If the history is flagged as invalid or this is a a background pixel write the noisy value and leave right away
|
|
if (_HistoryValidity == 0.0 || depth == UNITY_RAW_FAR_CLIP_VALUE)
|
|
{
|
|
_DenoiseOutputTextureRW[COORD_TEXTURE2D_X(mainCoord)] = LOAD_TEXTURE2D_X(_DenoiseInputTexture, mainCoord);
|
|
_SampleCountTextureRW[COORD_TEXTURE2D_X(mainCoord)] = 0;
|
|
return;
|
|
}
|
|
|
|
// Fetch the position of the current pixel
|
|
PositionInputs posInputs = GetPositionInput(geometryCoords, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, GetWorldToViewMatrix());
|
|
|
|
// Read the depth and velocity vectors
|
|
float2 velocity;
|
|
DecodeMotionVector(LOAD_TEXTURE2D_X(_CameraMotionVectorsTexture, geometryCoords), velocity);
|
|
|
|
// Real the normal data for this pixel
|
|
NormalData normalData;
|
|
DecodeFromNormalBuffer(geometryCoords, normalData);
|
|
|
|
// Compute the current and history UV coordinates
|
|
float2 currentUV = (mainCoord + 0.5f) * _ScreenSize.zw;
|
|
|
|
// Fetch the current and history values and apply the exposition to it.
|
|
float3 currentColor = LOAD_TEXTURE2D_X(_DenoiseInputTexture, mainCoord).xyz * GetCurrentExposureMultiplier();
|
|
if (_AffectSmoothSurfaces == 0 && _SingleReflectionBounce == 1 && PerceptualRoughnessToPerceptualSmoothness(normalData.perceptualRoughness) > 0.99)
|
|
{
|
|
_DenoiseOutputTextureRW[COORD_TEXTURE2D_X(mainCoord)] = float4(currentColor * GetInverseCurrentExposureMultiplier(), LOAD_TEXTURE2D_X(_DenoiseInputTexture, mainCoord).w);
|
|
_SampleCountTextureRW[COORD_TEXTURE2D_X(mainCoord)] = 1;
|
|
return;
|
|
}
|
|
|
|
// Compute the pixel coordinate for the history tapping
|
|
float2 historyUVUnscaled = posInputs.positionNDC - velocity;
|
|
|
|
#ifdef HALF_RESOLUTION
|
|
float2 historyTapCoord = (float2)((historyUVUnscaled * _HistorySizeAndScale.xy + 0.5) * 0.5);
|
|
float2 historyUV = historyTapCoord / _HistorySizeAndScale.xy * _HistorySizeAndScale.zw;
|
|
#else
|
|
float2 historyTapCoord = (float2)(historyUVUnscaled * _HistorySizeAndScale.xy);
|
|
float2 historyUV = historyUVUnscaled * _HistorySizeAndScale.zw;
|
|
#endif
|
|
|
|
float4 historyRaw = SAMPLE_TEXTURE2D_X_LOD(_HistoryBuffer, s_linear_clamp_sampler, historyUV, 0);
|
|
float3 history = historyRaw.xyz * GetCurrentExposureMultiplier();
|
|
|
|
// If the pixel was outside of the screen during the previous frame, invalidate the history
|
|
bool canBeReprojected = true;
|
|
if (historyTapCoord.x >= _CurrentEffectResolution.x || historyTapCoord.x < 0
|
|
|| historyTapCoord.y >= _CurrentEffectResolution.y || historyTapCoord.y < 0)
|
|
canBeReprojected = false;
|
|
|
|
#ifdef HALF_RESOLUTION
|
|
float2 historyDepthUV = ((float2)(mainCoord * 2.0 + 0.5f) - velocity * _CurrentEffectResolution.xy * 2.0f) * _HistoryBufferSize.xy;
|
|
#else
|
|
float2 historyDepthUV = historyUV;
|
|
#endif
|
|
|
|
// Fetch the depth of the history pixel. If the history position was a background point, invalidate the history
|
|
float historyDepth = SAMPLE_TEXTURE2D_X_LOD(_HistoryDepthTexture, s_linear_clamp_sampler, historyDepthUV, 0).r;
|
|
if (historyDepth == UNITY_RAW_FAR_CLIP_VALUE)
|
|
canBeReprojected = false;
|
|
|
|
// Compute the world space position
|
|
PositionInputs posInput = GetPositionInput(geometryCoords, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
|
|
|
|
// Compute the world space position (from previous frame)
|
|
float3 historyPositionWS = ComputeWorldSpacePosition(posInput.positionNDC - velocity, historyDepth, UNITY_MATRIX_PREV_I_VP);
|
|
|
|
// Compute the max world radius that we consider acceptable for history reprojection
|
|
float maxRadius = ComputeMaxReprojectionWorldRadius(posInput.positionWS, normalData.normalWS, _PixelSpreadAngleTangent);
|
|
|
|
// Is it too far from the current position?
|
|
if (length(historyPositionWS - posInput.positionWS) > maxRadius)
|
|
canBeReprojected = false;
|
|
|
|
// Depending on the roughness of the surface run one or the other temporal reprojection
|
|
float3 result;
|
|
if (normalData.perceptualRoughness > ROUGHNESS_ACCUMULATION_THRESHOLD)
|
|
{
|
|
|
|
|
|
float sampleCount = historyRaw.w;
|
|
if (canBeReprojected && sampleCount != 0.0)
|
|
{
|
|
float accumulationFactor = sampleCount >= 8.0 ? 0.93 : (sampleCount / (sampleCount + 1.0));
|
|
result = (currentColor * (1.0 - accumulationFactor) + history * accumulationFactor);
|
|
sampleCount = max(sampleCount + 1.0, 8.0);
|
|
}
|
|
else
|
|
{
|
|
result = currentColor;
|
|
sampleCount = 1.0;
|
|
}
|
|
|
|
// Update the sample count
|
|
historyRaw.w = sampleCount;
|
|
}
|
|
else
|
|
{
|
|
float exposureMultiplier = GetCurrentExposureMultiplier();
|
|
float3 topLeft = LOAD_TEXTURE2D_X(_DenoiseInputTexture, clamp(mainCoord - int2(1, 1), int2(0, 0), _CurrentEffectResolution.xy)).xyz;
|
|
float3 bottomRight = LOAD_TEXTURE2D_X(_DenoiseInputTexture, clamp(mainCoord + int2(1, 1), int2(0, 0), _CurrentEffectResolution.xy)).xyz;
|
|
topLeft *= exposureMultiplier;
|
|
bottomRight *= exposureMultiplier;
|
|
|
|
float3 corners = 4.0 * (topLeft + bottomRight) - 2.0 * currentColor;
|
|
|
|
float3 color = clamp(currentColor, 0.0, CLAMP_MAX);
|
|
|
|
float3 average = FastTonemap((corners + color) / 7.0);
|
|
|
|
topLeft = FastTonemap(topLeft);
|
|
bottomRight = FastTonemap(bottomRight);
|
|
color = FastTonemap(color);
|
|
|
|
float colorLuma = Luminance(color);
|
|
float averageLuma = Luminance(average);
|
|
float velocityLength = length(velocity);
|
|
float nudge = lerp(4.0, 0.25, saturate(velocityLength * 100.0)) * abs(averageLuma - colorLuma);
|
|
|
|
float3 minimum = min(bottomRight, topLeft) - nudge;
|
|
float3 maximum = max(topLeft, bottomRight) + nudge;
|
|
|
|
history = FastTonemap(history);
|
|
|
|
// Clip history samples
|
|
history = DirectClipToAABB(history, minimum, maximum);
|
|
|
|
// Blend color & history
|
|
// Feedback weight from unbiased luminance diff (Timothy Lottes)
|
|
float historyLuma = Luminance(history);
|
|
float diff = abs(colorLuma - historyLuma) / Max3(colorLuma, historyLuma, 0.2);
|
|
float weight = 1.0 - diff;
|
|
const float feedbackMin = 0.96;
|
|
const float feedbackMax = 0.91;
|
|
float feedback = lerp(feedbackMin, feedbackMax, weight * weight);
|
|
|
|
color = FastTonemapInvert(lerp(color, history, feedback));
|
|
result = canBeReprojected ? clamp(color, 0.0, CLAMP_MAX) : currentColor;
|
|
historyRaw.w = 0.0;
|
|
}
|
|
|
|
_DenoiseOutputTextureRW[COORD_TEXTURE2D_X(mainCoord)] = float4(result * GetInverseCurrentExposureMultiplier(), LOAD_TEXTURE2D_X(_DenoiseInputTexture, mainCoord).w);
|
|
_SampleCountTextureRW[COORD_TEXTURE2D_X(mainCoord)] = historyRaw.w;
|
|
}
|
|
|
|
[numthreads(REFLECTION_FILTER_TILE_SIZE, REFLECTION_FILTER_TILE_SIZE, 1)]
|
|
void CopyHistory(uint3 dispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
|
|
|
|
if (any(dispatchThreadId.xy > uint2(_ScreenSize.xy)))
|
|
return; // Out of bounds, discard
|
|
|
|
float4 currentColor = LOAD_TEXTURE2D_X(_DenoiseInputTexture, dispatchThreadId.xy);
|
|
// We need to apply a step function on the blend factor to evaluate the validity of the history (if it is stricly higher than 0.0 then its valid)
|
|
_DenoiseOutputTextureRW[COORD_TEXTURE2D_X(dispatchThreadId.xy)] = float4(currentColor.xyz, _SampleCountTextureRW[COORD_TEXTURE2D_X(dispatchThreadId.xy)].x);
|
|
}
|
|
|
|
int _DenoiserFilterRadius;
|
|
float _RoughnessBasedDenoising;
|
|
TEXTURE2D(_ReflectionFilterMapping);
|
|
|
|
#define BILATERAL_FILTER_SIGMA 0.9
|
|
|
|
// Separated bilateral filter (two passes, each with 2*Radius taps)
|
|
[numthreads(REFLECTION_FILTER_TILE_SIZE, REFLECTION_FILTER_TILE_SIZE, 1)]
|
|
void BILATERAL_FILTER(uint3 dispatchThreadId : SV_DispatchThreadID, uint2 groupThreadId : SV_GroupThreadID, uint2 groupId : SV_GroupID)
|
|
{
|
|
UNITY_XR_ASSIGN_VIEW_INDEX(dispatchThreadId.z);
|
|
|
|
// Fetch the current pixel coordinate
|
|
uint2 centerCoord = groupId * REFLECTION_FILTER_TILE_SIZE + groupThreadId;
|
|
|
|
// Based on which pass of the filter we are doing, adjust the sampling direction
|
|
#if FINAL_PASS
|
|
const uint2 passIncr = uint2(1, 0);
|
|
#else
|
|
const uint2 passIncr = uint2(0, 1);
|
|
#endif
|
|
|
|
#if HALF_RESOLUTION
|
|
uint2 geometrCoords = ComputeSourceCoordinates(centerCoord, _RayTracingCheckerIndex);
|
|
#else
|
|
uint2 geometrCoords = centerCoord;
|
|
#endif
|
|
// Tap the central pixel coordinates
|
|
const BilateralData center = TapBilateralData(geometrCoords);
|
|
|
|
// Compute the effective radius we should be using for the filtering
|
|
const float3 viewWS = GetWorldSpaceNormalizeViewDir(center.position);
|
|
float2 mappingUV = float2(dot(viewWS, center.normal), center.roughness);
|
|
float2 radiusScale = lerp(1.0f, SAMPLE_TEXTURE2D_LOD(_ReflectionFilterMapping, s_linear_clamp_sampler, mappingUV, 0.0f).xy, _RoughnessBasedDenoising);
|
|
#if FINAL_PASS
|
|
const float radius = _DenoiserFilterRadius * radiusScale.x;
|
|
#else
|
|
const float radius = _DenoiserFilterRadius * radiusScale.y;
|
|
#endif
|
|
const float sigma = radius * BILATERAL_FILTER_SIGMA;
|
|
const int effectiveRadius = min(sigma * 2.0, radius);
|
|
|
|
// Store the intermediate result
|
|
float3 finalColor = LOAD_TEXTURE2D_X(_DenoiseInputTexture, centerCoord).xyz;
|
|
|
|
// If this pixels does not have ray traced reflections anyway, just skip it.
|
|
if (_RaytracingReflectionMinSmoothness <= PerceptualRoughnessToPerceptualSmoothness(center.roughness))
|
|
{
|
|
// Initialize variables for accumulation
|
|
float3 colorSum = float3(0.0, 0.0, 0.0);
|
|
float wSum = 0.0;
|
|
|
|
int2 tapCoord = centerCoord - effectiveRadius * passIncr;
|
|
for (int r = -effectiveRadius; r <= effectiveRadius; ++r, tapCoord += passIncr)
|
|
{
|
|
// Make sure the pixel coord we are trying to use is in the screen (not out of bounds)
|
|
if (tapCoord.x >= _CurrentEffectResolution.x || tapCoord.x < 0 || tapCoord.y >= _CurrentEffectResolution.y || tapCoord.y < 0)
|
|
continue;
|
|
|
|
#if HALF_RESOLUTION
|
|
uint2 tapGeometryCoords = ComputeSourceCoordinates(tapCoord, _RayTracingCheckerIndex);
|
|
#else
|
|
uint2 tapGeometryCoords = tapCoord;
|
|
#endif
|
|
// Compute the weight (skip computation for the center)
|
|
const BilateralData tapData = TapBilateralData(tapGeometryCoords);
|
|
float w = r ? gaussian(r, sigma) * ComputeBilateralWeight(center, tapData) : 1.0;
|
|
w = _RaytracingReflectionMinSmoothness > PerceptualRoughnessToPerceptualSmoothness(tapData.roughness) ? 0.0 : w;
|
|
colorSum += LOAD_TEXTURE2D_X(_DenoiseInputTexture, tapCoord).xyz * w;
|
|
wSum += w;
|
|
}
|
|
// Normalize the result
|
|
finalColor = colorSum / wSum;
|
|
}
|
|
|
|
_DenoiseOutputTextureRW[COORD_TEXTURE2D_X(centerCoord)] = float4(finalColor, LOAD_TEXTURE2D_X(_DenoiseInputTexture, centerCoord).w);
|
|
}
|