diff --git a/CMakeLists.txt b/CMakeLists.txt index e6f68aa9..922a0759 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,6 +175,7 @@ if (NOT NRD_DISABLE_SHADER_COMPILATION) --sourceDir "Shaders/Source" --allResourcesBound --WX + --vulkanVersion 1.2 -c Shaders.cfg -o "${NRD_SHADERS_PATH}" -I "External/MathLib" diff --git a/External/MathLib b/External/MathLib index 3940a7be..407ecd0d 160000 --- a/External/MathLib +++ b/External/MathLib @@ -1 +1 @@ -Subproject commit 3940a7bef553c406e617c94ac6e72042f1054f4e +Subproject commit 407ecd0d1892d12ee1ec98c3d46cbeed73b79a0d diff --git a/Include/NRD.h b/Include/NRD.h index 582d44ce..06e2aa8d 100644 --- a/Include/NRD.h +++ b/Include/NRD.h @@ -29,8 +29,8 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. #define NRD_VERSION_MAJOR 4 #define NRD_VERSION_MINOR 6 -#define NRD_VERSION_BUILD 0 -#define NRD_VERSION_DATE "19 March 2024" +#define NRD_VERSION_BUILD 1 +#define NRD_VERSION_DATE "25 March 2024" #if defined(_MSC_VER) #define NRD_CALL __fastcall diff --git a/README.md b/README.md index 8e4dde5d..cf4dace0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# NVIDIA REAL-TIME DENOISERS v4.6.0 (NRD) +# NVIDIA REAL-TIME DENOISERS v4.6.1 (NRD) [![Build NRD SDK](https://github.com/NVIDIAGameWorks/RayTracingDenoiser/actions/workflows/build.yml/badge.svg)](https://github.com/NVIDIAGameWorks/RayTracingDenoiser/actions/workflows/build.yml) diff --git a/Resources/Version.h b/Resources/Version.h index 799314d6..99879373 100644 --- a/Resources/Version.h +++ b/Resources/Version.h @@ -23,6 +23,6 @@ Versioning rules: #define VERSION_MAJOR 4 #define VERSION_MINOR 6 -#define VERSION_BUILD 0 +#define VERSION_BUILD 1 #define VERSION_STRING STR(VERSION_MAJOR.VERSION_MINOR.VERSION_BUILD encoding=NRD_NORMAL_ENCODING.NRD_ROUGHNESS_ENCODING) diff --git a/Shaders/Include/Common.hlsli b/Shaders/Include/Common.hlsli index dc5cf46e..504e1d6c 100644 --- a/Shaders/Include/Common.hlsli +++ b/Shaders/Include/Common.hlsli @@ -53,6 +53,7 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. #define NRD_EXP_WEIGHT_DEFAULT_SCALE 3.0 #define NRD_ROUGHNESS_SENSITIVITY 0.01 // smaller => more sensitive #define NRD_CURVATURE_Z_THRESHOLD 0.1 // normalized % +#define NRD_MAX_ALLOWED_VIRTUAL_MOTION_ACCELERATION 15.0 // keep relatively high to avoid ruining concave mirrors // IMPORTANT: if == 1, then for 0-roughness "GetEncodingAwareNormalWeight" can return values < 1 even for same normals due to data re-packing // IMPORTANT: suits for REBLUR and RELAX because both use RGBA8 normals internally @@ -288,9 +289,19 @@ float GetColorCompressionExposureForSpatialPasses( float roughness ) #endif } +float GetSpecLobeTanHalfAngle( float roughness, float percentOfVolume = 0.75 ) +{ + // TODO: ideally should migrate to fixed "STL::ImportanceSampling::GetSpecularLobeTanHalfAngle", but since + // denoisers behavior have been tuned for the old version, let's continue to use it in critical places + roughness = saturate( roughness ); + percentOfVolume = saturate( percentOfVolume ); + + return roughness * roughness * percentOfVolume / ( 1.0 - percentOfVolume + NRD_EPS ); +} + // Thin lens -float ApplyThinLensEquation( float NoV, float hitDist, float curvature ) +float ApplyThinLensEquation( float hitDist, float curvature ) { /* https://computergraphics.stackexchange.com/questions/1718/what-is-the-simplest-way-to-compute-principal-curvature-for-a-mesh-triangle @@ -305,39 +316,28 @@ float ApplyThinLensEquation( float NoV, float hitDist, float curvature ) convex : O(-), I(+), C(+) concave : O(-), I(+ or -), C(-) - Why NoV? - hitDist is not O, we need to find projection to the axis: - O = hitDist * NoV - hitDistFocused is not I, we need to reproject it back to the view direction: - hitDistFocused = I / NoV - Combine: 2C = 1 / O + 1 / I 1 / I = 2C - 1 / O 1 / I = ( 2CO - 1 ) / O I = O / ( 2CO - 1 ) - I = [ ( O * NoV ) / ( 2CO * NoV - 1 ) ] / NoV - I = O / ( 2CO * NoV - 1 ) - O is always negative, while hit distance is always positive: - I = -O / ( -2CO * NoV - 1 ) - I = O / ( 2CO * NoV + 1 ) + I = -O / ( -2CO - 1 ) + I = O / ( 2CO + 1 ) Interactive graph: https://www.desmos.com/calculator/dn9spdgwiz */ - // TODO: dropping NoV improves behavior on curved surfaces in general ( see 76, 148, b7, b22, b26 ), but test 133 - // ( low curvature surface observed at grazing angle ) looks significantly worse, especially if motion is accelerated - float hitDistFocused = hitDist / ( 2.0 * curvature * hitDist * NoV + 1.0 ); + float hitDistFocused = hitDist / ( 2.0 * curvature * hitDist + 1.0 ); return hitDistFocused; } -float3 GetXvirtual( float NoV, float hitDist, float curvature, float3 X, float3 Xprev, float3 V, float dominantFactor ) +float3 GetXvirtual( float hitDist, float curvature, float3 X, float3 Xprev, float3 V, float dominantFactor ) { - float hitDistFocused = ApplyThinLensEquation( NoV, hitDist, curvature ); + float hitDistFocused = ApplyThinLensEquation( hitDist, curvature ); // Only hit distance is provided, not real motion in the virtual world. If the virtual position is close to the // surface due to focusing, better to replace current position with previous position because surface motion is known. diff --git a/Shaders/Include/REBLUR_Common.hlsli b/Shaders/Include/REBLUR_Common.hlsli index 6b087ff1..a3b3321c 100644 --- a/Shaders/Include/REBLUR_Common.hlsli +++ b/Shaders/Include/REBLUR_Common.hlsli @@ -368,10 +368,10 @@ float2x3 GetKernelBasis( float3 D, float3 N, float NoD, float roughness = 1.0, f // Weight parameters -float GetNormalWeightParams( float nonLinearAccumSpeed, float fraction, float roughness = 1.0 ) +float GetNormalWeightParams( float nonLinearAccumSpeed, float roughness = 1.0 ) { - float angle = STL::ImportanceSampling::GetSpecularLobeHalfAngle( roughness ); - angle *= lerp( saturate( fraction ), 1.0, nonLinearAccumSpeed ); // TODO: use as "percentOfVolume" instead? + float percentOfVolume = REBLUR_MAX_PERCENT_OF_LOBE_VOLUME * lerp( gLobeAngleFraction, 1.0, nonLinearAccumSpeed ); + float angle = atan( STL::ImportanceSampling::GetSpecularLobeTanHalfAngle( roughness, percentOfVolume ) ); return 1.0 / max( angle, NRD_NORMAL_ULP ); } diff --git a/Shaders/Include/REBLUR_Common_DiffuseSpatialFilter.hlsli b/Shaders/Include/REBLUR_Common_DiffuseSpatialFilter.hlsli index b639bb44..72b3b474 100644 --- a/Shaders/Include/REBLUR_Common_DiffuseSpatialFilter.hlsli +++ b/Shaders/Include/REBLUR_Common_DiffuseSpatialFilter.hlsli @@ -38,8 +38,6 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. fractionScale = REBLUR_POST_BLUR_FRACTION_SCALE; #endif - float lobeAngleFractionScale = saturate( gLobeAngleFraction * fractionScale ); - float hitDistScale = _REBLUR_GetHitDistanceNormalization( viewZ, gHitDistParams, 1.0 ); float hitDist = ExtractHitDist( diff ) * hitDistScale; @@ -78,7 +76,7 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. // Weights float2 geometryWeightParams = GetGeometryWeightParams( gPlaneDistSensitivity, frustumSize, Xv, Nv, diffNonLinearAccumSpeed ); - float normalWeightParams = GetNormalWeightParams( diffNonLinearAccumSpeed, lobeAngleFractionScale ); + float normalWeightParams = GetNormalWeightParams( diffNonLinearAccumSpeed ) / fractionScale; float2 px = float2( geometryWeightParams.x, normalWeightParams ); float2 py = float2( geometryWeightParams.y, 0.0 ); diff --git a/Shaders/Include/REBLUR_Common_SpecularSpatialFilter.hlsli b/Shaders/Include/REBLUR_Common_SpecularSpatialFilter.hlsli index d91e6faf..cac1136c 100644 --- a/Shaders/Include/REBLUR_Common_SpecularSpatialFilter.hlsli +++ b/Shaders/Include/REBLUR_Common_SpecularSpatialFilter.hlsli @@ -42,15 +42,14 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. fractionScale = REBLUR_POST_BLUR_FRACTION_SCALE; #endif - float lobeAngleFractionScale = saturate( gLobeAngleFraction * fractionScale ); - float roughnessFractionScale = saturate( gRoughnessFraction * fractionScale ); + float roughnessFractionScaled = saturate( gRoughnessFraction * fractionScale ); float hitDist = ExtractHitDist( spec ) * _REBLUR_GetHitDistanceNormalization( viewZ, gHitDistParams, roughness ); // Min blur radius float4 Dv = STL::ImportanceSampling::GetSpecularDominantDirection( Nv, Vv, roughness, STL_SPECULAR_DOMINANT_DIRECTION_G2 ); float NoD = abs( dot( Nv, Dv.xyz ) ); - float lobeTanHalfAngle = STL::ImportanceSampling::GetSpecularLobeTanHalfAngle( roughness ); + float lobeTanHalfAngle = STL::ImportanceSampling::GetSpecularLobeTanHalfAngle( roughness, REBLUR_MAX_PERCENT_OF_LOBE_VOLUME ); float lobeRadius = hitDist * NoD * lobeTanHalfAngle; float minBlurRadius = lobeRadius / PixelRadiusToWorld( gUnproject, gOrthoMode, 1.0, viewZ + hitDist * Dv.w ); @@ -96,8 +95,8 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. // Weights float2 geometryWeightParams = GetGeometryWeightParams( gPlaneDistSensitivity, frustumSize, Xv, Nv, specNonLinearAccumSpeed ); - float normalWeightParams = GetNormalWeightParams( specNonLinearAccumSpeed, lobeAngleFractionScale, roughness ); - float2 roughnessWeightParams = GetRoughnessWeightParams( roughness, roughnessFractionScale ); + float normalWeightParams = GetNormalWeightParams( specNonLinearAccumSpeed, roughness ) / fractionScale; + float2 roughnessWeightParams = GetRoughnessWeightParams( roughness, roughnessFractionScaled ); float3 px = float3( geometryWeightParams.x, normalWeightParams, roughnessWeightParams.x ); float3 py = float3( geometryWeightParams.y, 0.0, roughnessWeightParams.y ); diff --git a/Shaders/Include/REBLUR_Config.hlsli b/Shaders/Include/REBLUR_Config.hlsli index 7aef79cb..bb48defb 100644 --- a/Shaders/Include/REBLUR_Config.hlsli +++ b/Shaders/Include/REBLUR_Config.hlsli @@ -67,6 +67,7 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. #define REBLUR_HIT_DIST_MIN_WEIGHT( smc ) ( 0.1 * smc ) // was 0.1 +#define REBLUR_MAX_PERCENT_OF_LOBE_VOLUME 0.75 #define REBLUR_VIRTUAL_MOTION_PREV_PREV_WEIGHT_ITERATION_NUM 2 #define REBLUR_COLOR_CLAMPING_SIGMA_SCALE 2.0 // using smaller values leads to bias if camera rotates slowly due to reprojection instabilities #define REBLUR_FIREFLY_SUPPRESSOR_MAX_RELATIVE_INTENSITY float2( 10.0, 1.1 ) diff --git a/Shaders/Include/REBLUR_HistoryFix.hlsli b/Shaders/Include/REBLUR_HistoryFix.hlsli index 014b4b88..969494b7 100644 --- a/Shaders/Include/REBLUR_HistoryFix.hlsli +++ b/Shaders/Include/REBLUR_HistoryFix.hlsli @@ -118,7 +118,7 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : // Parameters float diffNonLinearAccumSpeed = 1.0 / ( 1.0 + frameNum.x ); - float diffNormalWeightParam = GetNormalWeightParams( diffNonLinearAccumSpeed, 1.0 ); + float diffNormalWeightParam = GetNormalWeightParams( diffNonLinearAccumSpeed ); float2 diffGeometryWeightParams = GetGeometryWeightParams( gPlaneDistSensitivity * slopeScale, frustumSize, Xv, Nv, diffNonLinearAccumSpeed ); float sumd = 1.0 + frameNum.x; @@ -305,11 +305,7 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : float specNonLinearAccumSpeed = 1.0 / ( 1.0 + frameNum.y ); float hitDistNormAtCenter = ExtractHitDist( spec ); - float lobeEnergy = lerp( 0.75, 0.85, specNonLinearAccumSpeed ); - float lobeHalfAngle = STL::ImportanceSampling::GetSpecularLobeHalfAngle( roughness, lobeEnergy ); // up to 85% energy to depend less on normal weight - lobeHalfAngle *= specNonLinearAccumSpeed; - - float specNormalWeightParam = 1.0 / max( lobeHalfAngle, NRD_NORMAL_ULP ); + float specNormalWeightParam = GetNormalWeightParams( specNonLinearAccumSpeed, roughness ); float2 specGeometryWeightParams = GetGeometryWeightParams( gPlaneDistSensitivity * slopeScale, frustumSize, Xv, Nv, specNonLinearAccumSpeed ); float2 relaxedRoughnessWeightParams = GetRelaxedRoughnessWeightParams( roughness * roughness, sqrt( gRoughnessFraction ) ); float2 hitDistanceWeightParams = GetHitDistanceWeightParams( hitDistNormAtCenter, specNonLinearAccumSpeed, roughness ); diff --git a/Shaders/Include/REBLUR_HitDistReconstruction.hlsli b/Shaders/Include/REBLUR_HitDistReconstruction.hlsli index 1d232523..2b63a2da 100644 --- a/Shaders/Include/REBLUR_HitDistReconstruction.hlsli +++ b/Shaders/Include/REBLUR_HitDistReconstruction.hlsli @@ -69,8 +69,8 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : float2 geometryWeightParams = GetGeometryWeightParams( gPlaneDistSensitivity, frustumSize, Xv, Nv, 1.0 ); float2 relaxedRoughnessWeightParams = GetRelaxedRoughnessWeightParams( roughness * roughness ); - float diffNormalWeightParam = GetNormalWeightParams( 1.0, 1.0, 1.0 ); - float specNormalWeightParam = GetNormalWeightParams( 1.0, 1.0, roughness ); + float diffNormalWeightParam = GetNormalWeightParams( 1.0 ); + float specNormalWeightParam = GetNormalWeightParams( 1.0, roughness ); // Hit distance reconstruction float2 sum = 1000.0 * float2( center.xy != 0.0 ); diff --git a/Shaders/Include/REBLUR_TemporalAccumulation.hlsli b/Shaders/Include/REBLUR_TemporalAccumulation.hlsli index 73a0b2f1..86e8cba4 100644 --- a/Shaders/Include/REBLUR_TemporalAccumulation.hlsli +++ b/Shaders/Include/REBLUR_TemporalAccumulation.hlsli @@ -321,13 +321,9 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : - by design: curvature = 0 on static objects if camera is static - quantization errors hurt - curvature on bumpy surfaces is just wrong, pulling virtual positions into a surface and introducing lags - - bad reprojection if curvature changes signs under motion - - code below works better on smooth curved surfaces, but not in tests: 174, 175 ( without normal map ) - BEFORE: hitDistFocused = hitDist / ( 2.0 * curvature * hitDist * NoV + 1.0 ); - AFTER: hitDistFocused = hitDist / ( 2.0 * curvature * hitDist / NoV + 1.0 ); + - suboptimal reprojection if curvature changes signs under motion */ float curvature; - float2 vmbDelta; { // IMPORTANT: this code allows to get non-zero parallax on objects attached to the camera float2 uvForZeroParallax = gOrthoMode == 0.0 ? smbPixelUv : pixelUv; @@ -401,41 +397,29 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : float s = STL::Math::LinearStep( NRD_NORMAL_ENCODING_ERROR, 2.0 * NRD_NORMAL_ENCODING_ERROR, d ); curvature *= s; - // Correction #2 - very negative inconsistent with previous frame curvature blows up reprojection ( tests 164, 171 - 176 ) - float2 uv1 = STL::Geometry::GetScreenUv( gWorldToClipPrev, X - V * ApplyThinLensEquation( NoV, hitDistForTracking, curvature ) ); + // Correction #2 - this is needed if camera is "inside" a concave mirror ( tests 133, 164, 171 - 176 ) + if( length( X ) < -1.0 / curvature ) // TODO: test 78 + curvature *= NoV; + + // Correction #3 - very negative inconsistent with previous frame curvature blows up reprojection ( tests 164, 171 - 176 ) + float2 uv1 = STL::Geometry::GetScreenUv( gWorldToClipPrev, X - V * ApplyThinLensEquation( hitDistForTracking, curvature ) ); float2 uv2 = STL::Geometry::GetScreenUv( gWorldToClipPrev, X ); float a = length( ( uv1 - uv2 ) * gRectSize ); - curvature *= float( a < 3.0 * deltaUvLen + gRectSizeInv.x ); // TODO:it's a hack, incompatible with concave mirrors ( tests 22b, 23b, 25b ) - - // Smooth virtual motion delta ( omitting huge values if curvature is negative and curvature radius is very small ) - float3 Xvirtual = GetXvirtual( NoV, hitDistForTracking, max( curvature, 0.0 ), X, Xprev, V, Dfactor ); - float2 vmbPixelUv = STL::Geometry::GetScreenUv( gWorldToClipPrev, Xvirtual ); - vmbDelta = vmbPixelUv - smbPixelUv; + curvature *= float( a < NRD_MAX_ALLOWED_VIRTUAL_MOTION_ACCELERATION * deltaUvLen + gRectSizeInv.x ); } // Virtual motion - coordinates - float3 Xvirtual = GetXvirtual( NoV, hitDistForTracking, curvature, X, Xprev, V, Dfactor ); + float3 Xvirtual = GetXvirtual( hitDistForTracking, curvature, X, Xprev, V, Dfactor ); float2 vmbPixelUv = STL::Geometry::GetScreenUv( gWorldToClipPrev, Xvirtual ); + float2 vmbDelta = vmbPixelUv - smbPixelUv; float vmbPixelsTraveled = length( vmbDelta * gRectSize ); float XvirtualLength = length( Xvirtual ); - // Estimate how many pixels are traveled by virtual motion - how many radians can it be? - // IMPORTANT: if curvature angle is multiplied by path length then we can get an angle exceeding 2 * PI, what is impossible. The max - // angle is PI ( most left and most right points on a hemisphere ), it can be achieved by using "tan" instead of angle. - float pixelSize = PixelRadiusToWorld( gUnproject, gOrthoMode, 1.0, viewZ ); - float curvatureAngleTan = pixelSize * abs( curvature ); // tana = pixelSize / curvatureRadius = pixelSize * curvature - curvatureAngleTan *= max( vmbPixelsTraveled / max( NoV, 0.01 ), 1.0 ); // path length - - float lobeHalfAngle = max( STL::ImportanceSampling::GetSpecularLobeHalfAngle( roughnessModified ), NRD_NORMAL_ULP ); - float curvatureAngle = atan( curvatureAngleTan ); - - // IMPORTANT: increase roughness sensitivity at high FPS - float roughnessSensitivity = NRD_ROUGHNESS_SENSITIVITY * lerp( 1.0, 0.5, STL::Math::SmoothStep( 1.0, 4.0, gFramerateScale ) ); - // Virtual motion - roughness STL::Filtering::Bilinear vmbBilinearFilter = STL::Filtering::GetBilinearFilter( vmbPixelUv, gRectSizePrev ); float2 vmbBilinearGatherUv = ( vmbBilinearFilter.origin + 1.0 ) * gResourceSizeInvPrev; + float roughnessSensitivity = NRD_ROUGHNESS_SENSITIVITY * lerp( 1.0, 0.5, STL::Math::SmoothStep( 1.0, 4.0, gFramerateScale ) ); // IMPORTANT: increase roughness sensitivity at high FPS float2 relaxedRoughnessWeightParams = GetRelaxedRoughnessWeightParams( roughness * roughness, gRoughnessFraction, roughnessSensitivity ); float4 vmbRoughness = gIn_Prev_Normal_Roughness.GatherAlpha( gNearestClamp, vmbBilinearGatherUv ).wzxy; float4 roughnessWeight = ComputeNonExponentialWeightWithSigma( vmbRoughness * vmbRoughness, relaxedRoughnessWeightParams.x, relaxedRoughnessWeightParams.y, roughnessSigma ); @@ -496,6 +480,18 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : bool vmbAllowCatRom = dot( vmbOcclusion, 1.0 ) > 3.5 && REBLUR_USE_CATROM_FOR_VIRTUAL_MOTION_IN_TA; vmbAllowCatRom = vmbAllowCatRom && smbAllowCatRom; // helps to reduce over-sharpening in disoccluded areas + // Estimate how many pixels are traveled by virtual motion - how many radians can it be? + // IMPORTANT: if curvature angle is multiplied by path length then we can get an angle exceeding 2 * PI, what is impossible. The max + // angle is PI ( most left and most right points on a hemisphere ), it can be achieved by using "tan" instead of angle. + float pixelSize = PixelRadiusToWorld( gUnproject, gOrthoMode, 1.0, viewZ ); + float curvatureAngleTan = pixelSize * abs( curvature ); // tana = pixelSize / curvatureRadius = pixelSize * curvature + curvatureAngleTan *= max( vmbPixelsTraveled / max( NoV, 0.01 ), 1.0 ); // path length + + float percentOfVolume = REBLUR_MAX_PERCENT_OF_LOBE_VOLUME * lerp( gLobeAngleFraction, 1.0, 1.0 / ( 1.0 + vmbSpecAccumSpeed ) ); + float lobeTanHalfAngle = STL::ImportanceSampling::GetSpecularLobeTanHalfAngle( roughnessModified, percentOfVolume ); + float lobeHalfAngle = max( atan( lobeTanHalfAngle ), NRD_NORMAL_ULP ); + float curvatureAngle = atan( curvatureAngleTan ); + // Virtual motion - normal: lobe overlapping ( test 107 ) float normalWeight = GetEncodingAwareNormalWeight( N, vmbN, lobeHalfAngle, curvatureAngle ); normalWeight = lerp( STL::Math::SmoothStep( 1.0, 0.0, vmbPixelsTraveled ), 1.0, normalWeight ); // jitter friendly @@ -508,13 +504,10 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : // Virtual motion - virtual parallax difference // Tests 3, 6, 8, 11, 14, 100, 103, 104, 106, 109, 110, 114, 120, 127, 130, 131, 132, 138, 139 and 9e float hitDistForTrackingPrev = gIn_Prev_Spec_HitDistForTracking.SampleLevel( gLinearClamp, vmbPixelUv * gResolutionScalePrev, 0 ); - float3 XvirtualPrev = GetXvirtual( NoV, hitDistForTrackingPrev, curvature, X, Xprev, V, Dfactor ); + float3 XvirtualPrev = GetXvirtual( hitDistForTrackingPrev, curvature, X, Xprev, V, Dfactor ); float XvirtualLengthPrev = length( XvirtualPrev ); float2 vmbPixelUvPrev = STL::Geometry::GetScreenUv( gWorldToClipPrev, XvirtualPrev ); - float percentOfVolume = 0.6; // TODO: why 60%? should be smaller for high FPS? - float lobeTanHalfAngle = STL::ImportanceSampling::GetSpecularLobeTanHalfAngle( roughness, percentOfVolume ); - #if( REBLUR_USE_MORE_STRICT_PARALLAX_BASED_CHECK == 1 ) float unproj1 = min( hitDistForTracking, hitDistForTrackingPrev ) / PixelRadiusToWorld( gUnproject, gOrthoMode, 1.0, max( XvirtualLength, XvirtualLengthPrev ) ); float lobeRadiusInPixels = lobeTanHalfAngle * unproj1; @@ -672,18 +665,21 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : REBLUR_TYPE specHistory = lerp( smbSpecHistory, vmbSpecHistory, virtualHistoryAmount ); // Anti-firefly suppressor + float2 specMaxRelativeIntensity = REBLUR_FIREFLY_SUPPRESSOR_MAX_RELATIVE_INTENSITY; + specMaxRelativeIntensity.x *= ( gMaxAccumulatedFrameNum + 1.0 ) / ( specAccumSpeed + 1.0 ); + float specAntifireflyFactor = specAccumSpeed * ( gMinBlurRadius + gMaxBlurRadius ) * REBLUR_FIREFLY_SUPPRESSOR_RADIUS_SCALE * smc; specAntifireflyFactor /= 1.0 + specAntifireflyFactor; float specHitDistResult = ExtractHitDist( specResult ); - float specHitDistClamped = min( specHitDistResult, ExtractHitDist( specHistory ) * REBLUR_FIREFLY_SUPPRESSOR_MAX_RELATIVE_INTENSITY.y ); + float specHitDistClamped = min( specHitDistResult, ExtractHitDist( specHistory ) * specMaxRelativeIntensity.y ); specHitDistClamped = lerp( specHitDistResult, specHitDistClamped, specAntifireflyFactor ); #if( defined REBLUR_OCCLUSION || defined REBLUR_DIRECTIONAL_OCCLUSION ) specResult = ChangeLuma( specResult, specHitDistClamped ); #else float specLumaResult = GetLuma( specResult ); - float specLumaClamped = min( specLumaResult, GetLuma( specHistory ) * REBLUR_FIREFLY_SUPPRESSOR_MAX_RELATIVE_INTENSITY.x ); + float specLumaClamped = min( specLumaResult, GetLuma( specHistory ) * specMaxRelativeIntensity.x ); specLumaClamped = lerp( specLumaResult, specLumaClamped, specAntifireflyFactor ); specResult = ChangeLuma( specResult, specLumaClamped ); @@ -806,18 +802,21 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : #endif // Anti-firefly suppressor + float2 diffMaxRelativeIntensity = REBLUR_FIREFLY_SUPPRESSOR_MAX_RELATIVE_INTENSITY; + diffMaxRelativeIntensity.x *= ( gMaxAccumulatedFrameNum + 1.0 ) / ( diffAccumSpeed + 1.0 ); + float diffAntifireflyFactor = diffAccumSpeed * ( gMinBlurRadius + gMaxBlurRadius ) * REBLUR_FIREFLY_SUPPRESSOR_RADIUS_SCALE; diffAntifireflyFactor /= 1.0 + diffAntifireflyFactor; float diffHitDistResult = ExtractHitDist( diffResult ); - float diffHitDistClamped = min( diffHitDistResult, ExtractHitDist( smbDiffHistory ) * REBLUR_FIREFLY_SUPPRESSOR_MAX_RELATIVE_INTENSITY.y ); + float diffHitDistClamped = min( diffHitDistResult, ExtractHitDist( smbDiffHistory ) * diffMaxRelativeIntensity.y ); diffHitDistClamped = lerp( diffHitDistResult, diffHitDistClamped, diffAntifireflyFactor ); #if( defined REBLUR_OCCLUSION || defined REBLUR_DIRECTIONAL_OCCLUSION ) diffResult = ChangeLuma( diffResult, diffHitDistClamped ); #else float diffLumaResult = GetLuma( diffResult ); - float diffLumaClamped = min( diffLumaResult, GetLuma( smbDiffHistory ) * REBLUR_FIREFLY_SUPPRESSOR_MAX_RELATIVE_INTENSITY.x ); + float diffLumaClamped = min( diffLumaResult, GetLuma( smbDiffHistory ) * diffMaxRelativeIntensity.x ); diffLumaClamped = lerp( diffLumaResult, diffLumaClamped, diffAntifireflyFactor ); diffResult = ChangeLuma( diffResult, diffLumaClamped ); diff --git a/Shaders/Include/REBLUR_TemporalStabilization.hlsli b/Shaders/Include/REBLUR_TemporalStabilization.hlsli index fe37d7a7..6943f4ad 100644 --- a/Shaders/Include/REBLUR_TemporalStabilization.hlsli +++ b/Shaders/Include/REBLUR_TemporalStabilization.hlsli @@ -300,7 +300,7 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : float3 V = GetViewVector( X ); float NoV = abs( dot( N, V ) ); float dominantFactor = STL::ImportanceSampling::GetSpecularDominantFactor( NoV, roughness, STL_SPECULAR_DOMINANT_DIRECTION_G2 ); - float3 Xvirtual = GetXvirtual( NoV, hitDistForTracking, curvature, X, Xprev, V, dominantFactor ); + float3 Xvirtual = GetXvirtual( hitDistForTracking, curvature, X, Xprev, V, dominantFactor ); float2 vmbPixelUv = STL::Geometry::GetScreenUv( gWorldToClipPrev, Xvirtual ); // Modify MVs if requested diff --git a/Shaders/Include/RELAX_Common.hlsli b/Shaders/Include/RELAX_Common.hlsli index 3b05c9d6..59b4e77b 100644 --- a/Shaders/Include/RELAX_Common.hlsli +++ b/Shaders/Include/RELAX_Common.hlsli @@ -118,7 +118,7 @@ float2 GetNormalWeightParams_ATrous(float roughness, float numFramesInHistory, f float f = 0.9 + 0.1 * relaxation; // This is the main parameter - cone angle - float angle = STL::ImportanceSampling::GetSpecularLobeHalfAngle(roughness, specularLobeAngleFraction); + float angle = atan(GetSpecLobeTanHalfAngle(roughness, specularLobeAngleFraction)); // Increasing angle ~10x to relax rejection of the neighbors if specular reprojection confidence is low angle *= 10.0 - 9.0 * relaxation; @@ -143,7 +143,7 @@ float GetSpecularNormalWeight_ATrous(float2 params0, float3 n0, float3 n, float3 float GetNormalWeightParams(float roughness, float angleFraction = 0.75) { - float angle = STL::ImportanceSampling::GetSpecularLobeHalfAngle(roughness, angleFraction); + float angle = atan(GetSpecLobeTanHalfAngle(roughness, angleFraction)); angle = 1.0 / max(angle, NRD_NORMAL_ULP); return angle; diff --git a/Shaders/Include/RELAX_HitDistReconstruction.hlsli b/Shaders/Include/RELAX_HitDistReconstruction.hlsli index 457e1c32..af960d60 100644 --- a/Shaders/Include/RELAX_HitDistReconstruction.hlsli +++ b/Shaders/Include/RELAX_HitDistReconstruction.hlsli @@ -13,7 +13,7 @@ groupshared float3 sharedHitdistViewZ[BUFFER_Y][BUFFER_X]; float GetNormalWeightParams(float nonLinearAccumSpeed, float fraction, float roughness = 1.0) { - float angle = STL::ImportanceSampling::GetSpecularLobeHalfAngle(roughness); + float angle = atan(GetSpecLobeTanHalfAngle(roughness)); angle *= lerp(saturate(fraction), 1.0, nonLinearAccumSpeed); // TODO: use as "percentOfVolume" instead? return 1.0 / max(angle, NRD_NORMAL_ULP); diff --git a/Shaders/Include/RELAX_TemporalAccumulation.hlsli b/Shaders/Include/RELAX_TemporalAccumulation.hlsli index 4f7c5a77..d3deeec3 100644 --- a/Shaders/Include/RELAX_TemporalAccumulation.hlsli +++ b/Shaders/Include/RELAX_TemporalAccumulation.hlsli @@ -712,14 +712,18 @@ NRD_EXPORT void NRD_CS_MAIN(uint2 pixelPos : SV_DispatchThreadId, uint2 threadPo float s = STL::Math::LinearStep(NRD_NORMAL_ENCODING_ERROR, 2.0 * NRD_NORMAL_ENCODING_ERROR, d); curvature *= s; - // Correction #2 - very negative inconsistent with previous frame curvature blows up reprojection ( tests 164, 171 - 176 ) - float2 uv1 = STL::Geometry::GetScreenUv(gWorldToClipPrev, currentWorldPos - V * ApplyThinLensEquation(NoV, hitDist, curvature)); + // Correction #2 - this is needed if camera is "inside" a concave mirror ( tests 133, 164, 171 - 176 ) + if( length( currentWorldPos ) < -1.0 / curvature ) + curvature *= NoV; + + // Correction #3 - very negative inconsistent with previous frame curvature blows up reprojection ( tests 164, 171 - 176 ) + float2 uv1 = STL::Geometry::GetScreenUv(gWorldToClipPrev, currentWorldPos - V * ApplyThinLensEquation(hitDist, curvature)); float2 uv2 = STL::Geometry::GetScreenUv(gWorldToClipPrev, currentWorldPos); float a = length((uv1 - uv2) * gRectSize); - curvature *= float(a < 3.0 * deltaUvLen + gRectSizeInv.x); // TODO:it's a hack, incompatible with concave mirrors ( tests 22b, 23b, 25b ) + curvature *= float(a < NRD_MAX_ALLOWED_VIRTUAL_MOTION_ACCELERATION * deltaUvLen + gRectSizeInv.x); // Thin lens equation for adjusting reflection HitT - float hitDistFocused = ApplyThinLensEquation(NoV, hitDist, curvature); + float hitDistFocused = ApplyThinLensEquation(hitDist, curvature); [flatten] if (abs(hitDistFocused) < 0.001) // TODO: why? @@ -785,7 +789,7 @@ NRD_EXPORT void NRD_CS_MAIN(uint2 pixelPos : SV_DispatchThreadId, uint2 threadPo float curvatureAngle = atan(tanCurvature); // Normal weight for virtual motion based reprojection - float lobeHalfAngle = max(STL::ImportanceSampling::GetSpecularLobeHalfAngle(currentRoughnessModified), NRD_NORMAL_ULP); + float lobeHalfAngle = max(atan(GetSpecLobeTanHalfAngle(currentRoughnessModified)), NRD_NORMAL_ULP); float angle = lobeHalfAngle + curvatureAngle; float normalWeight = GetEncodingAwareNormalWeight(currentNormal, prevNormalVMB, lobeHalfAngle, curvatureAngle); virtualHistoryAmount *= lerp(1.0 - saturate(uvDiffLengthInPixels), 1.0, normalWeight); // jitter friendly @@ -819,8 +823,8 @@ NRD_EXPORT void NRD_CS_MAIN(uint2 pixelPos : SV_DispatchThreadId, uint2 threadPo // Virtual history confidence - hit distance float SMC = GetSpecMagicCurve(currentRoughnessModified); float hitDistC = lerp(specularIllumination.a, prevReflectionHitTSMB, SMC); - float hitDist1 = ApplyThinLensEquation(NoV, hitDistC, curvature); - float hitDist2 = ApplyThinLensEquation(NoV, prevReflectionHitTVMB, curvature); + float hitDist1 = ApplyThinLensEquation(hitDistC, curvature); + float hitDist2 = ApplyThinLensEquation(prevReflectionHitTVMB, curvature); float maxDist = max(hitDist1, hitDist2); float dHitT = abs(hitDist1 - hitDist2); float dHitTMultiplier = lerp(20.0, 0.0, SMC); @@ -828,15 +832,15 @@ NRD_EXPORT void NRD_CS_MAIN(uint2 pixelPos : SV_DispatchThreadId, uint2 threadPo virtualHistoryHitDistConfidence = lerp(virtualHistoryHitDistConfidence, 1.0, SMC); // Virtual history confidence - virtual UV discrepancy - float3 virtualWorldPos = GetXvirtual(NoV, hitDist, curvature, currentWorldPos, prevWorldPos, V, D.w); + float3 virtualWorldPos = GetXvirtual(hitDist, curvature, currentWorldPos, prevWorldPos, V, D.w); float virtualWorldPosLength = length(virtualWorldPos); float hitDistForTrackingPrev = prevSpecularIlluminationAnd2ndMomentVMBResponsive.a; - float3 prevVirtualWorldPos = GetXvirtual(NoV, hitDistForTrackingPrev, curvature, currentWorldPos, prevWorldPos, V, D.w); + float3 prevVirtualWorldPos = GetXvirtual(hitDistForTrackingPrev, curvature, currentWorldPos, prevWorldPos, V, D.w); float virtualWorldPosLengthPrev = length(prevVirtualWorldPos); float2 prevUVVMBTest = STL::Geometry::GetScreenUv(gWorldToClipPrev, prevVirtualWorldPos, false); float percentOfVolume = 0.6; - float lobeTanHalfAngle = STL::ImportanceSampling::GetSpecularLobeTanHalfAngle(currentRoughness, percentOfVolume); + float lobeTanHalfAngle = GetSpecLobeTanHalfAngle(currentRoughness, percentOfVolume); lobeTanHalfAngle = max(lobeTanHalfAngle, 0.5 * gRectSizeInv.x); float unproj1 = min(hitDist, hitDistForTrackingPrev) / PixelRadiusToWorld(gUnproject, gOrthoMode, 1.0, max(virtualWorldPosLength, virtualWorldPosLengthPrev)); diff --git a/Shaders/Source/SpecularReflectionMv_Compute.cs.hlsl b/Shaders/Source/SpecularReflectionMv_Compute.cs.hlsl index 16b3c97b..c2f6eb07 100644 --- a/Shaders/Source/SpecularReflectionMv_Compute.cs.hlsl +++ b/Shaders/Source/SpecularReflectionMv_Compute.cs.hlsl @@ -178,17 +178,21 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : float s = STL::Math::LinearStep( NRD_NORMAL_ENCODING_ERROR, 2.0 * NRD_NORMAL_ENCODING_ERROR, d ); curvature *= s; - // Correction #2 - very negative inconsistent with previous frame curvature blows up reprojection ( tests 164, 171 - 176 ) - float2 uv1 = STL::Geometry::GetScreenUv( gWorldToClipPrev, X - V * ApplyThinLensEquation( NoV, hitDistForTracking, curvature ) ); + // Correction #2 - this is needed if camera is "inside" a concave mirror ( tests 133, 164, 171 - 176 ) + if( length( X ) < -1.0 / curvature ) + curvature *= NoV; + + // Correction #3 - very negative inconsistent with previous frame curvature blows up reprojection ( tests 164, 171 - 176 ) + float2 uv1 = STL::Geometry::GetScreenUv( gWorldToClipPrev, X - V * ApplyThinLensEquation( hitDistForTracking, curvature ) ); float2 uv2 = STL::Geometry::GetScreenUv( gWorldToClipPrev, X ); float a = length( ( uv1 - uv2 ) * gRectSize ); - curvature *= float( a < 3.0 * deltaUvLen + gRectSizeInv.x ); // TODO:it's a hack, incompatible with concave mirrors ( tests 22b, 23b, 25b ) + curvature *= float( a < NRD_MAX_ALLOWED_VIRTUAL_MOTION_ACCELERATION * deltaUvLen + gRectSizeInv.x ); } // Virtual motion float dominantFactor = STL::ImportanceSampling::GetSpecularDominantFactor( NoV, roughnessModified, STL_SPECULAR_DOMINANT_DIRECTION_G2 ); - float3 Xvirtual = GetXvirtual( NoV, hitDistForTracking, curvature, X, Xprev, V, dominantFactor ); + float3 Xvirtual = GetXvirtual( hitDistForTracking, curvature, X, Xprev, V, dominantFactor ); float2 vmbPixelUv = STL::Geometry::GetScreenUv( gWorldToClipPrev, Xvirtual ); gOut_SpecularReflectionMv[ pixelPos ] = vmbPixelUv - pixelUv; diff --git a/Source/Reblur.cpp b/Source/Reblur.cpp index 3f2b22fc..1b005105 100644 --- a/Source/Reblur.cpp +++ b/Source/Reblur.cpp @@ -375,7 +375,7 @@ void nrd::InstanceImpl::AddSharedConstants_Reblur(const ReblurSettings& settings consts->gMaxAccumulatedFrameNum = isHistoryReset ? 0 : float(maxAccumulatedFrameNum); consts->gMaxFastAccumulatedFrameNum = float(settings.maxFastAccumulatedFrameNum); consts->gAntiFirefly = settings.enableAntiFirefly ? 1.0f : 0.0f; - consts->gLobeAngleFraction = settings.lobeAngleFraction; + consts->gLobeAngleFraction = settings.lobeAngleFraction * settings.lobeAngleFraction; // TODO: GetSpecularLobeTanHalfAngle has been fixed, but we want to use existing settings consts->gRoughnessFraction = settings.roughnessFraction; consts->gResponsiveAccumulationRoughnessThreshold = settings.responsiveAccumulationRoughnessThreshold; consts->gHistoryFixFrameNum = (float)Min(settings.historyFixFrameNum, 3u);