Files
sbox-public/engine/Sandbox.Engine/Scene/Components/IndirectLighting/DDGIVolumeSystem.cs
Sam Pavlovic a5154e8856 Indirect Light Volumes Blotch Fix (#5009)
* Remove DDGI border, just get the accurate direction

* Try to use BC6H in place of RG16F for Indirect Light Volume depth now, we're using softer depth so we can take advantage of it no problem like Overwatch does.

* softer DDGI depth integration

* Blend DDGI borders nicely and simply, instead of wrapping a 6x6 probe

* Update shaders

* update terrain shader from rebase
2026-06-05 16:45:40 +01:00

105 lines
2.6 KiB
C#

namespace Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Maintains GPU data for all <see cref="IndirectLightVolume"/> instances in a scene.
/// Collects, sorts, and uploads volume parameters to the renderer.
/// </summary>
sealed class DDGIVolumeSystem : GameObjectSystem<DDGIVolumeSystem>
{
private GpuBuffer<IndirectLightVolume.DDGIVolumeGpuData> GpuBuffer;
private bool _dirty = true;
public DDGIVolumeSystem( Scene scene ) : base( scene )
{
Listen( Stage.FinishUpdate, 0, UpdateVolumes, "UpdateDDGIVolumes" );
}
public override void Dispose()
{
ReleaseBuffer();
Scene?.RenderAttributes?.Set( "DDGI_VolumeCount", 0 );
Scene?.RenderAttributes?.Set( "DDGI_Volumes", (GpuBuffer)null );
base.Dispose();
}
internal void MarkDirty()
{
_dirty = true;
}
private void UpdateVolumes()
{
using var _ = PerformanceStats.Timings.Render.Scope();
if ( Application.IsHeadless )
return;
if ( Scene?.RenderAttributes is null )
return;
// Mark textures as used every frame, so the streaming system keeps
// them resident while the volumes are active.
foreach ( var volume in Scene.GetAll<IndirectLightVolume>().Where( v => v is { Active: true, Enabled: true } ) )
{
volume.IrradianceTexture?.MarkUsed();
volume.DistanceTexture?.MarkUsed();
volume.RelocationTexture?.MarkUsed();
}
if ( !_dirty )
return;
_dirty = false;
var orderedVolumes = Scene
.GetAll<IndirectLightVolume>()
.Where( volume => volume is { Active: true } )
.OrderBy( volume => volume.Bounds.Volume );
var volumeData = new List<IndirectLightVolume.DDGIVolumeGpuData>();
foreach ( var volume in orderedVolumes )
{
if ( volume.Enabled && volume.BuildData( out var data ) )
{
volumeData.Add( data );
}
}
if ( volumeData.Count > 0 )
{
EnsureBufferCapacity( volumeData.Count );
GpuBuffer.SetData( volumeData );
Scene.RenderAttributes.Set( "DDGI_VolumeCount", volumeData.Count );
Scene.RenderAttributes.Set( "DDGI_Volumes", GpuBuffer );
return;
}
// No valid volumes: clear renderer attributes to avoid stale data.
ReleaseBuffer();
Scene.RenderAttributes.Set( "DDGI_VolumeCount", 0 );
Scene.RenderAttributes.Set( "DDGI_Volumes", (GpuBuffer)null );
}
private void EnsureBufferCapacity( int count )
{
if ( GpuBuffer is not null && GpuBuffer.ElementCount >= count )
return;
ReleaseBuffer();
GpuBuffer = new GpuBuffer<IndirectLightVolume.DDGIVolumeGpuData>( Math.Max( count, 1 ), debugName: "DDGI_Volumes" );
}
private void ReleaseBuffer()
{
GpuBuffer?.Dispose();
GpuBuffer = null;
}
}