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)
{