Add support for cascades to DDGI

This commit is contained in:
Wojciech Figat
2022-06-09 08:55:45 +02:00
parent 73d762cf0c
commit 6a74ebd62e
14 changed files with 395 additions and 234 deletions

Binary file not shown.

BIN
Content/Shaders/GI/DDGI.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/SSR.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -202,13 +202,13 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(AmbientOcclusionSettings);
/// <summary> /// <summary>
/// Ambient occlusion intensity. /// Ambient occlusion intensity.
/// </summary> /// </summary>
API_FIELD(Attributes="DefaultValue(0.8f), Limit(0, 5.0f, 0.01f), EditorOrder(1), PostProcessSetting((int)AmbientOcclusionSettingsOverride.Intensity)") API_FIELD(Attributes="DefaultValue(0.8f), Limit(0, 10.0f, 0.01f), EditorOrder(1), PostProcessSetting((int)AmbientOcclusionSettingsOverride.Intensity)")
float Intensity = 0.8f; float Intensity = 0.8f;
/// <summary> /// <summary>
/// Ambient occlusion power. /// Ambient occlusion power.
/// </summary> /// </summary>
API_FIELD(Attributes="DefaultValue(0.75f), Limit(0, 4.0f, 0.01f), EditorOrder(2), PostProcessSetting((int)AmbientOcclusionSettingsOverride.Power)") API_FIELD(Attributes="DefaultValue(0.75f), Limit(0, 10.0f, 0.01f), EditorOrder(2), PostProcessSetting((int)AmbientOcclusionSettingsOverride.Power)")
float Power = 0.75f; float Power = 0.75f;
/// <summary> /// <summary>

View File

@@ -12,6 +12,7 @@
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Debug/DebugDraw.h" #include "Engine/Debug/DebugDraw.h"
#include "Engine/Engine/Time.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderBuffers.h"
@@ -43,35 +44,51 @@ PACK_STRUCT(struct Data0
GlobalSignDistanceFieldPass::ConstantsData GlobalSDF; GlobalSignDistanceFieldPass::ConstantsData GlobalSDF;
GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas; GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas;
GBufferData GBuffer; GBufferData GBuffer;
Vector2 Padding0;
float ResetBlend; float ResetBlend;
float TemporalTime;
float IndirectLightingIntensity; float IndirectLightingIntensity;
float Padding0;
});
PACK_STRUCT(struct Data1
{
Vector3 Padding1;
uint32 CascadeIndex; // TODO: use push constants on Vulkan or root signature data on DX12 to reduce overhead of changing single DWORD
}); });
class DDGICustomBuffer : public RenderBuffers::CustomBuffer class DDGICustomBuffer : public RenderBuffers::CustomBuffer
{ {
public: public:
struct
{
Vector3 ProbesOrigin;
float ProbesSpacing = 0.0f;
Int3 ProbeScrollOffsets;
Int3 ProbeScrollDirections;
bool ProbeScrollClear[3];
void Clear()
{
ProbesOrigin = Vector3::Zero;
ProbeScrollOffsets = Int3::Zero;
ProbeScrollDirections = Int3::Zero;
ProbeScrollClear[0] = false;
ProbeScrollClear[1] = false;
ProbeScrollClear[2] = false;
}
} Cascades[4];
int32 CascadesCount = 0;
int32 ProbeRaysCount = 0; int32 ProbeRaysCount = 0;
float ProbesSpacing = 0.0f;
Int3 ProbeCounts = Int3::Zero; Int3 ProbeCounts = Int3::Zero;
Vector3 ProbesOrigin;
Int3 ProbeScrollOffsets;
Int3 ProbeScrollDirections;
bool ProbeScrollClear[3];
GPUTexture* ProbesTrace = nullptr; // Probes ray tracing: (RGB: hit radiance, A: hit distance) GPUTexture* ProbesTrace = nullptr; // Probes ray tracing: (RGB: hit radiance, A: hit distance)
GPUTexture* ProbesState = nullptr; // Probes state: (RGB: world-space offset, A: state) GPUTexture* ProbesState = nullptr; // Probes state: (RGB: world-space offset, A: state)
GPUTexture* ProbesIrradiance = nullptr; // Probes irradiance (RGB: sRGB color) GPUTexture* ProbesIrradiance = nullptr; // Probes irradiance (RGB: sRGB color)
GPUTexture* ProbesDistance = nullptr; // Probes distance (R: mean distance, G: mean distance^2) GPUTexture* ProbesDistance = nullptr; // Probes distance (R: mean distance, G: mean distance^2)
DynamicDiffuseGlobalIlluminationPass::BindingData Result; DynamicDiffuseGlobalIlluminationPass::BindingData Result;
FORCE_INLINE void Clear() FORCE_INLINE void Release()
{ {
ProbesOrigin = Vector3::Zero;
ProbeScrollOffsets = Int3::Zero;
ProbeScrollDirections = Int3::Zero;
ProbeScrollClear[0] = false;
ProbeScrollClear[1] = false;
ProbeScrollClear[2] = false;
RenderTargetPool::Release(ProbesTrace); RenderTargetPool::Release(ProbesTrace);
RenderTargetPool::Release(ProbesState); RenderTargetPool::Release(ProbesState);
RenderTargetPool::Release(ProbesIrradiance); RenderTargetPool::Release(ProbesIrradiance);
@@ -80,7 +97,7 @@ public:
~DDGICustomBuffer() ~DDGICustomBuffer()
{ {
Clear(); Release();
} }
}; };
@@ -150,7 +167,8 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources()
// Initialize resources // Initialize resources
const auto shader = _shader->GetShader(); const auto shader = _shader->GetShader();
_cb0 = shader->GetCB(0); _cb0 = shader->GetCB(0);
if (!_cb0) _cb1 = shader->GetCB(1);
if (!_cb0 || !_cb1)
return true; return true;
_csClassify = shader->GetCS("CS_Classify"); _csClassify = shader->GetCS("CS_Classify");
_csTraceRays = shader->GetCS("CS_TraceRays"); _csTraceRays = shader->GetCS("CS_TraceRays");
@@ -199,6 +217,7 @@ void DynamicDiffuseGlobalIlluminationPass::Dispose()
// Cleanup // Cleanup
_cb0 = nullptr; _cb0 = nullptr;
_cb1 = nullptr;
_csTraceRays = nullptr; _csTraceRays = nullptr;
_shader = nullptr; _shader = nullptr;
SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting); SAFE_DELETE_GPU_RESOURCE(_psIndirectLighting);
@@ -250,51 +269,77 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
// TODO: configurable via postFx settings (maybe use Global SDF distance?) // TODO: configurable via postFx settings (maybe use Global SDF distance?)
const float indirectLightingIntensity = 1.0f; const float indirectLightingIntensity = 1.0f;
const float probeHistoryWeight = 0.8f; const float probeHistoryWeight = 0.8f;
const Vector3 giDistance(2000, 2000, 2000); // GI distance around the view (in each direction) const int32 cascadesCount = 4; // in range 1-4
const float giResolution = 100.0f; // GI probes placement spacing // TODO: use GI.Distance as a easier to adjust total distance and automatically calculate distanceExtent from it
const Int3 probesCounts(Vector3::Ceil(giDistance / giResolution)); const float distance = 20000.0f; // GI distance around the view (in each direction)
const Vector3 probesDistance = Vector3(probesCounts) * giResolution; const float cascadesDistanceScales[] = { 1.0f, 3.0f, 6.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)
const float probesSpacing = 200.0f; // GI probes placement spacing nearby camera (for closest cascade; gets automatically reduced for further cascades)
const Int3 probesCounts(Vector3::Ceil(Vector3(distanceExtent, distanceExtent * verticalRangeScale, distanceExtent) / probesSpacing));
const int32 probeRaysCount = Math::Min(Math::AlignUp(256, DDGI_TRACE_RAYS_GROUP_SIZE_X), DDGI_TRACE_RAYS_LIMIT); // TODO: make it based on the GI Quality const int32 probeRaysCount = Math::Min(Math::AlignUp(256, DDGI_TRACE_RAYS_GROUP_SIZE_X), DDGI_TRACE_RAYS_LIMIT); // TODO: make it based on the GI Quality
// Calculate view origin // Initialize cascades
Vector3 viewOrigin = renderContext.View.Position; float probesSpacings[4];
Vector3 viewDirection = renderContext.View.Direction; Vector3 viewOrigins[4];
const float probesDistanceMax = probesDistance.MaxValue(); for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
const Vector2 viewRayHit = CollisionsHelper::LineHitsBox(viewOrigin, viewOrigin + viewDirection * (probesDistanceMax * 2.0f), viewOrigin - probesDistance, viewOrigin + probesDistance); {
const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.8f; // Each cascade has higher spacing between probes
viewOrigin += viewDirection * viewOriginOffset; float cascadeDistanceScale = cascadesDistanceScales[cascadeIndex];
const float viewOriginSnapping = giResolution; float cascadeProbesSpacing = probesSpacing * cascadeDistanceScale;
viewOrigin = Vector3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping; probesSpacings[cascadeIndex] = cascadeProbesSpacing;
//viewOrigin = Vector3::Zero;
// Calculate view origin for cascade by shifting it towards the view direction to account for better view frustum coverage
Vector3 viewOrigin = renderContext.View.Position;
Vector3 viewDirection = renderContext.View.Direction;
const Vector3 probesDistance = Vector3(probesCounts) * cascadeProbesSpacing;
const float probesDistanceMax = probesDistance.MaxValue();
const Vector2 viewRayHit = CollisionsHelper::LineHitsBox(viewOrigin, viewOrigin + viewDirection * (probesDistanceMax * 2.0f), viewOrigin - probesDistance, viewOrigin + probesDistance);
const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.6f;
viewOrigin += viewDirection * viewOriginOffset;
const float viewOriginSnapping = cascadeProbesSpacing;
viewOrigin = Vector3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping;
//viewOrigin = Vector3::Zero;
viewOrigins[cascadeIndex] = viewOrigin;
}
// Init buffers // Init buffers
const int32 probesCount = probesCounts.X * probesCounts.Y * probesCounts.Z; const int32 probesCountCascade = probesCounts.X * probesCounts.Y * probesCounts.Z;
if (probesCount == 0 || indirectLightingIntensity <= ZeroTolerance) const int32 probesCountTotal = probesCountCascade * cascadesCount;
if (probesCountTotal == 0 || indirectLightingIntensity <= ZeroTolerance)
return true; return true;
int32 probesCountX = probesCounts.X * probesCounts.Y; int32 probesCountCascadeX = probesCounts.X * probesCounts.Y;
int32 probesCountY = probesCounts.Z; int32 probesCountCascadeY = probesCounts.Z;
int32 probesCountTotalX = probesCountCascadeX;
int32 probesCountTotalY = probesCountCascadeY * cascadesCount;
bool clear = false; bool clear = false;
if (Math::NotNearEqual(ddgiData.ProbesSpacing, giResolution) || ddgiData.ProbeCounts != probesCounts || ddgiData.ProbeRaysCount != probeRaysCount) if (ddgiData.CascadesCount != cascadesCount || Math::NotNearEqual(ddgiData.Cascades[0].ProbesSpacing, probesSpacing) || ddgiData.ProbeCounts != probesCounts || ddgiData.ProbeRaysCount != probeRaysCount)
{ {
PROFILE_CPU_NAMED("Init"); PROFILE_CPU_NAMED("Init");
ddgiData.Clear(); ddgiData.Release();
ddgiData.CascadesCount = cascadesCount;
ddgiData.ProbeRaysCount = probeRaysCount; ddgiData.ProbeRaysCount = probeRaysCount;
ddgiData.ProbesSpacing = giResolution;
ddgiData.ProbeCounts = probesCounts; ddgiData.ProbeCounts = probesCounts;
ddgiData.ProbesOrigin = viewOrigin; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
{
auto& cascade = ddgiData.Cascades[cascadeIndex];
cascade.Clear();
cascade.ProbesSpacing = probesSpacings[cascadeIndex];
cascade.ProbesOrigin = viewOrigins[cascadeIndex];
}
// Allocate probes textures // Allocate probes textures
uint64 memUsage = 0; uint64 memUsage = 0;
auto desc = GPUTextureDescription::New2D(probesCountX, probesCountY, PixelFormat::Unknown); auto desc = GPUTextureDescription::New2D(probesCountTotalX, probesCountTotalY, PixelFormat::Unknown);
// TODO rethink probes data placement in memory -> what if we get [50x50x30] resolution? That's 75000 probes! Use sparse storage with active-only probes // TODO rethink probes data placement in memory -> what if we get [50x50x30] resolution? That's 75000 probes! Use sparse storage with active-only probes
#define INIT_TEXTURE(texture, format, width, height) desc.Format = format; desc.Width = width; desc.Height = height; ddgiData.texture = RenderTargetPool::Get(desc); if (!ddgiData.texture) return true; memUsage += ddgiData.texture->GetMemoryUsage() #define INIT_TEXTURE(texture, format, width, height) desc.Format = format; desc.Width = width; desc.Height = height; ddgiData.texture = RenderTargetPool::Get(desc); if (!ddgiData.texture) return true; memUsage += ddgiData.texture->GetMemoryUsage()
desc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess; desc.Flags = GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess;
INIT_TEXTURE(ProbesTrace, PixelFormat::R16G16B16A16_Float, probeRaysCount, probesCount); INIT_TEXTURE(ProbesTrace, PixelFormat::R16G16B16A16_Float, probeRaysCount, probesCountTotal); // TODO: limit to 4k probes for a single batch to trace
INIT_TEXTURE(ProbesState, PixelFormat::R16G16B16A16_Float, probesCountX, probesCountY); // TODO: optimize to a RGBA32 (pos offset can be normalized to [0-0.5] range of ProbesSpacing and packed with state flag) INIT_TEXTURE(ProbesState, PixelFormat::R16G16B16A16_Float, probesCountTotalX, probesCountTotalY); // TODO: optimize to a RGBA32 (pos offset can be normalized to [0-0.5] range of ProbesSpacing and packed with state flag)
INIT_TEXTURE(ProbesIrradiance, PixelFormat::R11G11B10_Float, probesCountX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), probesCountY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2)); INIT_TEXTURE(ProbesIrradiance, PixelFormat::R11G11B10_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2));
INIT_TEXTURE(ProbesDistance, PixelFormat::R16G16_Float, probesCountX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), probesCountY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2)); INIT_TEXTURE(ProbesDistance, PixelFormat::R16G16_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2));
#undef INIT_TEXTURE #undef INIT_TEXTURE
LOG(Info, "Dynamic Diffuse Global Illumination memory usage: {0} MB, probes: {1}", memUsage / 1024 / 1024, probesCount); LOG(Info, "Dynamic Diffuse Global Illumination memory usage: {0} MB, probes: {1}", memUsage / 1024 / 1024, probesCountTotal);
clear = true; clear = true;
} }
#if USE_EDITOR #if USE_EDITOR
@@ -309,46 +354,62 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
context->ClearUA(ddgiData.ProbesDistance, Vector4::Zero); context->ClearUA(ddgiData.ProbesDistance, Vector4::Zero);
} }
// Compute scrolling (probes are placed around camera but are scrolling to increase stability during movement) // Calculate which cascades should be updated this frame
//const uint64 cascadeFrequencies[] = { 1, 2, 3, 5 };
// TODO: prevent updating 2 cascades at once on Low quality
const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 };
bool cascadeSkipUpdate[4];
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
{ {
cascadeSkipUpdate[cascadeIndex] = !clear && (currentFrame % cascadeFrequencies[cascadeIndex]) != 0;
}
// Compute scrolling (probes are placed around camera but are scrolling to increase stability during movement)
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
{
if (cascadeSkipUpdate[cascadeIndex])
continue;
auto& cascade = ddgiData.Cascades[cascadeIndex];
// Reset the volume origin and scroll offsets for each axis // Reset the volume origin and scroll offsets for each axis
for (int32 axis = 0; axis < 3; axis++) for (int32 axis = 0; axis < 3; axis++)
{ {
if (ddgiData.ProbeScrollOffsets.Raw[axis] != 0 && (ddgiData.ProbeScrollOffsets.Raw[axis] % ddgiData.ProbeCounts.Raw[axis] == 0)) if (cascade.ProbeScrollOffsets.Raw[axis] != 0 && (cascade.ProbeScrollOffsets.Raw[axis] % ddgiData.ProbeCounts.Raw[axis] == 0))
{ {
ddgiData.ProbesOrigin.Raw[axis] += (float)ddgiData.ProbeCounts.Raw[axis] * ddgiData.ProbesSpacing * (float)ddgiData.ProbeScrollDirections.Raw[axis]; cascade.ProbesOrigin.Raw[axis] += (float)ddgiData.ProbeCounts.Raw[axis] * cascade.ProbesSpacing * (float)cascade.ProbeScrollDirections.Raw[axis];
ddgiData.ProbeScrollOffsets.Raw[axis] = 0; cascade.ProbeScrollOffsets.Raw[axis] = 0;
} }
} }
// Calculate the count of grid cells between the view origin and the scroll anchor // Calculate the count of grid cells between the view origin and the scroll anchor
const Vector3 volumeOrigin = ddgiData.ProbesOrigin + Vector3(ddgiData.ProbeScrollOffsets) * ddgiData.ProbesSpacing; const Vector3 volumeOrigin = cascade.ProbesOrigin + Vector3(cascade.ProbeScrollOffsets) * cascade.ProbesSpacing;
const Vector3 translation = viewOrigin - volumeOrigin; const Vector3 translation = viewOrigins[cascadeIndex] - volumeOrigin;
for (int32 axis = 0; axis < 3; axis++) for (int32 axis = 0; axis < 3; axis++)
{ {
const float value = translation.Raw[axis] / ddgiData.ProbesSpacing; const float value = translation.Raw[axis] / cascade.ProbesSpacing;
const int32 scroll = value >= 0.0f ? (int32)Math::Floor(value) : (int32)Math::Ceil(value); const int32 scroll = value >= 0.0f ? (int32)Math::Floor(value) : (int32)Math::Ceil(value);
ddgiData.ProbeScrollOffsets.Raw[axis] += scroll; cascade.ProbeScrollOffsets.Raw[axis] += scroll;
ddgiData.ProbeScrollClear[axis] = scroll != 0; cascade.ProbeScrollClear[axis] = scroll != 0;
ddgiData.ProbeScrollDirections.Raw[axis] = translation.Raw[axis] >= 0.0f ? 1 : -1; cascade.ProbeScrollDirections.Raw[axis] = translation.Raw[axis] >= 0.0f ? 1 : -1;
} }
} }
// Upload constants // Upload constants
{ {
ddgiData.Result.Constants.ProbesOrigin = ddgiData.ProbesOrigin; ddgiData.Result.Constants.CascadesCount = cascadesCount;
ddgiData.Result.Constants.ProbesSpacing = ddgiData.ProbesSpacing;
ddgiData.Result.Constants.ProbesCounts[0] = probesCounts.X; ddgiData.Result.Constants.ProbesCounts[0] = probesCounts.X;
ddgiData.Result.Constants.ProbesCounts[1] = probesCounts.Y; ddgiData.Result.Constants.ProbesCounts[1] = probesCounts.Y;
ddgiData.Result.Constants.ProbesCounts[2] = probesCounts.Z; ddgiData.Result.Constants.ProbesCounts[2] = probesCounts.Z;
ddgiData.Result.Constants.ProbesScrollOffsets = ddgiData.ProbeScrollOffsets; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
ddgiData.Result.Constants.ProbeScrollDirections = ddgiData.ProbeScrollDirections; {
ddgiData.Result.Constants.ProbeScrollClear[0] = ddgiData.ProbeScrollClear[0] != 0; auto& cascade = ddgiData.Cascades[cascadeIndex];
ddgiData.Result.Constants.ProbeScrollClear[1] = ddgiData.ProbeScrollClear[1] != 0; int32 probeScrollClear = cascade.ProbeScrollClear[0] + cascade.ProbeScrollClear[1] * 2 + cascade.ProbeScrollClear[2] * 4; // Pack clear flags into bits
ddgiData.Result.Constants.ProbeScrollClear[2] = ddgiData.ProbeScrollClear[2] != 0; ddgiData.Result.Constants.ProbesOriginAndSpacing[cascadeIndex] = Vector4(cascade.ProbesOrigin, cascade.ProbesSpacing);
ddgiData.Result.Constants.ProbesScrollOffsets[cascadeIndex] = Int4(cascade.ProbeScrollOffsets, probeScrollClear);
ddgiData.Result.Constants.ProbeScrollDirections[cascadeIndex] = Int4(cascade.ProbeScrollDirections, 0);
}
ddgiData.Result.Constants.RayMaxDistance = 10000.0f; // TODO: adjust to match perf/quality ratio (make it based on Global SDF and Global Surface Atlas distance) ddgiData.Result.Constants.RayMaxDistance = 10000.0f; // TODO: adjust to match perf/quality ratio (make it based on Global SDF and Global Surface Atlas distance)
ddgiData.Result.Constants.ViewDir = viewDirection; ddgiData.Result.Constants.ViewDir = renderContext.View.Direction;
ddgiData.Result.Constants.RaysCount = probeRaysCount; ddgiData.Result.Constants.RaysCount = probeRaysCount;
ddgiData.Result.Constants.ProbeHistoryWeight = probeHistoryWeight; ddgiData.Result.Constants.ProbeHistoryWeight = probeHistoryWeight;
ddgiData.Result.Constants.IrradianceGamma = 5.0f; ddgiData.Result.Constants.IrradianceGamma = 5.0f;
@@ -368,6 +429,18 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
data.GlobalSDF = bindingDataSDF.Constants; data.GlobalSDF = bindingDataSDF.Constants;
data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants; data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants;
data.ResetBlend = clear ? 1.0f : 0.0f; data.ResetBlend = clear ? 1.0f : 0.0f;
if (renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing)
{
// Use temporal offset in the dithering factor (gets cleaned out by TAA)
const float time = Time::Draw.UnscaledTime.GetTotalSeconds();
const float scale = 10;
const float integral = roundf(time / scale) * scale;
data.TemporalTime = time - integral;
}
else
{
data.TemporalTime = 0.0f;
}
data.IndirectLightingIntensity = indirectLightingIntensity; data.IndirectLightingIntensity = indirectLightingIntensity;
GBufferPass::SetInputs(renderContext.View, data.GBuffer); GBufferPass::SetInputs(renderContext.View, data.GBuffer);
context->UpdateCB(_cb0, &data); context->UpdateCB(_cb0, &data);
@@ -377,72 +450,117 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
// Classify probes (activation/deactivation and relocation) // Classify probes (activation/deactivation and relocation)
{ {
PROFILE_GPU_CPU("Probes Classification"); PROFILE_GPU_CPU("Probes Classification");
uint32 threadGroups = Math::DivideAndRoundUp(probesCount, DDGI_PROBE_CLASSIFY_GROUP_SIZE); uint32 threadGroups = Math::DivideAndRoundUp(probesCountCascade, DDGI_PROBE_CLASSIFY_GROUP_SIZE);
for (int32 i = 0; i < 4; i++) for (int32 i = 0; i < 4; i++)
{ {
context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume()); context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume());
} }
context->BindUA(0, ddgiData.Result.ProbesState); context->BindUA(0, ddgiData.Result.ProbesState);
context->Dispatch(_csClassify, threadGroups, 1, 1); for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
context->ResetUA();
}
// Trace rays from probes
{
PROFILE_GPU_CPU("Trace Rays");
// Global SDF with Global Surface Atlas software raytracing (X - per probe ray, Y - per probe)
ASSERT_LOW_LAYER((probeRaysCount % DDGI_TRACE_RAYS_GROUP_SIZE_X) == 0);
for (int32 i = 0; i < 4; i++)
{ {
context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume()); if (cascadeSkipUpdate[cascadeIndex])
context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume()); continue;
Data1 data;
data.CascadeIndex = cascadeIndex;
context->UpdateCB(_cb1, &data);
context->BindCB(1, _cb1);
context->Dispatch(_csClassify, threadGroups, 1, 1);
} }
context->BindSR(8, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr);
context->BindSR(9, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr);
context->BindSR(10, bindingDataSurfaceAtlas.AtlasDepth->View());
context->BindSR(11, bindingDataSurfaceAtlas.AtlasLighting->View());
context->BindSR(12, ddgiData.Result.ProbesState);
context->BindSR(13, skybox);
context->BindUA(0, ddgiData.ProbesTrace->View());
context->Dispatch(_csTraceRays, probeRaysCount / DDGI_TRACE_RAYS_GROUP_SIZE_X, probesCount, 1);
context->ResetUA(); context->ResetUA();
context->ResetSR();
#if 0
// Probes trace debug preview
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
context->SetRenderTarget(lightBuffer);
context->Draw(ddgiData.ProbesTrace);
return false;
#endif
} }
// Update probes // Update probes
{ {
PROFILE_GPU_CPU("Update Probes"); PROFILE_GPU_CPU("Probes Update");
context->BindSR(0, ddgiData.Result.ProbesState); bool anyDirty = false;
context->BindSR(1, ddgiData.ProbesTrace->View()); uint32 threadGroupsX, threadGroupsY;
for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++)
{
if (cascadeSkipUpdate[cascadeIndex])
continue;
anyDirty = true;
Data1 data;
data.CascadeIndex = cascadeIndex;
context->UpdateCB(_cb1, &data);
context->BindCB(1, _cb1);
// Update irradiance // TODO: run probes tracing+update in 4k batches
context->BindUA(0, ddgiData.Result.ProbesIrradiance);
context->Dispatch(_csUpdateProbesIrradiance, probesCountX, probesCountY, 1);
uint32 threadGroupsX = Math::DivideAndRoundUp(probesCountX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
uint32 threadGroupsY = Math::DivideAndRoundUp(probesCountY, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
context->Dispatch(_csUpdateBordersIrradianceRow, threadGroupsX, threadGroupsY, 1);
threadGroupsX = Math::DivideAndRoundUp(probesCountX, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
threadGroupsY = Math::DivideAndRoundUp(probesCountY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
context->Dispatch(_csUpdateBordersIrradianceCollumn, threadGroupsX, threadGroupsY, 1);
// Update distance // Trace rays from probes
context->BindUA(0, ddgiData.Result.ProbesDistance); {
context->Dispatch(_csUpdateProbesDistance, probesCountX, probesCountY, 1); PROFILE_GPU_CPU("Trace Rays");
threadGroupsX = Math::DivideAndRoundUp(probesCountX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
threadGroupsY = Math::DivideAndRoundUp(probesCountY, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE); // Global SDF with Global Surface Atlas software raytracing (thread X - per probe ray, thread Y - per probe)
context->Dispatch(_csUpdateBordersDistanceRow, threadGroupsX, threadGroupsY, 1); ASSERT_LOW_LAYER((probeRaysCount % DDGI_TRACE_RAYS_GROUP_SIZE_X) == 0);
threadGroupsX = Math::DivideAndRoundUp(probesCountX, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE); for (int32 i = 0; i < 4; i++)
threadGroupsY = Math::DivideAndRoundUp(probesCountY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE); {
context->Dispatch(_csUpdateBordersDistanceCollumn, threadGroupsX, threadGroupsY, 1); context->BindSR(i, bindingDataSDF.Cascades[i]->ViewVolume());
context->BindSR(i + 4, bindingDataSDF.CascadeMips[i]->ViewVolume());
}
context->BindSR(8, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr);
context->BindSR(9, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr);
context->BindSR(10, bindingDataSurfaceAtlas.AtlasDepth->View());
context->BindSR(11, bindingDataSurfaceAtlas.AtlasLighting->View());
context->BindSR(12, ddgiData.Result.ProbesState);
context->BindSR(13, skybox);
context->BindUA(0, ddgiData.ProbesTrace->View());
context->Dispatch(_csTraceRays, probeRaysCount / DDGI_TRACE_RAYS_GROUP_SIZE_X, probesCountCascade, 1);
context->ResetUA();
context->ResetSR();
#if 0
// Probes trace debug preview
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
context->SetRenderTarget(lightBuffer);
context->Draw(ddgiData.ProbesTrace);
return false;
#endif
}
context->BindSR(0, ddgiData.Result.ProbesState);
context->BindSR(1, ddgiData.ProbesTrace->View());
// Update probes irradiance texture
{
PROFILE_GPU_CPU("Update Irradiance");
context->BindUA(0, ddgiData.Result.ProbesIrradiance);
context->Dispatch(_csUpdateProbesIrradiance, probesCountCascadeX, probesCountCascadeY, 1);
}
// Update probes distance texture
{
PROFILE_GPU_CPU("Update Distance");
context->BindUA(0, ddgiData.Result.ProbesDistance);
context->Dispatch(_csUpdateProbesDistance, probesCountCascadeX, probesCountCascadeY, 1);
}
}
// Update probes border pixels
if (anyDirty)
{
PROFILE_GPU_CPU("Update Borders");
// Irradiance
context->BindUA(0, ddgiData.Result.ProbesIrradiance);
threadGroupsX = Math::DivideAndRoundUp(probesCountTotalX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
threadGroupsY = Math::DivideAndRoundUp(probesCountTotalY, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
context->Dispatch(_csUpdateBordersIrradianceRow, threadGroupsX, threadGroupsY, 1);
threadGroupsX = Math::DivideAndRoundUp(probesCountTotalX, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
threadGroupsY = Math::DivideAndRoundUp(probesCountTotalY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
context->Dispatch(_csUpdateBordersIrradianceCollumn, threadGroupsX, threadGroupsY, 1);
// Distance
context->BindUA(0, ddgiData.Result.ProbesDistance);
threadGroupsX = Math::DivideAndRoundUp(probesCountTotalX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
threadGroupsY = Math::DivideAndRoundUp(probesCountTotalY, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
context->Dispatch(_csUpdateBordersDistanceRow, threadGroupsX, threadGroupsY, 1);
threadGroupsX = Math::DivideAndRoundUp(probesCountTotalX, DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
threadGroupsY = Math::DivideAndRoundUp(probesCountTotalY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE);
context->Dispatch(_csUpdateBordersDistanceCollumn, threadGroupsX, threadGroupsY, 1);
context->ResetUA();
context->ResetSR();
}
} }
// Render indirect lighting // Render indirect lighting
@@ -453,7 +571,6 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
// DDGI indirect lighting debug preview // DDGI indirect lighting debug preview
context->Clear(lightBuffer, Color::Transparent); context->Clear(lightBuffer, Color::Transparent);
#endif #endif
context->ResetUA();
context->BindSR(0, renderContext.Buffers->GBuffer0->View()); context->BindSR(0, renderContext.Buffers->GBuffer0->View());
context->BindSR(1, renderContext.Buffers->GBuffer1->View()); context->BindSR(1, renderContext.Buffers->GBuffer1->View());
context->BindSR(2, renderContext.Buffers->GBuffer2->View()); context->BindSR(2, renderContext.Buffers->GBuffer2->View());
@@ -485,7 +602,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
Matrix world; Matrix world;
Matrix::Scaling(Vector3(0.2f), world); Matrix::Scaling(Vector3(0.2f), world);
const Mesh& debugMesh = _debugModel->LODs[0].Meshes[0]; const Mesh& debugMesh = _debugModel->LODs[0].Meshes[0];
for (int32 probeIndex = 0; probeIndex < probesCount; probeIndex++) for (int32 probeIndex = 0; probeIndex < probesCountTotal; probeIndex++)
debugMesh.Draw(debugRenderContext, _debugMaterial, world, StaticFlags::None, true, DrawPass::GBuffer, (float)probeIndex); debugMesh.Draw(debugRenderContext, _debugMaterial, world, StaticFlags::None, true, DrawPass::GBuffer, (float)probeIndex);
debugRenderContext.List->SortDrawCalls(debugRenderContext, false, DrawCallsListType::GBuffer); debugRenderContext.List->SortDrawCalls(debugRenderContext, false, DrawCallsListType::GBuffer);
context->SetViewportAndScissors(debugRenderContext.View.ScreenSize.X, debugRenderContext.View.ScreenSize.Y); context->SetViewportAndScissors(debugRenderContext.View.ScreenSize.X, debugRenderContext.View.ScreenSize.Y);

View File

@@ -3,7 +3,7 @@
#pragma once #pragma once
#include "../RendererPass.h" #include "../RendererPass.h"
#include "Engine/Core/Math/Int3.h" #include "Engine/Core/Math/Int4.h"
#include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Graphics/Textures/GPUTexture.h"
/// <summary> /// <summary>
@@ -15,19 +15,18 @@ public:
// Constant buffer data for DDGI access on a GPU. // Constant buffer data for DDGI access on a GPU.
PACK_STRUCT(struct ConstantsData PACK_STRUCT(struct ConstantsData
{ {
Vector3 ProbesOrigin; Vector4 ProbesOriginAndSpacing[4];
float ProbesSpacing; Int4 ProbesScrollOffsets[4];
Vector4 RaysRotation; Int4 ProbeScrollDirections[4];
uint32 ProbesCounts[3]; uint32 ProbesCounts[3];
uint32 CascadesCount;
float IrradianceGamma; float IrradianceGamma;
Int3 ProbesScrollOffsets;
float ProbeHistoryWeight; float ProbeHistoryWeight;
float RayMaxDistance;
float Padding0;
Vector4 RaysRotation;
Vector3 ViewDir; Vector3 ViewDir;
uint32 RaysCount; uint32 RaysCount;
Int3 ProbeScrollDirections;
float RayMaxDistance;
uint32 ProbeScrollClear[3];
uint32 Padding0;
}); });
// Binding data for the GPU. // Binding data for the GPU.
@@ -43,6 +42,7 @@ private:
bool _supported = false; bool _supported = false;
AssetReference<Shader> _shader; AssetReference<Shader> _shader;
GPUConstantBuffer* _cb0 = nullptr; GPUConstantBuffer* _cb0 = nullptr;
GPUConstantBuffer* _cb1 = nullptr;
GPUShaderProgramCS* _csClassify; GPUShaderProgramCS* _csClassify;
GPUShaderProgramCS* _csTraceRays; GPUShaderProgramCS* _csTraceRays;
GPUShaderProgramCS* _csUpdateProbesIrradiance; GPUShaderProgramCS* _csUpdateProbesIrradiance;

View File

@@ -346,8 +346,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
// TODO: configurable via graphics settings // TODO: configurable via graphics settings
const int32 resolution = 2048; const int32 resolution = 2048;
const float resolutionInv = 1.0f / resolution; const float resolutionInv = 1.0f / resolution;
// TODO: configurable via postFx settings (maybe use Global SDF distance?) // TODO: configurable via postFx settings (use GI distance)
const float distance = 20000; const float distance = 20000.0f;
// Initialize buffers // Initialize buffers
bool noCache = surfaceAtlasData.Resolution != resolution; bool noCache = surfaceAtlasData.Resolution != resolution;

View File

@@ -383,8 +383,10 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
const int32 resolution = 256; const int32 resolution = 256;
const int32 resolutionMip = Math::DivideAndRoundUp(resolution, GLOBAL_SDF_RASTERIZE_MIP_FACTOR); const int32 resolutionMip = Math::DivideAndRoundUp(resolution, GLOBAL_SDF_RASTERIZE_MIP_FACTOR);
// TODO: configurable via postFx settings // TODO: configurable via postFx settings
const float distanceExtent = 2000.0f; const int32 cascadesCount = 4; // in range 1-4
const float cascadesDistances[] = { distanceExtent, distanceExtent * 2.0f, distanceExtent * 4.0f, distanceExtent * 8.0f }; const float distance = true ? 20000.0f : 16000.0f; // TODO: switch based if using GI, then use GI range
const float cascadesDistanceScales[] = { 1.0f, 2.0f, 4.0f, 8.0f };
const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1];
// Initialize buffers // Initialize buffers
auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1); auto desc = GPUTextureDescription::New3D(resolution, resolution, resolution, GLOBAL_SDF_FORMAT, GPUTextureFlags::ShaderResource | GPUTextureFlags::UnorderedAccess, 1);
@@ -449,23 +451,22 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
bool anyDraw = false; bool anyDraw = false;
const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 }; const uint64 cascadeFrequencies[] = { 2, 3, 5, 11 };
//const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 }; //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 };
for (int32 cascade = 0; cascade < 4; cascade++)
for (int32 cascadeIndex = 0; cascadeIndex < 4; cascadeIndex++) for (int32 cascadeIndex = 0; cascadeIndex < 4; cascadeIndex++)
{ {
// Reduce frequency of the updates // Reduce frequency of the updates
if (useCache && (Engine::FrameCount % cascadeFrequencies[cascadeIndex]) != 0) if (useCache && (Engine::FrameCount % cascadeFrequencies[cascadeIndex]) != 0)
continue; continue;
auto& cascade = sdfData.Cascades[cascadeIndex]; auto& cascade = sdfData.Cascades[cascadeIndex];
const float distance = cascadesDistances[cascadeIndex]; const float cascadeDistance = distanceExtent * cascadesDistanceScales[cascadeIndex];
const float maxDistance = distance * 2; const float cascadeMaxDistance = cascadeDistance * 2;
const float voxelSize = maxDistance / resolution; const float cascadeVoxelSize = cascadeMaxDistance / resolution;
const float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; const float cascadeChunkSize = cascadeVoxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE;
static_assert(GLOBAL_SDF_RASTERIZE_CHUNK_SIZE % GLOBAL_SDF_RASTERIZE_MIP_FACTOR == 0, "Adjust chunk size to match the mip factor scale."); static_assert(GLOBAL_SDF_RASTERIZE_CHUNK_SIZE % GLOBAL_SDF_RASTERIZE_MIP_FACTOR == 0, "Adjust chunk size to match the mip factor scale.");
const Vector3 center = Vector3::Floor(renderContext.View.Position / chunkSize) * chunkSize; const Vector3 center = Vector3::Floor(renderContext.View.Position / cascadeChunkSize) * cascadeChunkSize;
//const Vector3 center = Vector3::Zero; //const Vector3 center = Vector3::Zero;
BoundingBox cascadeBounds(center - distance, center + distance); BoundingBox cascadeBounds(center - cascadeDistance, center + cascadeDistance);
// TODO: add scene detail scale factor to PostFx settings (eg. to increase or decrease scene details and quality) // TODO: add scene detail scale factor to PostFx settings (eg. to increase or decrease scene details and quality)
const float minObjectRadius = Math::Max(20.0f, voxelSize * 0.5f); // Skip too small objects for this cascade const float minObjectRadius = Math::Max(20.0f, cascadeVoxelSize * 0.5f); // Skip too small objects for this cascade
GPUTextureView* cascadeView = cascade.Texture->ViewVolume(); GPUTextureView* cascadeView = cascade.Texture->ViewVolume();
GPUTextureView* cascadeMipView = cascade.Mip->ViewVolume(); GPUTextureView* cascadeMipView = cascade.Mip->ViewVolume();
@@ -478,18 +479,18 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
} }
// Check if cascade center has been moved // Check if cascade center has been moved
if (!(useCache && Vector3::NearEqual(cascade.Position, center, voxelSize))) if (!(useCache && Vector3::NearEqual(cascade.Position, center, cascadeVoxelSize)))
{ {
// TODO: optimize for moving camera (copy sdf for cached chunks) // TODO: optimize for moving camera (copy sdf for cached chunks)
cascade.StaticChunks.Clear(); cascade.StaticChunks.Clear();
} }
cascade.Position = center; cascade.Position = center;
cascade.VoxelSize = voxelSize; cascade.VoxelSize = cascadeVoxelSize;
cascade.Bounds = cascadeBounds; cascade.Bounds = cascadeBounds;
// Draw all objects from all scenes into the cascade // Draw all objects from all scenes into the cascade
_objectsBufferCount = 0; _objectsBufferCount = 0;
_voxelSize = voxelSize; _voxelSize = cascadeVoxelSize;
_cascadeBounds = cascadeBounds; _cascadeBounds = cascadeBounds;
_cascadeIndex = cascadeIndex; _cascadeIndex = cascadeIndex;
_sdfData = &sdfData; _sdfData = &sdfData;
@@ -518,12 +519,12 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
} }
ModelsRasterizeData data; ModelsRasterizeData data;
data.CascadeCoordToPosMul = cascadeBounds.GetSize() / resolution; data.CascadeCoordToPosMul = cascadeBounds.GetSize() / resolution;
data.CascadeCoordToPosAdd = cascadeBounds.Minimum + voxelSize * 0.5f; data.CascadeCoordToPosAdd = cascadeBounds.Minimum + cascadeVoxelSize * 0.5f;
data.MaxDistance = maxDistance; data.MaxDistance = cascadeMaxDistance;
data.CascadeResolution = resolution; data.CascadeResolution = resolution;
data.CascadeMipResolution = resolutionMip; data.CascadeMipResolution = resolutionMip;
data.CascadeMipFactor = GLOBAL_SDF_RASTERIZE_MIP_FACTOR; data.CascadeMipFactor = GLOBAL_SDF_RASTERIZE_MIP_FACTOR;
data.CascadeVoxelSize = voxelSize; data.CascadeVoxelSize = cascadeVoxelSize;
context->BindUA(0, cascadeView); context->BindUA(0, cascadeView);
context->BindCB(1, _cb1); context->BindCB(1, _cb1);
const int32 chunkDispatchGroups = GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / GLOBAL_SDF_RASTERIZE_GROUP_SIZE; const int32 chunkDispatchGroups = GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / GLOBAL_SDF_RASTERIZE_GROUP_SIZE;
@@ -728,12 +729,12 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
for (int32 cascadeIndex = 0; cascadeIndex < 4; cascadeIndex++) for (int32 cascadeIndex = 0; cascadeIndex < 4; cascadeIndex++)
{ {
auto& cascade = sdfData.Cascades[cascadeIndex]; auto& cascade = sdfData.Cascades[cascadeIndex];
const float distance = cascadesDistances[cascadeIndex]; const float cascadeDistance = distanceExtent * cascadesDistanceScales[cascadeIndex];
const float maxDistance = distance * 2; const float cascadeMaxDistance = cascadeDistance * 2;
const float voxelSize = maxDistance / resolution; const float cascadeVoxelSize = cascadeMaxDistance / resolution;
const Vector3 center = cascade.Position; const Vector3 center = cascade.Position;
result.Constants.CascadePosDistance[cascadeIndex] = Vector4(center, distance); result.Constants.CascadePosDistance[cascadeIndex] = Vector4(center, cascadeDistance);
result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = voxelSize; result.Constants.CascadeVoxelSize.Raw[cascadeIndex] = cascadeVoxelSize;
result.Cascades[cascadeIndex] = cascade.Texture; result.Cascades[cascadeIndex] = cascade.Texture;
result.CascadeMips[cascadeIndex] = cascade.Mip; result.CascadeMips[cascadeIndex] = cascade.Mip;
} }

View File

@@ -22,19 +22,18 @@
// DDGI data for a constant buffer // DDGI data for a constant buffer
struct DDGIData struct DDGIData
{ {
float3 ProbesOrigin; float4 ProbesOriginAndSpacing[4];
float ProbesSpacing; int4 ProbesScrollOffsets[4];
float4 RaysRotation; int4 ProbeScrollDirections[4];
uint3 ProbesCounts; uint3 ProbesCounts;
uint CascadesCount;
float IrradianceGamma; float IrradianceGamma;
int3 ProbesScrollOffsets;
float ProbeHistoryWeight; float ProbeHistoryWeight;
float RayMaxDistance;
float Padding0;
float4 RaysRotation;
float3 ViewDir; float3 ViewDir;
uint RaysCount; uint RaysCount;
int3 ProbeScrollDirections;
float RayMaxDistance;
uint3 ProbeScrollClear; // TODO: pack into bits
uint Padding0;
}; };
uint GetDDGIProbeIndex(DDGIData data, uint3 probeCoords) uint GetDDGIProbeIndex(DDGIData data, uint3 probeCoords)
@@ -62,88 +61,106 @@ uint3 GetDDGIProbeCoords(DDGIData data, uint probeIndex)
return probeCoords; return probeCoords;
} }
uint2 GetDDGIProbeTexelCoords(DDGIData data, uint probeIndex) uint2 GetDDGIProbeTexelCoords(DDGIData data, uint cascadeIndex, uint probeIndex)
{ {
uint probesPerPlane = data.ProbesCounts.x * data.ProbesCounts.z; uint probesPerPlane = data.ProbesCounts.x * data.ProbesCounts.z;
uint planeIndex = probeIndex / probesPerPlane; uint planeIndex = probeIndex / probesPerPlane;
uint gridSpaceX = probeIndex % data.ProbesCounts.x; uint gridSpaceX = probeIndex % data.ProbesCounts.x;
uint gridSpaceY = probeIndex / data.ProbesCounts.x; uint gridSpaceY = probeIndex / data.ProbesCounts.x;
uint x = gridSpaceX + (planeIndex * data.ProbesCounts.x); uint x = gridSpaceX + (planeIndex * data.ProbesCounts.x);
uint y = gridSpaceY % data.ProbesCounts.z; uint y = gridSpaceY % data.ProbesCounts.z + cascadeIndex * data.ProbesCounts.z;
return uint2(x, y); return uint2(x, y);
} }
uint GetDDGIScrollingProbeIndex(DDGIData data, uint3 probeCoords) uint GetDDGIScrollingProbeIndex(DDGIData data, uint cascadeIndex, uint3 probeCoords)
{ {
// Probes are scrolled on edges to stabilize GI when camera moves // Probes are scrolled on edges to stabilize GI when camera moves
return GetDDGIProbeIndex(data, (probeCoords + data.ProbesScrollOffsets + data.ProbesCounts) % data.ProbesCounts); return GetDDGIProbeIndex(data, (probeCoords + data.ProbesScrollOffsets[cascadeIndex].xyz + data.ProbesCounts) % data.ProbesCounts);
} }
float3 GetDDGIProbeWorldPosition(DDGIData data, uint3 probeCoords) float3 GetDDGIProbeWorldPosition(DDGIData data, uint cascadeIndex, uint3 probeCoords)
{ {
float3 probePosition = probeCoords * data.ProbesSpacing; float3 probesOrigin = data.ProbesOriginAndSpacing[cascadeIndex].xyz;
float3 probeGridOffset = (data.ProbesSpacing * (data.ProbesCounts - 1)) * 0.5f; float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
return data.ProbesOrigin + probePosition - probeGridOffset + (data.ProbesScrollOffsets * data.ProbesSpacing); float3 probePosition = probeCoords * probesSpacing;
float3 probeGridOffset = (probesSpacing * (data.ProbesCounts - 1)) * 0.5f;
return probesOrigin + probePosition - probeGridOffset + (data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing);
} }
// Loads probe probe state // Loads probe probe state
float LoadDDGIProbeState(DDGIData data, Texture2D<float4> probesState, uint probeIndex) float LoadDDGIProbeState(DDGIData data, Texture2D<float4> probesState, uint cascadeIndex, uint probeIndex)
{ {
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, probeIndex); int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
float4 probeState = probesState.Load(int3(probeDataCoords, 0)); float4 probeState = probesState.Load(int3(probeDataCoords, 0));
return probeState.w; return probeState.w;
} }
// Loads probe world-space position (XYZ) and probe state (W) // Loads probe world-space position (XYZ) and probe state (W)
float4 LoadDDGIProbePositionAndState(DDGIData data, Texture2D<float4> probesState, uint probeIndex, uint3 probeCoords) float4 LoadDDGIProbePositionAndState(DDGIData data, Texture2D<float4> probesState, uint cascadeIndex, uint probeIndex, uint3 probeCoords)
{ {
int2 probeDataCoords = GetDDGIProbeTexelCoords(data, probeIndex); int2 probeDataCoords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
float4 probeState = probesState.Load(int3(probeDataCoords, 0)); float4 probeState = probesState.Load(int3(probeDataCoords, 0));
probeState.xyz += GetDDGIProbeWorldPosition(data, probeCoords); probeState.xyz += GetDDGIProbeWorldPosition(data, cascadeIndex, probeCoords);
return probeState; return probeState;
} }
// Calculates texture UVs for sampling probes atlas texture (irradiance or distance) // Calculates texture UVs for sampling probes atlas texture (irradiance or distance)
float2 GetDDGIProbeUV(DDGIData data, uint probeIndex, float2 octahedralCoords, uint resolution) float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2 octahedralCoords, uint resolution)
{ {
uint2 coords = GetDDGIProbeTexelCoords(data, probeIndex); uint2 coords = GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex);
float probeTexelSize = resolution + 2.0f; float probeTexelSize = resolution + 2.0f;
float textureWidth = probeTexelSize * (data.ProbesCounts.x * data.ProbesCounts.y); float2 textureSize = float2(data.ProbesCounts.x * data.ProbesCounts.y, data.ProbesCounts.z * data.CascadesCount) * probeTexelSize;
float textureHeight = probeTexelSize * data.ProbesCounts.z;
float2 uv = float2(coords.x * probeTexelSize, coords.y * probeTexelSize) + (probeTexelSize * 0.5f); float2 uv = float2(coords.x * probeTexelSize, coords.y * probeTexelSize) + (probeTexelSize * 0.5f);
uv += octahedralCoords.xy * (resolution * 0.5f); uv += octahedralCoords.xy * (resolution * 0.5f);
uv /= float2(textureWidth, textureHeight); uv /= textureSize;
return uv; return uv;
} }
// Samples DDGI probes volume at the given world-space position and returns the irradiance. // Samples DDGI probes volume at the given world-space position and returns the irradiance.
float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias) // rand - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending
float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias, float dither = 0.0f)
{ {
float4 irradiance = float4(0, 0, 0, 0); // Select the highest cascade that contains the sample location
float3 probesOrigin = data.ProbesScrollOffsets * data.ProbesSpacing + data.ProbesOrigin; uint cascadeIndex = 0;
float3 probesExtent = (data.ProbesCounts - 1) * (data.ProbesSpacing * 0.5f); for (; cascadeIndex < data.CascadesCount; cascadeIndex++)
{
float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
float3 probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz;
float3 probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
float fadeDistance = probesSpacing * 0.5f;
float cascadeWeight = saturate(Min3(probesExtent - abs(worldPosition - probesOrigin)) / fadeDistance);
if (cascadeWeight > dither) // Use dither to make transition smoother
break;
}
if (cascadeIndex == data.CascadesCount)
return float3(0, 0, 0);
float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
float3 probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz;
float3 probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
// Bias the world-space position to reduce artifacts // Bias the world-space position to reduce artifacts
float3 surfaceBias = (worldNormal * bias) + (data.ViewDir * (bias * -4.0f)); float3 surfaceBias = (worldNormal * bias) + (data.ViewDir * (bias * -4.0f));
float3 biasedWorldPosition = worldPosition + surfaceBias; float3 biasedWorldPosition = worldPosition + surfaceBias;
// Get the grid coordinates of the probe nearest the biased world position // Get the grid coordinates of the probe nearest the biased world position
uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / data.ProbesSpacing), 0, data.ProbesCounts - 1); uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), 0, data.ProbesCounts - 1);
float3 baseProbeWorldPosition = GetDDGIProbeWorldPosition(data, baseProbeCoords); float3 baseProbeWorldPosition = GetDDGIProbeWorldPosition(data, cascadeIndex, baseProbeCoords);
float3 biasAlpha = saturate((biasedWorldPosition - baseProbeWorldPosition) / data.ProbesSpacing); float3 biasAlpha = saturate((biasedWorldPosition - baseProbeWorldPosition) / probesSpacing);
// Loop over the closest probes to accumulate their contributions // Loop over the closest probes to accumulate their contributions
float4 irradiance = float4(0, 0, 0, 0);
for (uint i = 0; i < 8; i++) for (uint i = 0; i < 8; i++)
{ {
uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1; uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1;
uint3 probeCoords = clamp(baseProbeCoords + probeCoordsOffset, 0, data.ProbesCounts - 1); uint3 probeCoords = clamp(baseProbeCoords + probeCoordsOffset, 0, data.ProbesCounts - 1);
uint probeIndex = GetDDGIScrollingProbeIndex(data, probeCoords); uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords);
// Load probe position and state // Load probe position and state
float4 probeState = probesState.Load(int3(GetDDGIProbeTexelCoords(data, probeIndex), 0)); float4 probeState = probesState.Load(int3(GetDDGIProbeTexelCoords(data, cascadeIndex, probeIndex), 0));
if (probeState.w == DDGI_PROBE_STATE_INACTIVE) if (probeState.w == DDGI_PROBE_STATE_INACTIVE)
continue; continue;
float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * data.ProbesSpacing); float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing);
float3 probePosition = probeBasePosition + probeState.xyz; float3 probePosition = probeBasePosition + probeState.xyz;
// Calculate the distance and direction from the (biased and non-biased) shading point and the probe // Calculate the distance and direction from the (biased and non-biased) shading point and the probe
@@ -156,7 +173,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
// Sample distance texture // Sample distance texture
float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe); float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe);
float2 uv = GetDDGIProbeUV(data, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE); float2 uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE);
float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg * 2.0f; float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg * 2.0f;
float probeDistanceMean = probeDistance.x; float probeDistanceMean = probeDistance.x;
float probeDistanceMean2 = probeDistance.y; float probeDistanceMean2 = probeDistance.y;
@@ -183,7 +200,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
// Sample irradiance texture // Sample irradiance texture
octahedralCoords = GetOctahedralCoords(worldNormal); octahedralCoords = GetOctahedralCoords(worldNormal);
uv = GetDDGIProbeUV(data, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE); uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
float3 probeIrradiance = probesIrradiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb; float3 probeIrradiance = probesIrradiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
#if DDGI_SRGB_BLENDING #if DDGI_SRGB_BLENDING
probeIrradiance = pow(probeIrradiance, data.IrradianceGamma * 0.5f); probeIrradiance = pow(probeIrradiance, data.IrradianceGamma * 0.5f);
@@ -196,6 +213,18 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
irradiance += float4(probeIrradiance * weight, weight); irradiance += float4(probeIrradiance * weight, weight);
} }
#if 0
// Debug DDGI cascades with colors
if (cascadeIndex == 0)
irradiance = float4(1, 0, 0, 1);
else if (cascadeIndex == 1)
irradiance = float4(0, 1, 0, 1);
else if (cascadeIndex == 2)
irradiance = float4(0, 0, 1, 1);
else
irradiance = float4(1, 0, 1, 1);
#endif
if (irradiance.a > 0.0f) if (irradiance.a > 0.0f)
{ {
// Normalize irradiance // Normalize irradiance
@@ -204,10 +233,6 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<float4> probesState, Textur
irradiance.rgb *= irradiance.rgb; irradiance.rgb *= irradiance.rgb;
#endif #endif
irradiance.rgb *= 2.0f * PI; irradiance.rgb *= 2.0f * PI;
// Fade-out outside the probes volume
float fadeDistance = data.ProbesSpacing * 0.5f;
irradiance.rgb *= saturate(Min3(probesExtent - abs(worldPosition - probesOrigin)) / fadeDistance);
} }
return irradiance.rgb; return irradiance.rgb;
} }

View File

@@ -22,14 +22,20 @@
#define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 #define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8
#define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32
META_CB_BEGIN(0, Data) META_CB_BEGIN(0, Data0)
DDGIData DDGI; DDGIData DDGI;
GlobalSDFData GlobalSDF; GlobalSDFData GlobalSDF;
GlobalSurfaceAtlasData GlobalSurfaceAtlas; GlobalSurfaceAtlasData GlobalSurfaceAtlas;
GBufferData GBuffer; GBufferData GBuffer;
float2 Padding0;
float ResetBlend; float ResetBlend;
float TemporalTime;
float IndirectLightingIntensity; float IndirectLightingIntensity;
float2 Padding0;
META_CB_END
META_CB_BEGIN(1, Data1)
float3 Padding1;
uint CascadeIndex;
META_CB_END META_CB_END
// Calculates the evenly distributed direction ray on a sphere (Spherical Fibonacci lattice) // Calculates the evenly distributed direction ray on a sphere (Spherical Fibonacci lattice)
@@ -66,22 +72,24 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
if (probeIndex >= probesCount) if (probeIndex >= probesCount)
return; return;
uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex);
probeIndex = GetDDGIScrollingProbeIndex(DDGI, probeCoords); probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords);
int2 probeDataCoords = GetDDGIProbeTexelCoords(DDGI, probeIndex); int2 probeDataCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex);
float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w;
// Load probe state and position // Load probe state and position
float4 probeState = RWProbesState[probeDataCoords]; float4 probeState = RWProbesState[probeDataCoords];
float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, probeCoords); float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords);
float3 probePosition = probeBasePosition + probeState.xyz; float3 probePosition = probeBasePosition + probeState.xyz;
probeState.w = DDGI_PROBE_STATE_ACTIVE; probeState.w = DDGI_PROBE_STATE_ACTIVE;
// Use Global SDF to quickly get distance and direction to the scene geometry // Use Global SDF to quickly get distance and direction to the scene geometry
float sdf; float sdf;
float3 sdfNormal = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, probePosition.xyz, sdf)); float3 sdfNormal = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, probePosition.xyz, sdf));
float threshold = GlobalSDF.CascadeVoxelSize[0] * 0.5f; float sdfDst = abs(sdf);
float distanceLimit = length(DDGI.ProbesSpacing) * 2.0f; float threshold = GlobalSDF.CascadeVoxelSize[CascadeIndex] * 0.5f;
float relocateLimit = length(DDGI.ProbesSpacing) * 0.6f; float distanceLimit = length(probesSpacing) * 2.0f;
if (abs(sdf) > distanceLimit) // Probe is too far from geometry float relocateLimit = length(probesSpacing) * 0.6f;
if (sdfDst > distanceLimit) // Probe is too far from geometry
{ {
// Disable it // Disable it
probeState = float4(0, 0, 0, DDGI_PROBE_STATE_INACTIVE); probeState = float4(0, 0, 0, DDGI_PROBE_STATE_INACTIVE);
@@ -90,9 +98,9 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
{ {
if (sdf < threshold) // Probe is inside geometry if (sdf < threshold) // Probe is inside geometry
{ {
if (abs(sdf) < relocateLimit) if (sdfDst < relocateLimit)
{ {
float3 offsetToAdd = sdfNormal * sdf; float3 offsetToAdd = sdfNormal * (sdf + threshold);
if (distance(probeState.xyz, offsetToAdd) < relocateLimit) if (distance(probeState.xyz, offsetToAdd) < relocateLimit)
{ {
// Relocate it // Relocate it
@@ -105,7 +113,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID)
probeState.xyz = float3(0, 0, 0); probeState.xyz = float3(0, 0, 0);
} }
} }
else if (sdf > threshold * 2.0f) // Probe is far enough any geometry else if (sdf > threshold * 4.0f) // Probe is far enough any geometry
{ {
// Reset relocation // Reset relocation
probeState.xyz = float3(0, 0, 0); probeState.xyz = float3(0, 0, 0);
@@ -146,10 +154,10 @@ void CS_TraceRays(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Dispat
uint rayIndex = DispatchThreadId.x; uint rayIndex = DispatchThreadId.x;
uint probeIndex = DispatchThreadId.y; uint probeIndex = DispatchThreadId.y;
uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex);
probeIndex = GetDDGIScrollingProbeIndex(DDGI, probeCoords); probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords);
// Load current probe state and position // Load current probe state and position
float4 probePositionAndState = LoadDDGIProbePositionAndState(DDGI, ProbesState, probeIndex, probeCoords); float4 probePositionAndState = LoadDDGIProbePositionAndState(DDGI, ProbesState, CascadeIndex, probeIndex, probeCoords);
if (probePositionAndState.w == DDGI_PROBE_STATE_INACTIVE) if (probePositionAndState.w == DDGI_PROBE_STATE_INACTIVE)
return; // Skip disabled probes return; // Skip disabled probes
float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex); float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex);
@@ -222,16 +230,20 @@ void CS_UpdateProbes(uint3 DispatchThreadId : SV_DispatchThreadID, uint GroupInd
uint probesCount = DDGI.ProbesCounts.x * DDGI.ProbesCounts.y * DDGI.ProbesCounts.z; uint probesCount = DDGI.ProbesCounts.x * DDGI.ProbesCounts.y * DDGI.ProbesCounts.z;
bool skip = probeIndex >= probesCount; bool skip = probeIndex >= probesCount;
uint2 outputCoords = uint2(1, 1) + DispatchThreadId.xy + (DispatchThreadId.xy / DDGI_PROBE_RESOLUTION) * 2; uint2 outputCoords = uint2(1, 1) + DispatchThreadId.xy + (DispatchThreadId.xy / DDGI_PROBE_RESOLUTION) * 2;
outputCoords.y += CascadeIndex * DDGI.ProbesCounts.z * (DDGI_PROBE_RESOLUTION + 2);
// Clear probes that have been scrolled to a new positions (blending with current irradiance will happen the next frame) // Clear probes that have been scrolled to a new positions (blending with current irradiance will happen the next frame)
uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex);
int3 probesScrollOffsets = DDGI.ProbesScrollOffsets[CascadeIndex].xyz;
int probeScrollClear = DDGI.ProbesScrollOffsets[CascadeIndex].w;
int3 probeScrollDirections = DDGI.ProbeScrollDirections[CascadeIndex].xyz;
UNROLL UNROLL
for (uint planeIndex = 0; planeIndex < 3; planeIndex++) for (uint planeIndex = 0; planeIndex < 3; planeIndex++)
{ {
if (DDGI.ProbeScrollClear[planeIndex]) if (probeScrollClear & (1 << planeIndex) && !skip)
{ {
int scrollOffset = DDGI.ProbesScrollOffsets[planeIndex]; int scrollOffset = probesScrollOffsets[planeIndex];
int scrollDirection = DDGI.ProbeScrollDirections[planeIndex]; int scrollDirection = probeScrollDirections[planeIndex];
uint probeCount = DDGI.ProbesCounts[planeIndex]; uint probeCount = DDGI.ProbesCounts[planeIndex];
uint coord = (probeCount + (scrollDirection ? (scrollOffset - 1) : (scrollOffset % probeCount))) % probeCount; uint coord = (probeCount + (scrollDirection ? (scrollOffset - 1) : (scrollOffset % probeCount))) % probeCount;
if (probeCoords[planeIndex] == coord) if (probeCoords[planeIndex] == coord)
@@ -244,7 +256,7 @@ void CS_UpdateProbes(uint3 DispatchThreadId : SV_DispatchThreadID, uint GroupInd
} }
// Skip disabled probes // Skip disabled probes
float probeState = LoadDDGIProbeState(DDGI, ProbesState, probeIndex); float probeState = LoadDDGIProbeState(DDGI, ProbesState, CascadeIndex, probeIndex);
if (probeState == DDGI_PROBE_STATE_INACTIVE) if (probeState == DDGI_PROBE_STATE_INACTIVE)
skip = true; skip = true;
@@ -275,7 +287,8 @@ void CS_UpdateProbes(uint3 DispatchThreadId : SV_DispatchThreadID, uint GroupInd
uint backfacesCount = 0; uint backfacesCount = 0;
uint backfacesLimit = uint(DDGI.RaysCount * 0.1f); uint backfacesLimit = uint(DDGI.RaysCount * 0.1f);
#else #else
float distanceLimit = length(DDGI.ProbesSpacing) * 1.5f; float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w;
float distanceLimit = length(probesSpacing) * 1.5f;
#endif #endif
LOOP LOOP
for (uint rayIndex = 0; rayIndex < DDGI.RaysCount; rayIndex++) for (uint rayIndex = 0; rayIndex < DDGI.RaysCount; rayIndex++)
@@ -420,6 +433,7 @@ void CS_UpdateBorders(uint3 DispatchThreadId : SV_DispatchThreadID)
#ifdef _PS_IndirectLighting #ifdef _PS_IndirectLighting
#include "./Flax/GBuffer.hlsl" #include "./Flax/GBuffer.hlsl"
#include "./Flax/Random.hlsl"
#include "./Flax/LightingCommon.hlsl" #include "./Flax/LightingCommon.hlsl"
Texture2D<float4> ProbesState : register(t4); Texture2D<float4> ProbesState : register(t4);
@@ -445,8 +459,9 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0)
// Sample irradiance // Sample irradiance
float bias = 1.0f; float bias = 1.0f;
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias); float dither = RandN2(input.TexCoord + TemporalTime).x;
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesState, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias, dither);
// Calculate lighting // Calculate lighting
float3 diffuseColor = GetDiffuseColor(gBuffer); float3 diffuseColor = GetDiffuseColor(gBuffer);
float3 diffuse = Diffuse_Lambert(diffuseColor); float3 diffuse = Diffuse_Lambert(diffuseColor);

View File

@@ -209,5 +209,5 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex[4]
float GetGlobalSurfaceAtlasThreshold(GlobalSDFHit hit) float GetGlobalSurfaceAtlasThreshold(GlobalSDFHit hit)
{ {
// Scale the threshold based on the hit cascade (less precision) // Scale the threshold based on the hit cascade (less precision)
return hit.HitCascade * 10.0f + 20.0f; return hit.HitCascade * 20.0f + 25.0f;
} }

View File

@@ -9,6 +9,18 @@ float PseudoRandom(float2 xy)
return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f))); return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f)));
} }
// Generic noise (1-component)
float RandN1(float n)
{
return frac(sin(n) * 43758.5453123);
}
// Generic noise (2-components)
float2 RandN2(float2 n)
{
return frac(sin(dot(n, float2(12.9898, 78.233))) * float2(43758.5453123, 28001.8384));
}
void FindBestAxisVectors(float3 input, out float3 axis1, out float3 axis2) void FindBestAxisVectors(float3 input, out float3 axis1, out float3 axis2)
{ {
const float3 a = abs(input); const float3 a = abs(input);

View File

@@ -6,16 +6,6 @@
#include "./Flax/MonteCarlo.hlsl" #include "./Flax/MonteCarlo.hlsl"
#include "./Flax/GBufferCommon.hlsl" #include "./Flax/GBufferCommon.hlsl"
float max2(float2 v)
{
return max(v.x, v.y);
}
float2 RandN2(float2 pos, float2 random)
{
return frac(sin(dot(pos.xy + random, float2(12.9898, 78.233))) * float2(43758.5453, 28001.8384));
}
// 1:-1 to 0:1 // 1:-1 to 0:1
float2 ClipToUv(float2 clipPos) float2 ClipToUv(float2 clipPos)
{ {
@@ -62,7 +52,7 @@ float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D dep
float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix); float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix);
// Randomize it a little // Randomize it a little
float2 jitter = RandN2(uv, temporalTime); float2 jitter = RandN2(uv + temporalTime);
float2 Xi = jitter; float2 Xi = jitter;
Xi.y = lerp(Xi.y, 0.0, brdfBias); Xi.y = lerp(Xi.y, 0.0, brdfBias);
float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal; float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal;
@@ -80,7 +70,8 @@ float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D dep
float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix); float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix);
float3 rayUV = endUV - startUV; float3 rayUV = endUV - startUV;
rayUV *= stepSize / max2(abs(rayUV.xy)); float2 rayUVAbs = abs(rayUV.xy);
rayUV *= stepSize / max(rayUVAbs.x, rayUVAbs.y);
float3 startUv = startUV + rayUV * 2; float3 startUv = startUV + rayUV * 2;
float3 currOffset = startUv; float3 currOffset = startUv;

View File

@@ -138,7 +138,7 @@ float4 PS_ResolvePass(Quad_VS2PS input) : SV_Target0
float3 viewVector = normalize(gBufferData.ViewPos - gBuffer.WorldPos); float3 viewVector = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
// Randomize it a little // Randomize it a little
float2 random = RandN2(uv, TemporalTime); float2 random = RandN2(uv + TemporalTime);
float2 blueNoise = random.xy * 2.0 - 1.0; float2 blueNoise = random.xy * 2.0 - 1.0;
float2x2 offsetRotationMatrix = float2x2(blueNoise.x, blueNoise.y, -blueNoise.y, blueNoise.x); float2x2 offsetRotationMatrix = float2x2(blueNoise.x, blueNoise.y, -blueNoise.y, blueNoise.x);