Add new DDGI Reflections rendering mode for fully dynamic indirect specular
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -36,6 +36,7 @@ void GlobalIlluminationSettings::BlendWith(GlobalIlluminationSettings& other, fl
|
||||
BLEND_FLOAT(IndirectShadowsStrength);
|
||||
BLEND_COL(FallbackIrradiance);
|
||||
BLEND_ENUM(IndirectResolution);
|
||||
BLEND_ENUM(Reflections);
|
||||
}
|
||||
|
||||
void BloomSettings::BlendWith(BloomSettings& other, float weight)
|
||||
|
||||
@@ -23,7 +23,7 @@ API_ENUM() enum class GlobalIlluminationMode
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Dynamic Diffuse Global Illumination algorithm with scrolling probes volume (with cascades). Uses software raytracing - requires Global SDF and Global Surface Atlas.
|
||||
/// Dynamic Diffuse Global Illumination algorithm with scrolling probes volume (and cascades). Uses software raytracing - requires Global SDF and Global Surface Atlas.
|
||||
/// </summary>
|
||||
DDGI = 1,
|
||||
|
||||
@@ -161,6 +161,22 @@ API_ENUM() enum class ResolutionMode : int32
|
||||
Half = 2,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The reflections modes.
|
||||
/// </summary>
|
||||
API_ENUM() enum class ReflectionsMode : int32
|
||||
{
|
||||
/// <summary>
|
||||
/// Reflections from Environment Probes placed on the scene.
|
||||
/// </summary>
|
||||
EnvironmentProbes = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Reflections from Dynamic Diffuse Global Illumination algorithm with scrolling probes volume (and cascades). Uses software raytracing - requires Global SDF and Global Surface Atlas.
|
||||
/// </summary>
|
||||
DDGI = 1,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The screen space reflections modes.
|
||||
/// </summary>
|
||||
@@ -344,10 +360,15 @@ API_ENUM(Attributes="Flags") enum class GlobalIlluminationSettingsOverride : int
|
||||
/// </summary>
|
||||
IndirectResolution = 1 << 7,
|
||||
|
||||
/// <summary>
|
||||
/// Overrides <see cref="GlobalIlluminationSettings.Reflections"/> property.
|
||||
/// </summary>
|
||||
Reflections = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// All properties.
|
||||
/// </summary>
|
||||
All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength | IndirectResolution,
|
||||
All = Mode | Intensity | TemporalResponse | Distance | FallbackIrradiance | BounceIntensity | IndirectShadowsStrength | IndirectResolution | Reflections,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -413,6 +434,12 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable
|
||||
API_FIELD(Attributes="EditorOrder(50), PostProcessSetting((int)GlobalIlluminationSettingsOverride.IndirectResolution)")
|
||||
ResolutionMode IndirectResolution = ResolutionMode::Full;
|
||||
|
||||
/// <summary>
|
||||
/// The indirect specular reflections rendering mode to use.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes = "EditorOrder(100), PostProcessSetting((int)GlobalIlluminationSettingsOverride.Reflections)")
|
||||
ReflectionsMode Reflections = ReflectionsMode::EnvironmentProbes;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Blends the settings using given weight.
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#define DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT 4096 // Maximum amount of probes to update at once during rays tracing and blending
|
||||
#define DDGI_TRACE_RAYS_LIMIT 256 // Limit of rays per-probe (runtime value can be smaller)
|
||||
#define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side)
|
||||
#define DDGI_PROBE_RESOLUTION_RADIANCE 14 // Resolution (in texels) for probe radiance 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_PROBE_CLASSIFY_GROUP_SIZE 32
|
||||
#define DDGI_PROBE_EMPTY_AREA_DENSITY 8 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency
|
||||
@@ -127,6 +128,7 @@ public:
|
||||
GPUTexture* ProbesTrace = nullptr; // Probes ray tracing: (RGB: hit radiance, A: hit distance)
|
||||
GPUTexture* ProbesData = nullptr; // Probes data: (RGB: probe-space offset, A: state/data)
|
||||
GPUTexture* ProbesIrradiance = nullptr; // Probes irradiance (RGB: sRGB color)
|
||||
GPUTexture* ProbesRadiance = nullptr; // Probes radiance (RGB: HDR color)
|
||||
GPUTexture* ProbesDistance = nullptr; // Probes distance (R: mean distance, G: mean distance^2)
|
||||
GPUBuffer* ActiveProbes = nullptr; // List with indices of the active probes (built during probes classification to use indirect dispatches for probes updating), counter at 0
|
||||
GPUBuffer* UpdateProbesInitArgs = nullptr; // Indirect dispatch buffer for active-only probes updating (trace+blend)
|
||||
@@ -147,7 +149,13 @@ public:
|
||||
RenderTargetPool::Release(ProbesTrace);
|
||||
RenderTargetPool::Release(ProbesData);
|
||||
RenderTargetPool::Release(ProbesIrradiance);
|
||||
RenderTargetPool::Release(ProbesRadiance);
|
||||
RenderTargetPool::Release(ProbesDistance);
|
||||
ProbesTrace = nullptr;
|
||||
ProbesData = nullptr;
|
||||
ProbesIrradiance = nullptr;
|
||||
ProbesRadiance = nullptr;
|
||||
ProbesDistance = nullptr;
|
||||
SAFE_DELETE_GPU_RESOURCE(ActiveProbes);
|
||||
SAFE_DELETE_GPU_RESOURCE(UpdateProbesInitArgs);
|
||||
#if DDGI_DEBUG_STATS
|
||||
@@ -290,6 +298,7 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources()
|
||||
_csTraceRays[3] = shader->GetCS("CS_TraceRays", 3);
|
||||
_csUpdateProbesIrradiance = shader->GetCS("CS_UpdateProbes", 0);
|
||||
_csUpdateProbesDistance = shader->GetCS("CS_UpdateProbes", 1);
|
||||
_csUpdateProbesRadiance = shader->GetCS("CS_UpdateProbes", 2);
|
||||
auto device = GPUDevice::Instance;
|
||||
auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
|
||||
if (!_psIndirectLighting[0])
|
||||
@@ -311,6 +320,15 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources()
|
||||
if (_psIndirectLighting[3]->Init(psDesc))
|
||||
return true;
|
||||
}
|
||||
if (!_psSpecularLighting)
|
||||
{
|
||||
_psSpecularLighting = device->CreatePipelineState();
|
||||
psDesc.DepthEnable = psDesc.DepthBoundsEnable = false;
|
||||
psDesc.BlendMode = BlendingMode::Opaque;
|
||||
psDesc.PS = shader->GetPS("PS_SpecularLighting");
|
||||
if (_psSpecularLighting->Init(psDesc))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -329,7 +347,9 @@ void DynamicDiffuseGlobalIlluminationPass::OnShaderReloading(Asset* obj)
|
||||
_csTraceRays[3] = nullptr;
|
||||
_csUpdateProbesIrradiance = nullptr;
|
||||
_csUpdateProbesDistance = nullptr;
|
||||
SAFE_DELETE_GPU_RESOURCES(_psIndirectLighting)
|
||||
_csUpdateProbesRadiance = nullptr;
|
||||
SAFE_DELETE_GPU_RESOURCES(_psIndirectLighting);
|
||||
SAFE_DELETE_GPU_RESOURCE(_psSpecularLighting);
|
||||
invalidateResources();
|
||||
}
|
||||
|
||||
@@ -343,7 +363,8 @@ void DynamicDiffuseGlobalIlluminationPass::Dispose()
|
||||
_cb0 = nullptr;
|
||||
_cb1 = nullptr;
|
||||
_shader = nullptr;
|
||||
SAFE_DELETE_GPU_RESOURCES(_psIndirectLighting)
|
||||
SAFE_DELETE_GPU_RESOURCES(_psIndirectLighting);
|
||||
SAFE_DELETE_GPU_RESOURCE(_psSpecularLighting);
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
_debugModel = nullptr;
|
||||
_debugMaterial = nullptr;
|
||||
@@ -371,6 +392,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
if (GlobalSurfaceAtlasPass::Instance()->Render(renderContext, context, bindingDataSurfaceAtlas))
|
||||
return true;
|
||||
GPUTextureView* skybox = GBufferPass::Instance()->RenderSkybox(renderContext, context);
|
||||
PROFILE_GPU_CPU("Dynamic Diffuse Global Illumination");
|
||||
|
||||
// Setup options
|
||||
auto& settings = renderContext.List->Settings.GlobalIllumination;
|
||||
@@ -413,7 +435,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
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));
|
||||
const int32 maxProbeSize = Math::Max(DDGI_PROBE_RESOLUTION_IRRADIANCE, DDGI_PROBE_RESOLUTION_DISTANCE) + 2;
|
||||
const int32 maxProbeSize = Math::Max(DDGI_PROBE_RESOLUTION_IRRADIANCE, DDGI_PROBE_RESOLUTION_RADIANCE, DDGI_PROBE_RESOLUTION_DISTANCE) + 2;
|
||||
const int32 maxTextureSize = Math::Min(GPUDevice::Instance->Limits.MaximumTexture2DSize, GPU_MAX_TEXTURE_SIZE);
|
||||
while (probesCounts.X * probesCounts.Y * maxProbeSize > maxTextureSize
|
||||
|| probesCounts.Z * cascadesCount * maxProbeSize > maxTextureSize)
|
||||
@@ -458,7 +480,8 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
int32 probesCountTotalX = probesCountCascadeX;
|
||||
int32 probesCountTotalY = probesCountCascadeY * cascadesCount;
|
||||
bool clear = false;
|
||||
if (ddgiData.CascadesCount != cascadesCount || Math::NotNearEqual(ddgiData.Cascades[0].ProbesSpacing, probesSpacing) || ddgiData.ProbeCounts != probesCounts || ddgiData.ProbeRaysCount != probeRaysCount)
|
||||
bool withRadiance = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Reflections) && renderContext.List->Settings.GlobalIllumination.Reflections == ReflectionsMode::DDGI;
|
||||
if (ddgiData.CascadesCount != cascadesCount || Math::NotNearEqual(ddgiData.Cascades[0].ProbesSpacing, probesSpacing) || ddgiData.ProbeCounts != probesCounts || ddgiData.ProbeRaysCount != probeRaysCount || withRadiance != (ddgiData.ProbesRadiance != nullptr))
|
||||
{
|
||||
PROFILE_CPU_NAMED("Init");
|
||||
ddgiData.Release();
|
||||
@@ -483,6 +506,10 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
INIT_TEXTURE(ProbesData, PixelFormat::R8G8B8A8_SNorm, probesCountTotalX, probesCountTotalY);
|
||||
// TODO: add BC6H compression to probes data (https://github.com/knarkowicz/GPURealTimeBC6H)
|
||||
INIT_TEXTURE(ProbesIrradiance, PixelFormat::R11G11B10_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2));
|
||||
if (withRadiance)
|
||||
{
|
||||
INIT_TEXTURE(ProbesRadiance, PixelFormat::R11G11B10_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_RADIANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_RADIANCE + 2));
|
||||
}
|
||||
INIT_TEXTURE(ProbesDistance, PixelFormat::R16G16_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_DISTANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_DISTANCE + 2));
|
||||
#if DDGI_DEBUG_INSTABILITY
|
||||
INIT_TEXTURE(ProbesInstability, PixelFormat::R16_Float, probesCountTotalX * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2), probesCountTotalY * (DDGI_PROBE_RESOLUTION_IRRADIANCE + 2));
|
||||
@@ -513,6 +540,8 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
PROFILE_GPU("Clear");
|
||||
context->ClearUA(ddgiData.ProbesData, Float4::Zero);
|
||||
context->ClearUA(ddgiData.ProbesIrradiance, Float4::Zero);
|
||||
if (ddgiData.ProbesRadiance)
|
||||
context->ClearUA(ddgiData.ProbesRadiance, Float4::Zero);
|
||||
context->ClearUA(ddgiData.ProbesDistance, Float4::Zero);
|
||||
#if DDGI_DEBUG_INSTABILITY
|
||||
context->ClearUA(ddgiData.ProbesInstability, Float4::Zero);
|
||||
@@ -610,6 +639,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
ddgiData.Result.ProbesData = ddgiData.ProbesData->View();
|
||||
ddgiData.Result.ProbesDistance = ddgiData.ProbesDistance->View();
|
||||
ddgiData.Result.ProbesIrradiance = ddgiData.ProbesIrradiance->View();
|
||||
ddgiData.Result.ProbesRadiance = ddgiData.ProbesRadiance ? ddgiData.ProbesRadiance->View() : nullptr;
|
||||
|
||||
Data0 data;
|
||||
|
||||
@@ -643,7 +673,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
|
||||
// Update probes
|
||||
{
|
||||
PROFILE_GPU_CPU_NAMED("Probes Update");
|
||||
uint32 threadGroupsX;
|
||||
#if DDGI_DEBUG_STATS
|
||||
uint32 zero[4] = {};
|
||||
@@ -739,18 +768,27 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
PROFILE_GPU_CPU_NAMED("Update Probes");
|
||||
GPUComputePass pass(context);
|
||||
|
||||
// Batch barriers to avoid them mid-pass
|
||||
pass.Transition(ddgiData.ProbesData, GPUResourceAccess::UnorderedAccess);
|
||||
pass.Transition(ddgiData.ProbesIrradiance, GPUResourceAccess::UnorderedAccess);
|
||||
if (ddgiData.ProbesRadiance)
|
||||
pass.Transition(ddgiData.ProbesRadiance, GPUResourceAccess::UnorderedAccess);
|
||||
|
||||
// Distance
|
||||
context->BindSR(0, ddgiData.Result.ProbesData);
|
||||
context->BindSR(1, ddgiData.ProbesTrace->View());
|
||||
context->BindSR(2, ddgiData.ActiveProbes->View());
|
||||
context->BindUA(0, ddgiData.Result.ProbesDistance);
|
||||
context->BindUA(1, ddgiData.Result.ProbesData);
|
||||
context->DispatchIndirect(_csUpdateProbesDistance, ddgiData.UpdateProbesInitArgs, arg);
|
||||
context->ResetUA();
|
||||
context->ResetSR();
|
||||
|
||||
// Radiance
|
||||
if (ddgiData.ProbesRadiance)
|
||||
{
|
||||
context->BindUA(0, ddgiData.Result.ProbesRadiance);
|
||||
context->DispatchIndirect(_csUpdateProbesRadiance, ddgiData.UpdateProbesInitArgs, arg);
|
||||
}
|
||||
|
||||
// Irradiance
|
||||
context->BindSR(1, ddgiData.ProbesTrace->View());
|
||||
context->BindSR(2, ddgiData.ActiveProbes->View());
|
||||
context->BindUA(0, ddgiData.Result.ProbesIrradiance);
|
||||
context->BindUA(1, ddgiData.Result.ProbesData);
|
||||
#if DDGI_DEBUG_INSTABILITY
|
||||
@@ -795,14 +833,14 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
|
||||
bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, GPUContext* context, DDGICustomBuffer*& ddgiDataPtr, bool& render)
|
||||
{
|
||||
if (checkIfSkipPass())
|
||||
return true;
|
||||
if (renderContext.List->Scenes.Count() == 0)
|
||||
return true;
|
||||
RenderBuffers* renderBuffers = renderContext.Buffers;
|
||||
bool render = true;
|
||||
render = true;
|
||||
if (renderContext.View.IsOfflinePass)
|
||||
{
|
||||
// During offline pass (eg. probes rendering) we can try reuse main game viewport or editor viewport DDGI probes
|
||||
@@ -823,9 +861,9 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
}
|
||||
}
|
||||
auto& ddgiData = *renderBuffers->GetCustomBuffer<DDGICustomBuffer>(TEXT("DDGI"));
|
||||
ddgiDataPtr = &ddgiData;
|
||||
if (render && ddgiData.LastFrameUsed == Engine::FrameCount)
|
||||
render = false;
|
||||
PROFILE_GPU_CPU("Dynamic Diffuse Global Illumination");
|
||||
|
||||
if (render)
|
||||
{
|
||||
@@ -839,6 +877,17 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
|
||||
{
|
||||
// Get DDGI
|
||||
DDGICustomBuffer* ddgiData = nullptr;
|
||||
bool render = false;
|
||||
if (Render(renderContext, context, ddgiData, render))
|
||||
return true;
|
||||
|
||||
// Render indirect lighting
|
||||
if (lightBuffer)
|
||||
{
|
||||
@@ -850,8 +899,9 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
if (!render)
|
||||
{
|
||||
Data0 data;
|
||||
data.DDGI = ddgiData.Result.Constants;
|
||||
data.TemporalTime = 0.0f;
|
||||
data.DDGI = ddgiData->Result.Constants;
|
||||
data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
|
||||
data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8);
|
||||
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
|
||||
context->UpdateCB(_cb0, &data);
|
||||
context->BindCB(0, _cb0);
|
||||
@@ -861,9 +911,9 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
context->BindSR(1, renderContext.Buffers->GBuffer1->View());
|
||||
context->BindSR(2, renderContext.Buffers->GBuffer2->View());
|
||||
context->BindSR(3, depthBuffer->View());
|
||||
context->BindSR(4, ddgiData.Result.ProbesData);
|
||||
context->BindSR(5, ddgiData.Result.ProbesDistance);
|
||||
context->BindSR(6, ddgiData.Result.ProbesIrradiance);
|
||||
context->BindSR(4, ddgiData->Result.ProbesData);
|
||||
context->BindSR(5, ddgiData->Result.ProbesDistance);
|
||||
context->BindSR(6, ddgiData->Result.ProbesIrradiance);
|
||||
auto& settings = renderContext.List->Settings.GlobalIllumination;
|
||||
if (settings.IndirectResolution == ResolutionMode::Full || !MultiScaler::Instance()->IsReady())
|
||||
{
|
||||
@@ -889,6 +939,7 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
context->SetState(_psIndirectLighting[Graphics::GICascadesBlending ? 3 : 2]);
|
||||
context->DrawFullscreenTriangle();
|
||||
context->ResetRenderTarget();
|
||||
// TODO: consider mixing with temporal-filter to reduce flickering (GI is quite smooth so easy to get rid of ghosting)
|
||||
MultiScaler::Instance()->BilateralUpscale(context, Viewport(Float2(renderContext.View.ScreenSize)), temp, lightBuffer, depthBuffer, renderContext.Buffers->GBuffer1, BlendingMode::Add);
|
||||
RenderTargetPool::Release(temp);
|
||||
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
|
||||
@@ -924,12 +975,12 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
}
|
||||
constexpr int32 maxProbesPerDrawing = 32 * 1024;
|
||||
int32 probesDrawingStart = 0;
|
||||
while (probesDrawingStart < ddgiData.ProbesCountTotal)
|
||||
while (probesDrawingStart < ddgiData->ProbesCountTotal)
|
||||
{
|
||||
debugRenderContext.List = RenderList::GetFromPool();
|
||||
debugRenderContext.View.Pass = DrawPass::GBuffer;
|
||||
debugRenderContext.View.Prepare(debugRenderContext);
|
||||
const int32 probesDrawingEnd = Math::Min(probesDrawingStart + maxProbesPerDrawing, ddgiData.ProbesCountTotal);
|
||||
const int32 probesDrawingEnd = Math::Min(probesDrawingStart + maxProbesPerDrawing, ddgiData->ProbesCountTotal);
|
||||
BatchedDrawCall batchedDrawCall(debugRenderContext.List);
|
||||
batchedDrawCall.DrawCall = drawCall;
|
||||
batchedDrawCall.DrawCall.InstanceCount = probesDrawingEnd - probesDrawingStart;
|
||||
@@ -955,17 +1006,18 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
context->SetRenderTarget(*renderContext.Buffers->DepthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
|
||||
{
|
||||
// Pass DDGI data to the material
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesData"), Variant(ddgiData.ProbesData));
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesData"), Variant(ddgiData->ProbesData));
|
||||
#if DDGI_DEBUG_INSTABILITY
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesInstability));
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData->ProbesInstability));
|
||||
#else
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesIrradiance));
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData->ProbesIrradiance));
|
||||
#endif
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesDistance"), Variant(ddgiData.ProbesDistance));
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesDistance"), Variant(ddgiData->ProbesDistance));
|
||||
_debugMaterial->SetParameterValue(TEXT("ProbesRadiance"), Variant(ddgiData->ProbesRadiance));
|
||||
auto cb = _debugMaterial->GetShader()->GetCB(3);
|
||||
if (cb)
|
||||
{
|
||||
context->UpdateCB(cb, &ddgiData.Result.Constants);
|
||||
context->UpdateCB(cb, &ddgiData->Result.Constants);
|
||||
context->BindCB(3, cb);
|
||||
}
|
||||
}
|
||||
@@ -984,6 +1036,52 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicDiffuseGlobalIlluminationPass::RenderReflections(RenderContext& renderContext, GPUContext* context, GPUTextureView* reflectionsBuffer)
|
||||
{
|
||||
// Get DDGI
|
||||
DDGICustomBuffer* ddgiData = nullptr;
|
||||
bool render = false;
|
||||
if (Render(renderContext, context, ddgiData, render))
|
||||
return true;
|
||||
|
||||
// Render specular lighting
|
||||
if (reflectionsBuffer && ddgiData->Result.ProbesRadiance)
|
||||
{
|
||||
PROFILE_GPU_CPU_NAMED("Specular Lighting");
|
||||
if (!render)
|
||||
{
|
||||
Data0 data;
|
||||
data.DDGI = ddgiData->Result.Constants;
|
||||
data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f;
|
||||
data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8);
|
||||
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
|
||||
context->UpdateCB(_cb0, &data);
|
||||
context->BindCB(0, _cb0);
|
||||
}
|
||||
context->BindSR(0, renderContext.Buffers->GBuffer0->View());
|
||||
context->BindSR(1, renderContext.Buffers->GBuffer1->View());
|
||||
context->BindSR(2, renderContext.Buffers->GBuffer2->View());
|
||||
context->BindSR(3, renderContext.Buffers->DepthBuffer->View());
|
||||
context->BindSR(4, ddgiData->Result.ProbesData);
|
||||
context->BindSR(5, ddgiData->Result.ProbesDistance);
|
||||
context->BindSR(6, ddgiData->Result.ProbesRadiance);
|
||||
auto& settings = renderContext.List->Settings.GlobalIllumination;
|
||||
{
|
||||
// Full-res
|
||||
auto rtAction = GPUDrawPassAction::Store;
|
||||
GPUDrawPass pass(context, ToSpan(&reflectionsBuffer, 1), ToSpan(&rtAction, 1));
|
||||
context->SetViewportAndScissors(renderContext.View.ScreenSize.X, renderContext.View.ScreenSize.Y);
|
||||
context->SetRenderTarget(reflectionsBuffer);
|
||||
context->SetState(_psSpecularLighting);
|
||||
context->DrawFullscreenTriangle();
|
||||
}
|
||||
context->ResetSR();
|
||||
context->ResetRenderTarget();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DynamicDiffuseGlobalIlluminationPass::UpdateRegions(const RenderBuffers* buffers, Span<BoundingBox> regions)
|
||||
{
|
||||
#if !DDGI_DEBUG_FORCE_UPDATE
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
GPUTextureView* ProbesData;
|
||||
GPUTextureView* ProbesDistance;
|
||||
GPUTextureView* ProbesIrradiance;
|
||||
GPUTextureView* ProbesRadiance;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -49,7 +50,9 @@ private:
|
||||
GPUShaderProgramCS* _csTraceRays[4];
|
||||
GPUShaderProgramCS* _csUpdateProbesIrradiance;
|
||||
GPUShaderProgramCS* _csUpdateProbesDistance;
|
||||
GPUShaderProgramCS* _csUpdateProbesRadiance;
|
||||
GPUPipelineState* _psIndirectLighting[4] = {};
|
||||
GPUPipelineState* _psSpecularLighting = nullptr;
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
AssetReference<Model> _debugModel;
|
||||
AssetReference<MaterialBase> _debugMaterial;
|
||||
@@ -73,6 +76,15 @@ public:
|
||||
/// <returns>True if failed to render (platform doesn't support it, out of video memory, disabled feature or effect is not ready), otherwise false.</returns>
|
||||
bool Render(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the DDGI Reflections.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="context">The GPU context.</param>
|
||||
/// <param name="reflectionsBuffer">The reflections buffer (output).</param>
|
||||
/// <returns>True if failed to render (platform doesn't support it, out of video memory, disabled feature or effect is not ready), otherwise false.</returns>
|
||||
bool RenderReflections(RenderContext& renderContext, GPUContext* context, GPUTextureView* reflectionsBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Forces DDGI update for a specific regions (eg. when object is moved). It will refresh the probes in the given regions on the next GI update.
|
||||
/// </summary>
|
||||
@@ -85,6 +97,7 @@ private:
|
||||
uint64 LastFrameShaderReload = 0;
|
||||
void OnShaderReloading(Asset* obj);
|
||||
#endif
|
||||
bool Render(RenderContext& renderContext, GPUContext* context, class DDGICustomBuffer*& ddgiDataPtr, bool& render);
|
||||
bool RenderInner(RenderContext& renderContext, GPUContext* context, class DDGICustomBuffer& ddgiData);
|
||||
|
||||
public:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "GBufferPass.h"
|
||||
#include "RenderList.h"
|
||||
#include "ScreenSpaceReflectionsPass.h"
|
||||
#include "GI/DynamicDiffuseGlobalIllumination.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
||||
@@ -266,47 +267,36 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
|
||||
{
|
||||
auto device = GPUDevice::Instance;
|
||||
auto context = device->GetMainContext();
|
||||
if (checkIfSkipPass())
|
||||
bool useReflections = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Reflections);
|
||||
bool useSSR = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::SSR) && renderContext.List->Settings.ScreenSpaceReflections.Intensity > ZeroTolerance;
|
||||
int32 probesCount = renderContext.List->EnvironmentProbes.Count();
|
||||
bool renderProbes = probesCount > 0 && renderContext.List->Settings.GlobalIllumination.Reflections == ReflectionsMode::EnvironmentProbes;
|
||||
bool renderDDGI = renderContext.List->Settings.GlobalIllumination.Reflections == ReflectionsMode::DDGI;
|
||||
auto shader = _shader->GPU;
|
||||
auto cb = shader->GetCB(0);
|
||||
|
||||
// Check if no need to render reflection environment
|
||||
if (!useReflections || !(renderProbes || useSSR || renderDDGI) || checkIfSkipPass())
|
||||
{
|
||||
// Skip pass (just clear buffer when doing debug preview)
|
||||
if (renderContext.View.Mode == ViewMode::Reflections)
|
||||
context->Clear(lightBuffer, Color::Black);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache data
|
||||
auto& view = renderContext.View;
|
||||
bool useReflections = EnumHasAnyFlags(view.Flags, ViewFlags::Reflections);
|
||||
bool useSSR = EnumHasAnyFlags(view.Flags, ViewFlags::SSR) && renderContext.List->Settings.ScreenSpaceReflections.Intensity > ZeroTolerance;
|
||||
int32 probesCount = renderContext.List->EnvironmentProbes.Count();
|
||||
bool renderProbes = probesCount > 0;
|
||||
auto shader = _shader->GPU;
|
||||
auto cb = shader->GetCB(0);
|
||||
|
||||
// Check if no need to render reflection environment
|
||||
if (!useReflections || !(renderProbes || useSSR))
|
||||
return;
|
||||
PROFILE_GPU_CPU("Reflections");
|
||||
|
||||
// Setup data
|
||||
const int32 width = renderContext.Buffers->GetWidth();
|
||||
const int32 height = renderContext.Buffers->GetHeight();
|
||||
Data data;
|
||||
GBufferPass::SetInputs(view, data.GBuffer);
|
||||
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
|
||||
auto& ssrSettings = renderContext.List->Settings.ScreenSpaceReflections;
|
||||
data.SSRTexelSize = Float2(1.0f / (float)RenderTools::GetResolution(width, ssrSettings.ResolvePassResolution), 1.0f / (float)RenderTools::GetResolution(height, ssrSettings.ResolvePassResolution));
|
||||
|
||||
// Bind GBuffer inputs
|
||||
auto depthBuffer = renderContext.Buffers->GetReadOnlyDepthBuffer();
|
||||
context->BindSR(0, renderContext.Buffers->GBuffer0);
|
||||
context->BindSR(1, renderContext.Buffers->GBuffer1);
|
||||
context->BindSR(2, renderContext.Buffers->GBuffer2);
|
||||
context->BindSR(3, depthBuffer.SRV);
|
||||
|
||||
auto tempDesc = GPUTextureDescription::New2D(renderContext.Buffers->GetWidth(), renderContext.Buffers->GetHeight(), PixelFormat::R11G11B10_Float);
|
||||
auto reflectionsBuffer = RenderTargetPool::Get(tempDesc);
|
||||
RENDER_TARGET_POOL_SET_NAME(reflectionsBuffer, "Reflections");
|
||||
context->Clear(*reflectionsBuffer, Color::Black);
|
||||
|
||||
// Reflection Probes pass
|
||||
if (!useSSR && renderContext.View.Mode == ViewMode::Reflections)
|
||||
@@ -316,16 +306,22 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
|
||||
PROFILE_GPU_CPU("Env Probes");
|
||||
context->SetViewportAndScissors((float)tempDesc.Width, (float)tempDesc.Height);
|
||||
context->SetRenderTarget(depthBuffer.RTV, *reflectionsBuffer);
|
||||
context->Clear(*reflectionsBuffer, Color::Black);
|
||||
context->BindSR(0, renderContext.Buffers->GBuffer0);
|
||||
context->BindSR(1, renderContext.Buffers->GBuffer1);
|
||||
context->BindSR(2, renderContext.Buffers->GBuffer2);
|
||||
context->BindSR(3, depthBuffer.SRV);
|
||||
|
||||
// Sort probes by the radius
|
||||
// TODO: sort probes in async during draw calls sorting
|
||||
Sorting::QuickSort(renderContext.List->EnvironmentProbes.Get(), renderContext.List->EnvironmentProbes.Count(), &SortProbes);
|
||||
|
||||
// Render all env probes
|
||||
auto& sphereMesh = _sphereModel->LODs.Get()[0].Meshes.Get()[0];
|
||||
auto& boxMesh = _boxModel->LODs.Get()[0].Meshes.Get()[0];
|
||||
auto probes = renderContext.List->EnvironmentProbes.Get();
|
||||
for (int32 i = 0; i < probesCount; i++)
|
||||
{
|
||||
const RenderEnvironmentProbeData& probe = renderContext.List->EnvironmentProbes.Get()[i];
|
||||
const RenderEnvironmentProbeData& probe = probes[i];
|
||||
|
||||
// Calculate world*view*projection matrix
|
||||
Matrix world, wvp;
|
||||
@@ -334,15 +330,15 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
|
||||
if (probe.BoxProjection)
|
||||
{
|
||||
OrientedBoundingBox bounds(probe.Radius, Transform(probe.Position, probe.Orientation, probe.Scale));
|
||||
minMaxDepth = RenderTools::GetDepthBounds(view, bounds);
|
||||
minMaxDepth = RenderTools::GetDepthBounds(renderContext.View, bounds);
|
||||
RenderTools::ComputeBoxModelDrawMatrix(renderContext.View, bounds, world, isViewInside);
|
||||
}
|
||||
else
|
||||
{
|
||||
minMaxDepth = RenderTools::GetDepthBounds(view, BoundingSphere(probe.Position, probe.Radius));
|
||||
minMaxDepth = RenderTools::GetDepthBounds(renderContext.View, BoundingSphere(probe.Position, probe.Radius));
|
||||
RenderTools::ComputeSphereModelDrawMatrix(renderContext.View, probe.Position, probe.Radius, world, isViewInside);
|
||||
}
|
||||
Matrix::Multiply(world, view.ViewProjection(), wvp);
|
||||
Matrix::Multiply(world, renderContext.View.ViewProjection(), wvp);
|
||||
|
||||
// Setup depth bounds (if device supports it)
|
||||
if (_depthBounds)
|
||||
@@ -369,21 +365,29 @@ void ReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* light
|
||||
if (_depthBounds)
|
||||
context->SetDepthBounds();
|
||||
}
|
||||
else if (renderDDGI)
|
||||
{
|
||||
if (DynamicDiffuseGlobalIlluminationPass::Instance()->RenderReflections(renderContext, context, *reflectionsBuffer))
|
||||
context->Clear(*reflectionsBuffer, Color::Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->Clear(*reflectionsBuffer, Color::Black);
|
||||
}
|
||||
|
||||
// Screen Space Reflections pass
|
||||
GPUTexture* ssrBuffer = nullptr;
|
||||
if (useSSR)
|
||||
{
|
||||
ssrBuffer = ScreenSpaceReflectionsPass::Instance()->Render(renderContext, *reflectionsBuffer, lightBuffer);
|
||||
|
||||
// Restore
|
||||
context->BindSR(0, renderContext.Buffers->GBuffer0);
|
||||
context->BindSR(1, renderContext.Buffers->GBuffer1);
|
||||
context->BindSR(2, renderContext.Buffers->GBuffer2);
|
||||
context->BindSR(3, depthBuffer.SRV);
|
||||
context->SetViewportAndScissors((float)tempDesc.Width, (float)tempDesc.Height);
|
||||
}
|
||||
|
||||
context->BindSR(0, renderContext.Buffers->GBuffer0);
|
||||
context->BindSR(1, renderContext.Buffers->GBuffer1);
|
||||
context->BindSR(2, renderContext.Buffers->GBuffer2);
|
||||
context->BindSR(3, depthBuffer.SRV);
|
||||
|
||||
if (renderContext.View.Mode == ViewMode::Reflections)
|
||||
{
|
||||
#if GPU_ENABLE_DEVELOPMENT
|
||||
|
||||
@@ -416,6 +416,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
}
|
||||
setup.UseTemporalAAJitter = renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing;
|
||||
setup.UseGlobalSurfaceAtlas = renderContext.View.Mode == ViewMode::GlobalSurfaceAtlas ||
|
||||
(EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Reflections) && renderContext.List->Settings.GlobalIllumination.Reflections == ReflectionsMode::DDGI) ||
|
||||
(EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::GI) && renderContext.List->Settings.GlobalIllumination.Mode == GlobalIlluminationMode::DDGI);
|
||||
setup.UseGlobalSDF = (graphicsSettings->EnableGlobalSDF && EnumHasAnyFlags(view.Flags, ViewFlags::GlobalSDF)) ||
|
||||
renderContext.View.Mode == ViewMode::GlobalSDF ||
|
||||
@@ -438,7 +439,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
case ViewMode::SpecularColor:
|
||||
case ViewMode::SubsurfaceColor:
|
||||
case ViewMode::ShadingModel:
|
||||
case ViewMode::Reflections:
|
||||
case ViewMode::GlobalSDF:
|
||||
case ViewMode::GlobalSDFOverdraw:
|
||||
case ViewMode::GlobalSurfaceAtlas:
|
||||
|
||||
+233
-116
@@ -19,6 +19,7 @@
|
||||
#define DDGI_PROBE_ATTENTION_MIN 0.02f // Minimum probe attention value that still makes it active.
|
||||
#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_RADIANCE 14 // Resolution (in texels) for probe radiance 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.0f // Distance in probes over which cascades blending happens
|
||||
#ifndef DDGI_CASCADE_BLEND_SMOOTH
|
||||
@@ -164,16 +165,122 @@ float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2
|
||||
return uv;
|
||||
}
|
||||
|
||||
float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, uint cascadeIndex, float3 probesOrigin, float3 probesExtent, float probesSpacing, float3 biasedWorldPosition)
|
||||
struct DDGICascadeSampling
|
||||
{
|
||||
bool invalidCascade = cascadeIndex >= data.CascadesCount;
|
||||
cascadeIndex = min(cascadeIndex, data.CascadesCount - 1);
|
||||
float3 ProbesOrigin;
|
||||
uint CascadeIndex;
|
||||
float3 ProbesExtent;
|
||||
float ProbesSpacing;
|
||||
float3 BiasedWorldPosition;
|
||||
float CascadeWeight;
|
||||
};
|
||||
|
||||
struct DDGIProbeBase
|
||||
{
|
||||
uint3 ProbeCoords;
|
||||
float3 ProbeWorldPosition;
|
||||
float3 BiasAlpha;
|
||||
};
|
||||
|
||||
struct DDGIProbeSample
|
||||
{
|
||||
float2 Weights;
|
||||
uint ProbeIndex;
|
||||
float3 ProbePosition;
|
||||
};
|
||||
|
||||
DDGIProbeBase GetDDGIProbeBase(DDGIData data, DDGICascadeSampling cascade, float3 worldPosition)
|
||||
{
|
||||
// Get the grid coordinates of the probe nearest the biased world position
|
||||
DDGIProbeBase base;
|
||||
base.ProbeCoords = clamp(uint3((worldPosition - cascade.ProbesOrigin + cascade.ProbesExtent) / cascade.ProbesSpacing), uint3(0, 0, 0), data.ProbesCounts - uint3(1, 1, 1));
|
||||
base.ProbeWorldPosition = GetDDGIProbeWorldPosition(data, cascade.CascadeIndex, base.ProbeCoords);
|
||||
base.BiasAlpha = saturate((cascade.BiasedWorldPosition - base.ProbeWorldPosition) / cascade.ProbesSpacing);
|
||||
return base;
|
||||
}
|
||||
|
||||
DDGIProbeSample SampleDDGIProbe(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, float3 worldPosition, float3 worldNormal, DDGICascadeSampling cascade, DDGIProbeBase base, uint i, inout uint fallbacks)
|
||||
{
|
||||
DDGIProbeSample probe;
|
||||
uint3 probeCoordsOffset = uint3(i, i >> 2u, i >> 1u) & uint3(1u, 1u, 1u);
|
||||
uint3 probeCoords = clamp(base.ProbeCoords + probeCoordsOffset, uint3(0, 0, 0), data.ProbesCounts - uint3(1, 1, 1));
|
||||
probe.ProbeIndex = GetDDGIScrollingProbeIndex(data, cascade.CascadeIndex, probeCoords);
|
||||
|
||||
// Load probe position and state
|
||||
float4 probeData = LoadDDGIProbeData(data, probesData, cascade.CascadeIndex, probe.ProbeIndex);
|
||||
uint probeState = DecodeDDGIProbeState(probeData);
|
||||
uint useVisibility = true;
|
||||
float minWight = 0.001f;
|
||||
if (probeState == DDGI_PROBE_STATE_INACTIVE)
|
||||
{
|
||||
// Use fallback probe that is closest to this one
|
||||
uint3 fallbackCoords = DDGI_FALLBACK_COORDS_DECODE(probeData);
|
||||
float fallbackToProbeDist = length((float3)probeCoords - (float3)fallbackCoords);
|
||||
useVisibility = fallbackToProbeDist <= 1.0f; // Skip visibility test that blocks too far probes due to limiting max distance to 1.5 of probe spacing
|
||||
if (fallbackToProbeDist > 2.0f) minWight = 1.0f;
|
||||
probeCoords = fallbackCoords;
|
||||
probe.ProbeIndex = GetDDGIScrollingProbeIndex(data, cascade.CascadeIndex, fallbackCoords);
|
||||
probeData = LoadDDGIProbeData(data, probesData, cascade.CascadeIndex, probe.ProbeIndex);
|
||||
fallbacks++;
|
||||
//if (DecodeDDGIProbeState(probeData) == DDGI_PROBE_STATE_INACTIVE) continue;
|
||||
}
|
||||
|
||||
// Calculate probe position
|
||||
probe.ProbePosition = base.ProbeWorldPosition + (((float3)probeCoords - (float3)base.ProbeCoords) * cascade.ProbesSpacing) + probeData.xyz * cascade.ProbesSpacing;
|
||||
|
||||
// Calculate the distance and direction from the (biased and non-biased) shading point and the probe
|
||||
float3 worldPosToProbe = normalize(probe.ProbePosition - worldPosition);
|
||||
float3 biasedPosToProbe = normalize(probe.ProbePosition - cascade.BiasedWorldPosition);
|
||||
float biasedPosToProbeDist = length(probe.ProbePosition - cascade.BiasedWorldPosition) * 0.95f;
|
||||
|
||||
// Smooth backface test
|
||||
// x - weight, y - non-directional weight with a bias
|
||||
#if LIGHTING_NO_DIRECTIONAL
|
||||
probe.Weights = float2(1, 1);
|
||||
#else
|
||||
float backfaceWeight = Square(dot(worldPosToProbe, worldNormal) * 0.5f + 0.5f);
|
||||
probe.Weights = float2(max(backfaceWeight, 0.1f), backfaceWeight + 0.2f);
|
||||
#endif
|
||||
|
||||
// Sample distance texture
|
||||
float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, probe.ProbeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE);
|
||||
float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg;
|
||||
|
||||
// Visibility weight (Chebyshev)
|
||||
if (biasedPosToProbeDist > probeDistance.x && useVisibility)
|
||||
{
|
||||
float variance = abs(Square(probeDistance.x) - probeDistance.y);
|
||||
float visibilityWeight = variance / (variance + Square(biasedPosToProbeDist - probeDistance.x));
|
||||
visibilityWeight = lerp(1, visibilityWeight, data.IndirectShadowsStrength);
|
||||
probe.Weights *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.0f);
|
||||
}
|
||||
|
||||
// Avoid a weight of zero
|
||||
probe.Weights = max(probe.Weights, minWight);
|
||||
|
||||
// Adjust weight curve to inject a small portion of light
|
||||
const float minWeightThreshold = 0.2f;
|
||||
if (probe.Weights.x < minWeightThreshold)
|
||||
probe.Weights.x *= (probe.Weights.x * probe.Weights.x) * (1.0f / (minWeightThreshold * minWeightThreshold));
|
||||
|
||||
// Calculate trilinear weights based on the distance to each probe to smoothly transition between grid of 8 probes
|
||||
float3 trilinear = lerp(1.0f - base.BiasAlpha, base.BiasAlpha, (float3)probeCoordsOffset);
|
||||
probe.Weights *= saturate(trilinear.x * trilinear.y * trilinear.z * 2.0f);
|
||||
|
||||
return probe;
|
||||
}
|
||||
|
||||
float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, DDGICascadeSampling cascade)
|
||||
{
|
||||
bool invalidCascade = cascade.CascadeIndex >= data.CascadesCount;
|
||||
cascade.CascadeIndex = min(cascade.CascadeIndex, data.CascadesCount - 1);
|
||||
#if DDGI_FALLBACK_OUTER_DEDICATED_PROBE
|
||||
if (invalidCascade)
|
||||
{
|
||||
// Sample a special probe as a fallback for ambient GI outside the last cascade
|
||||
float2 octahedralCoords = GetOctahedralCoords(worldNormal);
|
||||
float2 uv = GetDDGIProbeUV(data, cascadeIndex, 0, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, 0, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
|
||||
float3 probeIrradiance = probesIrradiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
#if DDGI_SRGB_BLENDING == 1
|
||||
probeIrradiance = Square(pow(probeIrradiance, DDGI_SRGB_BLENDING_GAMMA));
|
||||
@@ -182,12 +289,8 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
return probeIrradiance;
|
||||
}
|
||||
#endif
|
||||
uint3 probeCoordsEnd = data.ProbesCounts - uint3(1, 1, 1);
|
||||
uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), uint3(0, 0, 0), probeCoordsEnd);
|
||||
|
||||
// Get the grid coordinates of the probe nearest the biased world position
|
||||
float3 baseProbeWorldPosition = GetDDGIProbeWorldPosition(data, cascadeIndex, baseProbeCoords);
|
||||
float3 biasAlpha = saturate((biasedWorldPosition - baseProbeWorldPosition) / probesSpacing);
|
||||
DDGIProbeBase base = GetDDGIProbeBase(data, cascade, worldPosition);
|
||||
|
||||
// Loop over the closest probes to accumulate their contributions
|
||||
float4 totalIrradiance = float4(0, 0, 0, 0);
|
||||
@@ -195,74 +298,11 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
float fallbacks = 0;
|
||||
for (uint i = 0; i < 8; i++)
|
||||
{
|
||||
uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1;
|
||||
uint3 probeCoords = clamp(baseProbeCoords + probeCoordsOffset, uint3(0, 0, 0), probeCoordsEnd);
|
||||
uint probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, probeCoords);
|
||||
|
||||
// Load probe position and state
|
||||
float4 probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex);
|
||||
uint probeState = DecodeDDGIProbeState(probeData);
|
||||
uint useVisibility = true;
|
||||
float minWight = 0.001f;
|
||||
if (probeState == DDGI_PROBE_STATE_INACTIVE)
|
||||
{
|
||||
// Use fallback probe that is closest to this one
|
||||
uint3 fallbackCoords = DDGI_FALLBACK_COORDS_DECODE(probeData);
|
||||
float fallbackToProbeDist = length((float3)probeCoords - (float3)fallbackCoords);
|
||||
useVisibility = fallbackToProbeDist <= 1.0f; // Skip visibility test that blocks too far probes due to limiting max distance to 1.5 of probe spacing
|
||||
if (fallbackToProbeDist > 2.0f) minWight = 1.0f;
|
||||
probeCoords = fallbackCoords;
|
||||
probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, fallbackCoords);
|
||||
probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex);
|
||||
fallbacks++;
|
||||
//if (DecodeDDGIProbeState(probeData) == DDGI_PROBE_STATE_INACTIVE) continue;
|
||||
}
|
||||
|
||||
// Calculate probe position
|
||||
float3 probePosition = baseProbeWorldPosition + (((float3)probeCoords - (float3)baseProbeCoords) * probesSpacing) + probeData.xyz * probesSpacing;
|
||||
|
||||
// Calculate the distance and direction from the (biased and non-biased) shading point and the probe
|
||||
float3 worldPosToProbe = normalize(probePosition - worldPosition);
|
||||
float3 biasedPosToProbe = normalize(probePosition - biasedWorldPosition);
|
||||
float biasedPosToProbeDist = length(probePosition - biasedWorldPosition) * 0.95f;
|
||||
|
||||
// Smooth backface test
|
||||
// x - weight, y - non-directional weight with a bias
|
||||
#if LIGHTING_NO_DIRECTIONAL
|
||||
float2 weights = float2(1, 1);
|
||||
#else
|
||||
float backfaceWeight = Square(dot(worldPosToProbe, worldNormal) * 0.5f + 0.5f);
|
||||
float2 weights = float2(max(backfaceWeight, 0.1f), backfaceWeight + 0.2f);
|
||||
#endif
|
||||
|
||||
// Sample distance texture
|
||||
float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe);
|
||||
float2 uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE);
|
||||
float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg;
|
||||
|
||||
// Visibility weight (Chebyshev)
|
||||
if (biasedPosToProbeDist > probeDistance.x && useVisibility)
|
||||
{
|
||||
float variance = abs(Square(probeDistance.x) - probeDistance.y);
|
||||
float visibilityWeight = variance / (variance + Square(biasedPosToProbeDist - probeDistance.x));
|
||||
visibilityWeight = lerp(1, visibilityWeight, data.IndirectShadowsStrength);
|
||||
weights *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.0f);
|
||||
}
|
||||
|
||||
// Avoid a weight of zero
|
||||
weights = max(weights, minWight);
|
||||
|
||||
// Adjust weight curve to inject a small portion of light
|
||||
const float minWeightThreshold = 0.2f;
|
||||
if (weights.x < minWeightThreshold) weights.x *= (weights.x * weights.x) * (1.0f / (minWeightThreshold * minWeightThreshold));
|
||||
|
||||
// Calculate trilinear weights based on the distance to each probe to smoothly transition between grid of 8 probes
|
||||
float3 trilinear = lerp(1.0f - biasAlpha, biasAlpha, (float3)probeCoordsOffset);
|
||||
weights *= saturate(trilinear.x * trilinear.y * trilinear.z * 2.0f);
|
||||
DDGIProbeSample probe = SampleDDGIProbe(data, probesData, probesDistance, worldPosition, worldNormal, cascade, base, i, fallbacks);
|
||||
|
||||
// Sample irradiance texture
|
||||
octahedralCoords = GetOctahedralCoords(worldNormal);
|
||||
uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
|
||||
float2 octahedralCoords = GetOctahedralCoords(worldNormal);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, probe.ProbeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_IRRADIANCE);
|
||||
float3 probeIrradiance = probesIrradiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
#if DDGI_SRGB_BLENDING == 1
|
||||
probeIrradiance = pow(probeIrradiance, DDGI_SRGB_BLENDING_GAMMA);
|
||||
@@ -271,17 +311,17 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
#endif
|
||||
|
||||
// Accumulate weighted irradiance
|
||||
totalIrradiance += float4(probeIrradiance * weights.x, weights.x);
|
||||
totalIrradianceNonDir += float4(probeIrradiance * weights.y, weights.y);
|
||||
totalIrradiance += float4(probeIrradiance * probe.Weights.x, probe.Weights.x);
|
||||
totalIrradianceNonDir += float4(probeIrradiance * probe.Weights.y, probe.Weights.y);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Debug DDGI cascades with colors
|
||||
if (cascadeIndex == 0)
|
||||
if (cascade.CascadeIndex == 0)
|
||||
totalIrradiance = float4(1, 0, 0, 1);
|
||||
else if (cascadeIndex == 1)
|
||||
else if (cascade.CascadeIndex == 1)
|
||||
totalIrradiance = float4(0, 1, 0, 1);
|
||||
else if (cascadeIndex == 2)
|
||||
else if (cascade.CascadeIndex == 2)
|
||||
totalIrradiance = float4(0, 0, 1, 1);
|
||||
else if (invalidCascade) // Area outside the last cascade that clamps to it
|
||||
totalIrradiance = float4(1, 0, 1, 1);
|
||||
@@ -309,6 +349,51 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D<snorm float4> probes
|
||||
return totalIrradiance.rgb;
|
||||
}
|
||||
|
||||
float3 SampleDDGISpecularCascade(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesRadiance, float3 worldPosition, float3 worldNormal, float3 reflection, DDGICascadeSampling cascade)
|
||||
{
|
||||
bool invalidCascade = cascade.CascadeIndex >= data.CascadesCount;
|
||||
cascade.CascadeIndex = min(cascade.CascadeIndex, data.CascadesCount - 1);
|
||||
#if DDGI_FALLBACK_OUTER_DEDICATED_PROBE
|
||||
if (invalidCascade)
|
||||
{
|
||||
// Sample a special probe as a fallback for ambient sky reflection outside the last cascade
|
||||
float2 octahedralCoords = GetOctahedralCoords(reflection);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, 0, octahedralCoords, DDGI_PROBE_RESOLUTION_RADIANCE);
|
||||
float3 probeRadiance = probesRadiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
return probeRadiance;
|
||||
}
|
||||
#endif
|
||||
|
||||
DDGIProbeBase base = GetDDGIProbeBase(data, cascade, worldPosition);
|
||||
|
||||
// Loop over the closest probes to accumulate their contributions
|
||||
float4 totalRadiance = float4(0, 0, 0, 0);
|
||||
float fallbacks = 0;
|
||||
for (uint i = 0; i < 8; i++)
|
||||
{
|
||||
DDGIProbeSample probe = SampleDDGIProbe(data, probesData, probesDistance, worldPosition, worldNormal, cascade, base, i, fallbacks);
|
||||
|
||||
// Parallax correction
|
||||
//float3 sampleVector = normalize((worldPosition - probe.ProbePosition) / (cascade.ProbesSpacing * 2) + reflection);
|
||||
float3 sampleVector = reflection;
|
||||
|
||||
// Sample radiance texture
|
||||
float2 octahedralCoords = GetOctahedralCoords(sampleVector);
|
||||
float2 uv = GetDDGIProbeUV(data, cascade.CascadeIndex, probe.ProbeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_RADIANCE);
|
||||
float3 probeRadiance = probesRadiance.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
// TODO: filter radiance based on roughness
|
||||
// TODO: or maybe build mip-chain for updated probes radiance texture and sample it based on roughness like env probes?
|
||||
|
||||
// Accumulate weighted radiance
|
||||
totalRadiance += float4(probeRadiance * probe.Weights.x, probe.Weights.x);
|
||||
}
|
||||
|
||||
// Normalize radiance
|
||||
totalRadiance.rgb /= max(totalRadiance.a, 0.0001f);
|
||||
|
||||
return totalRadiance.rgb;
|
||||
}
|
||||
|
||||
float3 GetDDGISurfaceBias(float3 viewDir, float probesSpacing, float3 worldNormal, float bias)
|
||||
{
|
||||
#if LIGHTING_NO_DIRECTIONAL
|
||||
@@ -327,67 +412,99 @@ float sdRoundBox(float3 p, float3 b, float 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
|
||||
float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = DDGI_DEFAULT_BIAS, float dither = 0.0f)
|
||||
DDGICascadeSampling GetDDGICascade(DDGIData data, float3 worldPosition, float3 worldNormal, float bias, float dither)
|
||||
{
|
||||
// Select the highest cascade that contains the sample location
|
||||
float probesSpacing = 0, cascadeWeight = 0;
|
||||
float3 probesOrigin = (float3)0, probesExtent = (float3)0, biasedWorldPosition = (float3)0;
|
||||
DDGICascadeSampling cascade;
|
||||
cascade.ProbesOrigin = float3(0, 0, 0);
|
||||
cascade.ProbesExtent = float3(0, 0, 0);
|
||||
cascade.BiasedWorldPosition = float3(0, 0, 0);
|
||||
cascade.ProbesSpacing = 0;
|
||||
cascade.CascadeWeight = 0;
|
||||
float3 viewDir = normalize(data.ViewPos - worldPosition);
|
||||
#if DDGI_CASCADE_BLEND_SMOOTH
|
||||
dither = 0.0f;
|
||||
#endif
|
||||
#ifdef DDGI_DEBUG_CASCADE
|
||||
uint cascadeIndex = DDGI_DEBUG_CASCADE;
|
||||
cascade.CascadeIndex = DDGI_DEBUG_CASCADE;
|
||||
#else
|
||||
uint cascadeIndex = 0;
|
||||
if (data.CascadesCount == 0)
|
||||
return float3(0, 0, 0);
|
||||
for (; cascadeIndex < data.CascadesCount; cascadeIndex++)
|
||||
cascade.CascadeIndex = 0;
|
||||
for (; cascade.CascadeIndex < data.CascadesCount; cascade.CascadeIndex++)
|
||||
{
|
||||
// Get cascade data
|
||||
probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
|
||||
probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz;
|
||||
probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
|
||||
biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias);
|
||||
cascade.ProbesSpacing = data.ProbesOriginAndSpacing[cascade.CascadeIndex].w;
|
||||
cascade.ProbesOrigin = data.ProbesScrollOffsets[cascade.CascadeIndex].xyz * cascade.ProbesSpacing + data.ProbesOriginAndSpacing[cascade.CascadeIndex].xyz;
|
||||
cascade.ProbesExtent = (data.ProbesCounts - 1) * (cascade.ProbesSpacing * 0.5f);
|
||||
cascade.BiasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, cascade.ProbesSpacing, worldNormal, bias);
|
||||
|
||||
// Calculate cascade blending weight (use input bias to smooth transition)
|
||||
float fadeDistance = probesSpacing * DDGI_CASCADE_BLEND_SIZE;
|
||||
float3 blendPos = worldPosition - data.BlendOrigin[cascadeIndex].xyz;
|
||||
cascadeWeight = sdRoundBox(blendPos, probesExtent - probesSpacing, probesSpacing * 2) + fadeDistance;
|
||||
cascadeWeight = 1 - saturate(cascadeWeight / fadeDistance);
|
||||
if (cascadeWeight > dither)
|
||||
float fadeDistance = cascade.ProbesSpacing * DDGI_CASCADE_BLEND_SIZE;
|
||||
float3 blendPos = worldPosition - data.BlendOrigin[cascade.CascadeIndex].xyz;
|
||||
cascade.CascadeWeight = sdRoundBox(blendPos, cascade.ProbesExtent - cascade.ProbesSpacing, cascade.ProbesSpacing * 2) + fadeDistance;
|
||||
cascade.CascadeWeight = 1 - saturate(cascade.CascadeWeight / fadeDistance);
|
||||
if (cascade.CascadeWeight > dither)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return cascade;
|
||||
}
|
||||
|
||||
// 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
|
||||
float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = DDGI_DEFAULT_BIAS, float dither = 0.0f)
|
||||
{
|
||||
if (data.CascadesCount == 0)
|
||||
return float3(0, 0, 0);
|
||||
|
||||
// Select the cascade
|
||||
DDGICascadeSampling cascade = GetDDGICascade(data, worldPosition, worldNormal, bias, dither);
|
||||
|
||||
// Sample cascade
|
||||
float3 result = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndex, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition);
|
||||
float3 result = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascade);
|
||||
|
||||
// Blend with the next cascade (or fallback irradiance outside the volume)
|
||||
#if DDGI_CASCADE_BLEND_SMOOTH && !defined(DDGI_DEBUG_CASCADE)
|
||||
cascadeIndex++;
|
||||
if (cascadeIndex <= data.CascadesCount && cascadeWeight < 0.99f)
|
||||
// Blend with the next cascade
|
||||
cascade.CascadeIndex++;
|
||||
if (cascade.CascadeIndex <= data.CascadesCount && cascade.CascadeWeight < 0.99f)
|
||||
{
|
||||
uint cascadeIndexTmp = cascadeIndex;
|
||||
cascadeIndex = min(cascadeIndex, data.CascadesCount - 1);
|
||||
probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
|
||||
probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz;
|
||||
probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
|
||||
biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias);
|
||||
float3 resultNext = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndexTmp, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition);
|
||||
result *= cascadeWeight;
|
||||
result += resultNext * (1 - cascadeWeight);
|
||||
uint cascadeIndexTmp = cascade.CascadeIndex;
|
||||
cascade.CascadeIndex = min(cascade.CascadeIndex, data.CascadesCount - 1);
|
||||
cascade.ProbesSpacing = data.ProbesOriginAndSpacing[cascade.CascadeIndex].w;
|
||||
cascade.ProbesOrigin = data.ProbesScrollOffsets[cascade.CascadeIndex].xyz * cascade.ProbesSpacing + data.ProbesOriginAndSpacing[cascade.CascadeIndex].xyz;
|
||||
cascade.ProbesExtent = (data.ProbesCounts - 1) * (cascade.ProbesSpacing * 0.5f);
|
||||
float3 viewDir = normalize(data.ViewPos - worldPosition);
|
||||
cascade.BiasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, cascade.ProbesSpacing, worldNormal, bias);
|
||||
float3 resultNext = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascade);
|
||||
result *= cascade.CascadeWeight;
|
||||
result += resultNext * (1 - cascade.CascadeWeight);
|
||||
}
|
||||
#endif
|
||||
if (cascadeIndex >= data.CascadesCount)
|
||||
|
||||
// Blend between the last cascade and the fallback irradiance
|
||||
if (cascade.CascadeIndex >= data.CascadesCount)
|
||||
{
|
||||
// Blend between the last cascade and the fallback irradiance
|
||||
float fallbackWeight = (1 - cascadeWeight) * data.FallbackIrradiance.a;
|
||||
float fallbackWeight = (1 - cascade.CascadeWeight) * data.FallbackIrradiance.a;
|
||||
result = lerp(result, data.FallbackIrradiance.rgb, fallbackWeight);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Samples DDGI probes volume at the given world-space position and returns the specular reflections.
|
||||
// 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
|
||||
float3 SampleDDGISpecular(DDGIData data, Texture2D<snorm float4> probesData, Texture2D<float4> probesDistance, Texture2D<float4> probesRadiance, float3 worldPosition, float3 worldNormal, float roughness, float bias = DDGI_DEFAULT_BIAS, float dither = 0.0f)
|
||||
{
|
||||
if (data.CascadesCount == 0)
|
||||
return float3(0, 0, 0);
|
||||
|
||||
// Select the cascade
|
||||
DDGICascadeSampling cascade = GetDDGICascade(data, worldPosition, worldNormal, bias, dither);
|
||||
|
||||
// Sample cascade
|
||||
float3 reflection = reflect(normalize(worldPosition - data.ViewPos), worldNormal);
|
||||
float3 specular = SampleDDGISpecularCascade(data, probesData, probesDistance, probesRadiance, worldPosition, worldNormal, reflection, cascade);
|
||||
|
||||
return specular;
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ RWByteAddressBuffer RWStats : register(u1);
|
||||
Texture3D<snorm float> GlobalSDFTex : register(t0);
|
||||
Texture3D<snorm float> GlobalSDFMip : register(t1);
|
||||
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t2);
|
||||
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t3);
|
||||
ByteAddressBuffer GlobalSurfaceAtlasCulledObjects : register(t3);
|
||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t4);
|
||||
Texture2D GlobalSurfaceAtlasDepth : register(t5);
|
||||
Texture2D GlobalSurfaceAtlasTex : register(t6);
|
||||
@@ -545,7 +545,7 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID)
|
||||
// Sample Global Surface Atlas to get the lighting at the hit location
|
||||
float3 hitPosition = hit.GetHitPosition(trace);
|
||||
float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, hit);
|
||||
float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, RWGlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -probeRayDirection, surfaceThreshold);
|
||||
float4 surfaceColor = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -probeRayDirection, surfaceThreshold);
|
||||
radiance = float4(surfaceColor.rgb, hit.HitTime);
|
||||
|
||||
// Add some bias to prevent self occlusion artifacts in Chebyshev due to Global SDF being very incorrect in small scale
|
||||
@@ -586,6 +586,10 @@ groupshared float OutputInstability[DDGI_PROBE_RESOLUTION * DDGI_PROBE_RESOLUTIO
|
||||
// Update distance
|
||||
#define DDGI_PROBE_RESOLUTION DDGI_PROBE_RESOLUTION_DISTANCE
|
||||
groupshared float CachedProbesTraceDistance[DDGI_TRACE_RAYS_LIMIT];
|
||||
#elif DDGI_PROBE_UPDATE_RADIANCE
|
||||
// Update radiance
|
||||
#define DDGI_PROBE_RESOLUTION DDGI_PROBE_RESOLUTION_RADIANCE
|
||||
groupshared float4 CachedProbesTraceRadiance[DDGI_TRACE_RAYS_LIMIT];
|
||||
#endif
|
||||
|
||||
// Source: https://github.com/turanszkij/WickedEngine
|
||||
@@ -699,14 +703,10 @@ static const uint4 BorderOffsets[BorderOffsetsSize] = {
|
||||
groupshared float3 CachedProbesTraceDirection[DDGI_TRACE_RAYS_LIMIT];
|
||||
|
||||
RWTexture2D<float4> RWOutput : register(u0);
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE
|
||||
RWTexture2D<snorm float4> RWProbesData : register(u1);
|
||||
#if DDGI_DEBUG_INSTABILITY
|
||||
RWTexture2D<snorm float4> ProbesData : register(u1);
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE && DDGI_DEBUG_INSTABILITY
|
||||
RWTexture2D<float> RWOutputInstability : register(u2);
|
||||
#endif
|
||||
#elif DDGI_PROBE_UPDATE_DEPTH
|
||||
Texture2D<snorm float4> ProbesData : register(t0);
|
||||
#endif
|
||||
Texture2D<float4> ProbesTrace : register(t1);
|
||||
ByteAddressBuffer ActiveProbes : register(t2);
|
||||
|
||||
@@ -714,6 +714,7 @@ ByteAddressBuffer ActiveProbes : register(t2);
|
||||
META_CS(true, FEATURE_LEVEL_SM5)
|
||||
META_PERMUTATION_1(DDGI_PROBE_UPDATE_IRRADIANCE=1)
|
||||
META_PERMUTATION_1(DDGI_PROBE_UPDATE_DEPTH=1)
|
||||
META_PERMUTATION_1(DDGI_PROBE_UPDATE_RADIANCE=1)
|
||||
[numthreads(DDGI_PROBE_RESOLUTION, DDGI_PROBE_RESOLUTION, 1)]
|
||||
void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex)
|
||||
{
|
||||
@@ -725,12 +726,8 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords);
|
||||
|
||||
// Load probe data
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE
|
||||
int2 probeDataCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex);
|
||||
float4 probeData = RWProbesData[probeDataCoords];
|
||||
#elif DDGI_PROBE_UPDATE_DEPTH
|
||||
float4 probeData = LoadDDGIProbeData(DDGI, ProbesData, CascadeIndex, probeIndex);
|
||||
#endif
|
||||
float4 probeData = ProbesData[probeDataCoords];
|
||||
float probeAttention = DecodeDDGIProbeAttention(probeData);
|
||||
uint probeState = DecodeDDGIProbeState(probeData);
|
||||
uint probeRaysCount = GetProbeRaysCount(DDGI, probeAttention);
|
||||
@@ -750,7 +747,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
for (uint i = 0; i < raysCount; i++)
|
||||
{
|
||||
uint rayIndex = raysStart + i;
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE || DDGI_PROBE_UPDATE_RADIANCE
|
||||
CachedProbesTraceRadiance[rayIndex] = ProbesTrace[uint2(rayIndex, GroupId.x)];
|
||||
#elif DDGI_PROBE_UPDATE_DEPTH
|
||||
float rayDistance = ProbesTrace[uint2(rayIndex, GroupId.x)].w;
|
||||
@@ -800,6 +797,14 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
// Add distance (R), distance^2 (G) and weight (A)
|
||||
float rayDistance = CachedProbesTraceDistance[rayIndex];
|
||||
result += float4(rayDistance, rayDistance * rayDistance, 0.0f, 1.0f) * rayWeight;
|
||||
#elif DDGI_PROBE_UPDATE_RADIANCE
|
||||
// Clamped cosine filtering
|
||||
// ["Ray tracing the world of Assassin's Creed Shadows", SIGGRAPH 2025]
|
||||
rayWeight = pow(saturate((rayWeight - 0.75) / (1 - 0.75)), 4);
|
||||
|
||||
// Add radiance (RGB) and weight (A)
|
||||
float4 rayRadiance = CachedProbesTraceRadiance[rayIndex];
|
||||
result += float4(rayRadiance.rgb * rayWeight, rayWeight);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -807,7 +812,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
float epsilon = (float)probeRaysCount * 1e-9f;
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE
|
||||
result.rgb *= 1.0f / (2.0f * max(result.a, epsilon));
|
||||
#elif DDGI_PROBE_UPDATE_DEPTH
|
||||
#elif DDGI_PROBE_UPDATE_DEPTH || DDGI_PROBE_UPDATE_RADIANCE
|
||||
result.rgb *= 1.0f / max(result.a, epsilon);
|
||||
#endif
|
||||
|
||||
@@ -829,7 +834,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
instability *= 2.0f; // Make it stronger on scene changes
|
||||
//instability = saturate(instability);
|
||||
OutputInstability[GroupIndex] = instability;
|
||||
#if DDGI_DEBUG_INSTABILITY
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE && DDGI_DEBUG_INSTABILITY
|
||||
RWOutputInstability[outputCoords] = instability;
|
||||
//RWOutputInstability[outputCoords] = probeAttention; // Debug test probe attention visualization
|
||||
#endif
|
||||
@@ -870,12 +875,18 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
//result.rgb = previous + (irradianceDelta * 0.25f);
|
||||
}
|
||||
result = float4(lerp(result.rgb, previous.rgb, historyWeight), 1.0f);
|
||||
#elif DDGI_PROBE_UPDATE_RADIANCE
|
||||
historyWeightSlow = 0.98f;
|
||||
historyWeight = lerp(historyWeightSlow, min(historyWeightFast * 1.1f, historyWeightSlow), probeAttention * probeAttention);
|
||||
result.rgb = lerp(result.rgb, previous, historyWeight);
|
||||
#elif DDGI_PROBE_UPDATE_DEPTH
|
||||
result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f);
|
||||
#endif
|
||||
|
||||
#if DDGI_PROBE_UPDATE_IRRADIANCE || DDGI_PROBE_UPDATE_RADIANCE
|
||||
// Apply quantization error to reduce yellowish artifacts due to R11G11B10 format
|
||||
float noise = InterleavedGradientNoise(octahedralCoords * 10, FrameIndexMod8);
|
||||
result.rgb = QuantizeColor(result.rgb, noise, QuantizationError);
|
||||
#elif DDGI_PROBE_UPDATE_DEPTH
|
||||
result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f);
|
||||
#endif
|
||||
|
||||
RWOutput[outputCoords] = result;
|
||||
@@ -909,7 +920,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
|
||||
// Update probe data for the next frame
|
||||
probeState = DDGI_PROBE_STATE_ACTIVE;
|
||||
RWProbesData[probeDataCoords] = EncodeDDGIProbeData(probeData.xyz, probeState, probeAttention);
|
||||
ProbesData[probeDataCoords] = EncodeDDGIProbeData(probeData.xyz, probeState, probeAttention);
|
||||
}
|
||||
|
||||
#if DDGI_DEBUG_INSTABILITY && defined(BorderOffsetsSize)
|
||||
@@ -955,7 +966,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_IndirectLighting
|
||||
#if defined(_PS_IndirectLighting) || defined(_PS_SpecularLighting)
|
||||
|
||||
#include "./Flax/GBuffer.hlsl"
|
||||
#include "./Flax/Random.hlsl"
|
||||
@@ -963,6 +974,15 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_
|
||||
|
||||
Texture2D<snorm float4> ProbesData : register(t4);
|
||||
Texture2D<float4> ProbesDistance : register(t5);
|
||||
|
||||
// Shared code for both irradiance and specular sampling
|
||||
#define DDGI_GET_DITHER RandN2(input.TexCoord + TemporalTime).x
|
||||
#define DDGI_GET_SAMPLE_POS gBuffer.WorldPos + gBuffer.Normal * (dither * 0.1f + 0.1f)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_IndirectLighting
|
||||
|
||||
Texture2D<float4> ProbesIrradiance : register(t6);
|
||||
|
||||
// Pixel shader for drawing indirect lighting in fullscreen
|
||||
@@ -973,15 +993,13 @@ float4 PS_IndirectLighting(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
// Sample GBuffer
|
||||
GBufferSample gBuffer = SampleGBuffer(GBuffer, input.TexCoord);
|
||||
|
||||
// Check if cannot shadow pixel
|
||||
BRANCH
|
||||
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT)
|
||||
return float4(0, 0, 0, 0);
|
||||
|
||||
// Sample irradiance
|
||||
float dither = RandN2(input.TexCoord + TemporalTime).x;
|
||||
float3 samplePos = gBuffer.WorldPos + gBuffer.Normal * (dither * 0.1f + 0.1f);
|
||||
float dither = DDGI_GET_DITHER;
|
||||
float3 samplePos = DDGI_GET_SAMPLE_POS;
|
||||
float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, samplePos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither);
|
||||
|
||||
// Calculate lighting
|
||||
@@ -991,3 +1009,27 @@ float4 PS_IndirectLighting(Quad_VS2PS input) : SV_Target0
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_SpecularLighting
|
||||
|
||||
Texture2D<float4> ProbesRadiance : register(t6);
|
||||
|
||||
// Pixel shader for drawing specular lighting in fullscreen
|
||||
META_PS(true, FEATURE_LEVEL_SM5)
|
||||
float4 PS_SpecularLighting(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
// Sample GBuffer
|
||||
GBufferSample gBuffer = SampleGBuffer(GBuffer, input.TexCoord);
|
||||
BRANCH
|
||||
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT)
|
||||
return float4(0, 0, 0, 0);
|
||||
|
||||
// Sample specular reflection
|
||||
float dither = DDGI_GET_DITHER;
|
||||
float3 samplePos = DDGI_GET_SAMPLE_POS;
|
||||
float3 specular = SampleDDGISpecular(DDGI, ProbesData, ProbesDistance, ProbesRadiance, samplePos, gBuffer.Normal, gBuffer.Roughness, DDGI_DEFAULT_BIAS, dither);
|
||||
|
||||
return float4(specular, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user