diff --git a/CMakeLists.txt b/CMakeLists.txt index 55ee3f28..8a408980 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,7 @@ file (GLOB GLOB_RESOURCES "Resources/*") source_group ("Resources" FILES ${GLOB_RESOURCES}) if (NRD_STATIC_LIBRARY) + set (COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS} NRD_STATIC_LIBRARY) add_library (${PROJECT_NAME} STATIC ${GLOB_SOURCE} ${GLOB_DENOISERS} ${ML_FILES} ${GLOB_RESOURCES} ${GLOB_INCUDE}) else () add_library (${PROJECT_NAME} SHARED ${GLOB_SOURCE} ${GLOB_DENOISERS} ${ML_FILES} ${GLOB_RESOURCES} ${GLOB_INCUDE}) diff --git a/External/MathLib b/External/MathLib index f4c7b439..a0a19ec1 160000 --- a/External/MathLib +++ b/External/MathLib @@ -1 +1 @@ -Subproject commit f4c7b4399168bece42a27a01dba1201d4a0cdd6b +Subproject commit a0a19ec1925437f4e472d1915add18051e3385d9 diff --git a/Include/NRD.h b/Include/NRD.h index 6ceb99ce..3f4b679c 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 9 -#define NRD_VERSION_BUILD 1 -#define NRD_VERSION_DATE "14 August 2024" +#define NRD_VERSION_BUILD 2 +#define NRD_VERSION_DATE "23 August 2024" #if defined(_MSC_VER) #define NRD_CALL __fastcall @@ -41,7 +41,7 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. #endif #ifndef NRD_API - #if NRD_STATIC_LIBRARY + #ifdef NRD_STATIC_LIBRARY #define NRD_API #else #define NRD_API extern "C" diff --git a/Include/NRDDescs.h b/Include/NRDDescs.h index fd6df356..1b93046d 100644 --- a/Include/NRDDescs.h +++ b/Include/NRDDescs.h @@ -373,7 +373,7 @@ namespace nrd MAX_NUM }; - struct MemoryAllocatorInterface + struct AllocationCallbacks { void* (*Allocate)(void* userArg, size_t size, size_t alignment); void* (*Reallocate)(void* userArg, void* memory, size_t size, size_t alignment); @@ -409,7 +409,7 @@ namespace nrd struct InstanceCreationDesc { - MemoryAllocatorInterface memoryAllocatorInterface; + AllocationCallbacks allocationCallbacks; const DenoiserDesc* denoisers; uint32_t denoisersNum; }; diff --git a/README.md b/README.md index 314cd488..feccb681 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# NVIDIA REAL-TIME DENOISERS v4.9.1 (NRD) +# NVIDIA REAL-TIME DENOISERS v4.9.2 (NRD) [![Build NRD SDK](https://github.com/NVIDIAGameWorks/RayTracingDenoiser/actions/workflows/build.yml/badge.svg)](https://github.com/NVIDIAGameWorks/RayTracingDenoiser/actions/workflows/build.yml) @@ -632,17 +632,20 @@ When denoising reflections in pure mirrors, some advantages can be reached if *N Notes, requirements and restrictions: - the primary hit (0th bounce) gets replaced with the first "non-pure mirror" hit in the bounce chain - this hit becomes *PSR* - all associated data in the g-buffer gets replaced by *PSR* data -- the camera "sees" PSRs like the mirror surfaces in-between don't exist. This space is called virtual world space - - virtual space position lies on the same view vector as the primary hit position, but the position is elongated. Elongation depends on `hitT` and curvature at bounces, starting from the primary hit +- the camera "sees" PSR like the mirror surface in-between don't exist. This space is called virtual world space + - virtual space position lies on the same view vector as the primary hit position, but the position is elongated. Elongation depends on `hitT` and curvature at hits, starting from the primary hit - virtual space normal is the normal at *PSR* hit mirrored several times in the reversed order until the primary hit is reached - *PSR* data is NOT always data at the *PSR* hit! - material properties (albedo, metalness, roughness etc.) are from *PSR* hit - - `IN_VIEWZ` contains `viewZ` of the virtual position - - `IN_MV` contains motion of the virtual position - `IN_NORMAL_ROUGHNESS` contains normal at virtual world space and roughness at *PSR* - - accumulated `hitT` for *NRD* starts at the *PSR* hit. Curvature must be taken into account on the application side only for 2nd+ bounces starting from this hit (similarly to `hitT` requirements in *Noisy Inputs* section) + - `IN_VIEWZ` contains `viewZ` of the virtual position, potentially adjusted several times by curvature at hits + - `IN_MV` contains motion of the virtual position, potentially adjusted several times by curvature at hits + - accumulated `hitT` starts at the *PSR* hit, potentially adjusted several times by curvature at hits + - curvature should be taken into account starting from the 1st bounce, because the primary surface normal will be replaced by *PSR* normal, i.e. the former will be unreachable on the *NRD* side - ray direction for *NRD* must be transformed into virtual space +IMPORTANT: in other words, *PSR* is perfect for flat mirrors. *PSR* on curved surfaces works even without respecting curvature, but reprojection artefacts can appear. + In case of *PSR* *NRD* disocclusion logic doesn't take curvature at primary hit into account, because data for primary hits is replaced. This can lead to more intense disocclusions on bumpy surfaces due to significant ray divergence. To mitigate this problem 2x-10x larger `disocclusionThreshold` can be used. This is an applicable solution if the denoiser is used to denoise surfaces with *PSR* only (glass only, for example). In a general case, when *PSR* and normal surfaces are mixed on the screen, higher disocclusion thresholds are needed only for pixels with *PSR*. This can be achieved by using `IN_DISOCCLUSION_THRESHOLD_MIX` input to smoothly mix baseline `disocclusionThreshold` into bigger `disocclusionThresholdAlternate` from `CommonSettings`. Most likely the increased disocclusion threshold is needed only for pixels with normal details at primary hits (local curvature is not zero). The illustration below shows expected inputs for secondary hits: diff --git a/Resources/Version.h b/Resources/Version.h index f0310860..6ae32e93 100644 --- a/Resources/Version.h +++ b/Resources/Version.h @@ -23,6 +23,6 @@ Versioning rules: #define VERSION_MAJOR 4 #define VERSION_MINOR 9 -#define VERSION_BUILD 1 +#define VERSION_BUILD 2 #define VERSION_STRING STR(VERSION_MAJOR.VERSION_MINOR.VERSION_BUILD encoding=NRD_NORMAL_ENCODING.NRD_ROUGHNESS_ENCODING) diff --git a/Shaders/Include/REBLUR_Config.hlsli b/Shaders/Include/REBLUR_Config.hlsli index a6903ee2..ae8fa95a 100644 --- a/Shaders/Include/REBLUR_Config.hlsli +++ b/Shaders/Include/REBLUR_Config.hlsli @@ -61,8 +61,8 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. #define REBLUR_BLUR_FRACTION_SCALE 1.0 #define REBLUR_POST_BLUR_ROTATOR_MODE NRD_FRAME -#define REBLUR_POST_BLUR_FRACTION_SCALE 0.5 -#define REBLUR_POST_BLUR_RADIUS_SCALE 2.0 +#define REBLUR_POST_BLUR_FRACTION_SCALE 0.5 // TODO: adjust based on sum of non-noisy data based weights... +#define REBLUR_POST_BLUR_RADIUS_SCALE 2.0 // ... ( normalized to number of taps ) from the blur pass? #define REBLUR_HIT_DIST_MIN_WEIGHT( smc ) ( 0.1 * smc ) // was 0.1 diff --git a/Shaders/Include/REBLUR_TemporalStabilization.hlsli b/Shaders/Include/REBLUR_TemporalStabilization.hlsli index fc9fb902..0ab18a6a 100644 --- a/Shaders/Include/REBLUR_TemporalStabilization.hlsli +++ b/Shaders/Include/REBLUR_TemporalStabilization.hlsli @@ -315,13 +315,15 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : float3 Fenv = BRDF::EnvironmentTerm_Rtg( Rf0, NoV, roughness ); - float lumSpec = Color::Luminance( Fenv ) * virtualHistoryAmount; // TODO: review + float lumSpec = Color::Luminance( Fenv ); float lumDiff = Color::Luminance( albedo * ( 1.0 - Fenv ) ); float specProb = lumSpec / ( lumDiff + lumSpec + NRD_EPS ); - Rng::Hash::Initialize( pixelPos, gFrameIndex ); float f = Math::SmoothStep( gSpecProbabilityThresholdsForMvModification.x, gSpecProbabilityThresholdsForMvModification.y, specProb ); - if( Rng::Hash::GetFloat( ) < f ) + f *= 1.0 - GetSpecMagicCurve( roughness ); + f *= 1.0 - Math::Sqrt01( abs( curvature ) ); + + if( f != 0.0 ) { float3 specMv = Xvirtual - X; // world-space delta fits badly into FP16! Prefer 2.5D motion! if( gMvScale.w == 0.0 ) @@ -330,11 +332,11 @@ NRD_EXPORT void NRD_CS_MAIN( int2 threadPos : SV_GroupThreadId, int2 pixelPos : specMv.z = Geometry::AffineTransform( gWorldToViewPrev, Xvirtual ).z - viewZ; // TODO: is it useful? } - mv = specMv; - // Modify only .xy for 2D and .xyz for 2.5D and 3D MVs - inMv.xy = mv.xy / gMvScale.xy; - inMv.z = gMvScale.z == 0.0 ? inMv.z : mv.z / gMvScale.z; + mv.xy = specMv.xy / gMvScale.xy; + mv.z = gMvScale.z == 0.0 ? inMv.z : specMv.z / gMvScale.z; + + inMv.xyz = lerp( inMv.xyz, mv, f ); gInOut_Mv[ WithRectOrigin( pixelPos ) ] = inMv; } diff --git a/Source/InstanceImpl.h b/Source/InstanceImpl.h index 446698a2..86be1871 100644 --- a/Source/InstanceImpl.h +++ b/Source/InstanceImpl.h @@ -14,7 +14,7 @@ license agreement from NVIDIA CORPORATION is strictly prohibited. #include "NRD.h" -typedef nrd::MemoryAllocatorInterface MemoryAllocatorInterface; +typedef nrd::AllocationCallbacks AllocationCallbacks; #include "StdAllocator.h" #include "Timer.h" diff --git a/Source/StdAllocator.h b/Source/StdAllocator.h index 7d5a637f..54617e31 100644 --- a/Source/StdAllocator.h +++ b/Source/StdAllocator.h @@ -102,14 +102,14 @@ inline void AlignedFree(void* userArg, void* memory) #endif -inline void CheckAndSetDefaultAllocator(MemoryAllocatorInterface& memoryAllocatorInterface) +inline void CheckAndSetDefaultAllocator(AllocationCallbacks& allocationCallbacks) { - if (memoryAllocatorInterface.Allocate != nullptr) + if (allocationCallbacks.Allocate != nullptr) return; - memoryAllocatorInterface.Allocate = AlignedMalloc; - memoryAllocatorInterface.Reallocate = AlignedRealloc; - memoryAllocatorInterface.Free = AlignedFree; + allocationCallbacks.Allocate = AlignedMalloc; + allocationCallbacks.Reallocate = AlignedRealloc; + allocationCallbacks.Free = AlignedFree; } template @@ -121,7 +121,7 @@ struct StdAllocator typedef std::true_type propagate_on_container_move_assignment; typedef std::false_type is_always_equal; - StdAllocator(const MemoryAllocatorInterface& memoryAllocatorInterface) : m_Interface(memoryAllocatorInterface) + StdAllocator(const AllocationCallbacks& allocationCallbacks) : m_Interface(allocationCallbacks) { CheckAndSetDefaultAllocator(m_Interface); } StdAllocator(const StdAllocator& allocator) : m_Interface(allocator.GetInterface()) @@ -143,14 +143,14 @@ struct StdAllocator void deallocate(T* memory, size_t) noexcept { m_Interface.Free(m_Interface.userArg, memory); } - const MemoryAllocatorInterface& GetInterface() const + const AllocationCallbacks& GetInterface() const { return m_Interface; } template using other = StdAllocator; private: - MemoryAllocatorInterface m_Interface = {}; + AllocationCallbacks m_Interface = {}; }; template diff --git a/Source/Wrapper.cpp b/Source/Wrapper.cpp index f0211a7b..edffa441 100644 --- a/Source/Wrapper.cpp +++ b/Source/Wrapper.cpp @@ -244,9 +244,9 @@ NRD_API nrd::Result NRD_CALL nrd::CreateInstance(const InstanceCreationDesc& ins #endif InstanceCreationDesc modifiedInstanceCreationDesc = instanceCreationDesc; - CheckAndSetDefaultAllocator(modifiedInstanceCreationDesc.memoryAllocatorInterface); + CheckAndSetDefaultAllocator(modifiedInstanceCreationDesc.allocationCallbacks); - StdAllocator memoryAllocator(modifiedInstanceCreationDesc.memoryAllocatorInterface); + StdAllocator memoryAllocator(modifiedInstanceCreationDesc.allocationCallbacks); InstanceImpl* implementation = Allocate(memoryAllocator, memoryAllocator); const Result result = implementation->Create(modifiedInstanceCreationDesc); diff --git a/UPDATE.md b/UPDATE.md index 4c93d424..2c180eb3 100644 --- a/UPDATE.md +++ b/UPDATE.md @@ -254,6 +254,7 @@ A single NRD instance can now include any combination of denoisers, including re - *API*: - exposed `ReblurSettings::hitDistanceStabilizationStrength` allowing to control the responsiviness of ambient and specular occlusion in the temporal stabilization pass and reach parity with `REBLUR_OCCLUSION` if set to `0` + - `MemoryAllocatorInterface` renamed to `AllocationCallbacks` - exposed `CommonSettings::strandMaterialID`, enabling "under-the-hood" tweaks for hair (and grass) denoising - exposed `CommonSettings::strandThickness`, defining how NRD adapts to sub-pixel thick details. It works in conjunction with `CommonSettings::disocclusionThresholdAlternate` for `CommonSettings::strandMaterialID` without a need to provide `IN_DISOCCLUSION_THRESHOLD_MIX` texture - *REBLUR*: