From 352913ba14ca56fc952ff0a1385c9027e9b002c1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Jul 2024 14:48:24 +0200 Subject: [PATCH] Add random per-probe rotation for rays tracing in DDGI --- .../GI/DynamicDiffuseGlobalIllumination.cpp | 9 ++++---- .../GI/DynamicDiffuseGlobalIllumination.h | 1 - .../Renderer/GI/GlobalSurfaceAtlasPass.cpp | 2 +- .../Renderer/GlobalSignDistanceFieldPass.cpp | 2 +- Source/Shaders/GI/DDGI.hlsl | 1 - Source/Shaders/GI/DDGI.shader | 23 ++++++++++++++----- Source/Shaders/Math.hlsl | 3 +++ Source/Shaders/Noise.hlsl | 18 +++++++++++++++ Source/Shaders/Quaternion.hlsl | 5 ++++ 9 files changed, 50 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 6b92607ea..27ec7146d 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -46,6 +46,7 @@ GPU_CB_STRUCT(Data0 { GlobalSignDistanceFieldPass::ConstantsData GlobalSDF; GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas; ShaderGBufferData GBuffer; + Float4 RaysRotation; float Padding0; uint32 ProbesCount; float ResetBlend; @@ -379,7 +380,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont desc2 = GPUBufferDescription::Buffer(sizeof(GPUDispatchIndirectArgs) * Math::DivideAndRoundUp(probesCountCascade, DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT), GPUBufferFlags::Argument | GPUBufferFlags::UnorderedAccess, PixelFormat::R32_UInt, nullptr, sizeof(uint32)); INIT_BUFFER(UpdateProbesInitArgs, "DDGI.UpdateProbesInitArgs"); #undef INIT_BUFFER - LOG(Info, "Dynamic Diffuse Global Illumination memory usage: {0} MB, probes: {1}", memUsage / 1024 / 1024, probesCountTotal); + LOG(Info, "Dynamic Diffuse Global Illumination probes: {0}, memory usage: {1} MB", probesCountTotal, memUsage / (1024 * 1024)); clear = true; } #if COMPILE_WITH_DEV_ENV @@ -465,14 +466,15 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont ddgiData.Result.ProbesDistance = ddgiData.ProbesDistance->View(); ddgiData.Result.ProbesIrradiance = ddgiData.ProbesIrradiance->View(); + Data0 data; + // Compute random rotation matrix for probe rays orientation (randomized every frame) Matrix3x3 raysRotationMatrix; CalculateVolumeRandomRotation(raysRotationMatrix); - Quaternion& raysRotation = *(Quaternion*)&ddgiData.Result.Constants.RaysRotation; + Quaternion& raysRotation = *(Quaternion*)&data.RaysRotation; Quaternion::RotationMatrix(raysRotationMatrix, raysRotation); raysRotation.Conjugate(); - Data0 data; data.DDGI = ddgiData.Result.Constants; data.GlobalSDF = bindingDataSDF.Constants; data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants; @@ -638,7 +640,6 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, auto& ddgiData = *renderBuffers->GetCustomBuffer(TEXT("DDGI")); if (render && ddgiData.LastFrameUsed == Engine::FrameCount) render = false; - PROFILE_GPU_CPU("Dynamic Diffuse Global Illumination"); if (render) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index 3931b2777..28b18f39c 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -22,7 +22,6 @@ public: float ProbeHistoryWeight; float RayMaxDistance; float IndirectLightingIntensity; - Float4 RaysRotation; Float3 ViewPos; uint32 RaysCount; Float3 FallbackIrradiance; diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 7386fb077..65a5ba7d6 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -742,7 +742,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co return true; memUsage += surfaceAtlasData.ChunksBuffer->GetMemoryUsage(); } - LOG(Info, "Global Surface Atlas resolution: {0}, memory usage: {1} MB", resolution, memUsage / 1024 / 1024); + LOG(Info, "Global Surface Atlas resolution: {0}, memory usage: {1} MB", resolution, memUsage / (1024 * 1024)); } for (SceneRendering* scene : renderContext.List->Scenes) surfaceAtlasData.ListenSceneRendering(scene); diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index cf2b26d44..173604069 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -764,7 +764,7 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex } } uint64 memoryUsage = sdfData.Texture->GetMemoryUsage() + sdfData.TextureMip->GetMemoryUsage(); - LOG(Info, "Global SDF memory usage: {0} MB", memoryUsage / 1024 / 1024); + LOG(Info, "Global SDF memory usage: {0} MB", memoryUsage / (1024 * 1024)); } if (sdfData.Origin != renderContext.View.Origin) { diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index 03979035b..fff009022 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -32,7 +32,6 @@ struct DDGIData float ProbeHistoryWeight; float RayMaxDistance; float IndirectLightingIntensity; - float4 RaysRotation; float3 ViewPos; uint RaysCount; float3 FallbackIrradiance; diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 9344600b5..c80407d54 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -11,6 +11,7 @@ #include "./Flax/Common.hlsl" #include "./Flax/Math.hlsl" +#include "./Flax/Noise.hlsl" #include "./Flax/Quaternion.hlsl" #include "./Flax/GlobalSignDistanceField.hlsl" #include "./Flax/GI/GlobalSurfaceAtlas.hlsl" @@ -30,6 +31,7 @@ DDGIData DDGI; GlobalSDFData GlobalSDF; GlobalSurfaceAtlasData GlobalSurfaceAtlas; GBufferData GBuffer; +float4 RaysRotation; float Padding0; uint ProbesCount; float ResetBlend; @@ -55,10 +57,19 @@ float3 GetSphericalFibonacci(float sampleIndex, float samplesCount) } // Calculates a random normalized ray direction (based on the ray index and the current probes rotation phrase) -float3 GetProbeRayDirection(DDGIData data, uint rayIndex) +float3 GetProbeRayDirection(DDGIData data, uint rayIndex, uint raysCount, uint probeIndex, uint3 probeCoords) { - float3 direction = GetSphericalFibonacci((float)rayIndex, (float)data.RaysCount); - return normalize(QuaternionRotate(data.RaysRotation, direction)); + float4 rotation = RaysRotation; + + // Randomize rotation per-probe (otherwise all probes are in sync) + float3 probePos = (float3)probeCoords / (float3)data.ProbesCounts; + float3 randomAxis = normalize(Mod289(probePos)); + float randomAngle = (float)probeIndex / (float)ProbesCount * (2.0f * PI); + rotation = QuaternionMultiply(rotation, QuaternionFromAxisAngle(randomAxis, randomAngle)); + + // Random rotation per-ray - relative to the per-frame rays rotation + float3 direction = GetSphericalFibonacci((float)rayIndex, (float)raysCount); + return normalize(QuaternionRotate(rotation, direction)); } // Calculates amount of rays to allocate for a probe @@ -299,7 +310,7 @@ Texture2D ProbesData : register(t7); TextureCube Skybox : register(t8); ByteAddressBuffer ActiveProbes : register(t9); -// Compute shader for tracing rays for probes using Global SDF and Global Surface Atlas. +// Compute shader for tracing rays for probes using Global SDF and Global Surface Atlas (1 ray per-thread). META_CS(true, FEATURE_LEVEL_SM5) META_PERMUTATION_1(DDGI_TRACE_RAYS_COUNT=96) META_PERMUTATION_1(DDGI_TRACE_RAYS_COUNT=128) @@ -320,7 +331,7 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) if (probeState == DDGI_PROBE_STATE_INACTIVE || rayIndex >= probeRaysCount) return; // Skip disabled probes or if current thread's ray is unused float3 probePosition = DecodeDDGIProbePosition(DDGI, probeData, CascadeIndex, probeIndex, probeCoords); - float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex); + float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex, probeRaysCount, probeIndex, probeCoords); // Trace ray with Global SDF GlobalSDFTrace trace; @@ -428,7 +439,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ float rayDistance = ProbesTrace[uint2(rayIndex, GroupId.x)].w; CachedProbesTraceDistance[rayIndex] = min(abs(rayDistance), distanceLimit); #endif - CachedProbesTraceDirection[rayIndex] = GetProbeRayDirection(DDGI, rayIndex); + CachedProbesTraceDirection[rayIndex] = GetProbeRayDirection(DDGI, rayIndex, probeRaysCount, probeIndex, probeCoords); } } GroupMemoryBarrierWithGroupSync(); diff --git a/Source/Shaders/Math.hlsl b/Source/Shaders/Math.hlsl index 2eebbee47..cd91e09d0 100644 --- a/Source/Shaders/Math.hlsl +++ b/Source/Shaders/Math.hlsl @@ -3,6 +3,9 @@ #ifndef __MATH__ #define __MATH__ +#define RadiansToDegrees (180.0f / PI) +#define DegreesToRadians (PI / 180.0f) + uint NextPow2(uint value) { uint mask = (1 << firstbithigh(value)) - 1; diff --git a/Source/Shaders/Noise.hlsl b/Source/Shaders/Noise.hlsl index 7297145d4..8ba32c34c 100644 --- a/Source/Shaders/Noise.hlsl +++ b/Source/Shaders/Noise.hlsl @@ -71,6 +71,24 @@ float2 rand2dTo2d(float2 value) ); } +float rand3dTo1d(float3 value, float3 dotDir = float3(12.9898, 78.233, 37.719)) +{ + // https://www.ronja-tutorials.com/post/024-white-noise/ + float3 smallValue = sin(value); + float random = dot(smallValue, dotDir); + return frac(sin(random) * 143758.5453); +} + +float3 rand3dTo3d(float3 value) +{ + // https://www.ronja-tutorials.com/post/024-white-noise/ + return float3( + rand3dTo1d(value, float3(12.989, 78.233, 37.719)), + rand3dTo1d(value, float3(39.346, 11.135, 83.155)), + rand3dTo1d(value, float3(73.156, 52.235, 09.151)) + ); +} + // Classic Perlin noise float PerlinNoise(float2 p) { diff --git a/Source/Shaders/Quaternion.hlsl b/Source/Shaders/Quaternion.hlsl index 204fec811..d40511db2 100644 --- a/Source/Shaders/Quaternion.hlsl +++ b/Source/Shaders/Quaternion.hlsl @@ -15,4 +15,9 @@ float3 QuaternionRotate(float4 q, float3 v) return (v * (q.w * q.w - b2) + b * (dot(v, b) * 2.f) + cross(b, v) * (q.w * 2.f)); } +float4 QuaternionFromAxisAngle(float3 axis, float angle) +{ + return float4(axis * sin(angle * 0.5f), cos(angle * 0.5f)); +} + #endif