diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 5da4ee04f..bb68520c0 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 178 +#define MATERIAL_GRAPH_VERSION 179 class Material; class GPUShader; diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index bdb9bbe2a..63a8ef151 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -338,7 +338,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont } // Calculate the probes count based on the amount of cascades and the distance to cover - const float cascadesDistanceScales[] = { 1.0f, 3.0f, 6.0f, 10.0f }; // Scales each cascade further away from the camera origin + const float cascadesDistanceScales[] = { 1.0f, 3.0f, 5.0f, 10.0f }; // Scales each cascade further away from the camera origin const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1]; const float verticalRangeScale = 0.8f; // Scales the probes volume size at Y axis (horizontal aspect ratio makes the DDGI use less probes vertically to cover whole screen) Int3 probesCounts(Float3::Ceil(Float3(distanceExtent, distanceExtent * verticalRangeScale, distanceExtent) / probesSpacing)); @@ -354,6 +354,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont // Initialize cascades float probesSpacings[4]; Float3 viewOrigins[4]; + Float3 blendOrigins[4]; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { // Each cascade has higher spacing between probes @@ -369,9 +370,10 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont const Float3 viewRayHit = CollisionsHelper::LineHitsBox(viewOrigin, viewOrigin + viewDirection * (probesDistanceMax * 2.0f), viewOrigin - probesDistance, viewOrigin + probesDistance); const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.4f; viewOrigin += viewDirection * viewOriginOffset; + //viewOrigin = Float3::Zero; + blendOrigins[cascadeIndex] = viewOrigin; const float viewOriginSnapping = cascadeProbesSpacing; viewOrigin = Float3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping; - //viewOrigin = Float3::Zero; viewOrigin -= UNITS_TO_METERS(0.5f); // Bias to avoid precision issues (eg. if floor mesh is exactly at Y=0) viewOrigins[cascadeIndex] = viewOrigin; } @@ -504,6 +506,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont { auto& cascade = ddgiData.Cascades[cascadeIndex]; ddgiData.Result.Constants.ProbesOriginAndSpacing[cascadeIndex] = Float4(cascade.ProbesOrigin, cascade.ProbesSpacing); + ddgiData.Result.Constants.BlendOrigin[cascadeIndex] = Float4(blendOrigins[cascadeIndex], 0.0f); ddgiData.Result.Constants.ProbesScrollOffsets[cascadeIndex] = Int4(cascade.ProbeScrollOffsets, 0); } ddgiData.Result.Constants.RayMaxDistance = distance; diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index e6ace0373..123a47c6d 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -15,7 +15,8 @@ public: // Constant buffer data for DDGI access on a GPU. GPU_CB_STRUCT(ConstantsData { Float4 ProbesOriginAndSpacing[4]; - Int4 ProbesScrollOffsets[4]; + Float4 BlendOrigin[4]; // w is unused + Int4 ProbesScrollOffsets[4]; // w is unused uint32 ProbesCounts[3]; uint32 CascadesCount; float IrradianceGamma; diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index eb3995ada..e70454c2b 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -20,7 +20,7 @@ #define DDGI_PROBE_ATTENTION_MAX 0.98f // Maximum probe attention value that still makes it active (but not activated which is 1.0f). #define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) -#define DDGI_CASCADE_BLEND_SIZE 2.5f // Distance in probes over which cascades blending happens +#define DDGI_CASCADE_BLEND_SIZE 2.0f // Distance in probes over which cascades blending happens #ifndef DDGI_CASCADE_BLEND_SMOOTH #define DDGI_CASCADE_BLEND_SMOOTH 0 // Enables smooth cascade blending, otherwise dithering will be used #endif @@ -31,7 +31,8 @@ struct DDGIData { float4 ProbesOriginAndSpacing[4]; - int4 ProbesScrollOffsets[4]; // w unused + float4 BlendOrigin[4]; // w is unused + int4 ProbesScrollOffsets[4]; // w is unused uint3 ProbesCounts; uint CascadesCount; float IrradianceGamma; @@ -290,6 +291,13 @@ float3 GetDDGISurfaceBias(float3 viewDir, float probesSpacing, float3 worldNorma return (worldNormal * 0.2f + viewDir * 0.8f) * (0.6f * probesSpacing * bias); } +// [Inigo Quilez, https://iquilezles.org/articles/distfunctions/] +float sdRoundBox(float3 p, float3 b, float r) +{ + float3 q = abs(p) - b + r; + return length(max(q, 0.0f)) + min(max(q.x, max(q.y, q.z)), 0.0f) - r; +} + // Samples DDGI probes volume at the given world-space position and returns the irradiance. // bias - scales the bias vector to the initial sample point to reduce self-shading artifacts // dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending @@ -312,13 +320,10 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias); // Calculate cascade blending weight (use input bias to smooth transition) - float cascadeBlendSmooth = frac(max(distance(data.ViewPos, worldPosition) - probesExtent.x, 0) / probesSpacing) * 0.1f; - float3 cascadeBlendPoint = worldPosition - probesOrigin - cascadeBlendSmooth * probesSpacing; float fadeDistance = probesSpacing * DDGI_CASCADE_BLEND_SIZE; -#if DDGI_CASCADE_BLEND_SMOOTH - fadeDistance *= 2.0f; // Make it even smoother when using linear blending -#endif - cascadeWeight = saturate(Min3(probesExtent - abs(cascadeBlendPoint)) / fadeDistance); + float3 blendPos = worldPosition - data.BlendOrigin[cascadeIndex].xyz; + cascadeWeight = sdRoundBox(blendPos, probesExtent - probesSpacing, probesSpacing * 2) + fadeDistance; + cascadeWeight = 1 - saturate(cascadeWeight / fadeDistance); if (cascadeWeight > dither) break; } diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index fd82531ac..6f68cef34 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -122,7 +122,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float prevProbesSpacing = DDGI.ProbesOriginAndSpacing[prevCascade].w; float3 prevProbesOrigin = DDGI.ProbesScrollOffsets[prevCascade].xyz * prevProbesSpacing + DDGI.ProbesOriginAndSpacing[prevCascade].xyz; float3 prevProbesExtent = (DDGI.ProbesCounts - 1) * (prevProbesSpacing * 0.5f); - prevProbesExtent -= probesSpacing * ceil(DDGI_CASCADE_BLEND_SIZE); // Apply safe margin to allow probes on cascade edges + prevProbesExtent -= probesSpacing * ceil(DDGI_CASCADE_BLEND_SIZE) * 2; // Apply safe margin to allow probes on cascade edges float prevCascadeWeight = Min3(prevProbesExtent - abs(probeBasePosition - prevProbesOrigin)); if (prevCascadeWeight > 0.1f) {