[Serializable]
public struct DensityVolumeParameters
{
public Color albedo ; // Single scattering albedo [0, 1]. Alpha is ignored
public float meanFreePath ; // In meters [1, inf ]. Should be chromatic - this is an optimization!
public float asymmetry ; // Only used if (isLocal == false)
public Color albedo ; // Single scattering albedo: [0, 1]. Alpha is ignored
public float meanFreePath ; // In meters: [1, 1000000 ]. Should be chromatic - this is an optimization!
public float asymmetry ; // Controls the phase function: [-1, 1]
public void Constrain ( )
{
Normal ,
Ultra ,
Count
}
class VBuffer
} // enum VolumetricLightingPreset
[Serializable]
public struct ControllerParameters
public float vBufferNearPlane ; // Distance in meters
public float vBufferFarPlane ; // Distance in meters
public float depthSliceDistributionUniformity ; // Controls the exponential depth distribution: [0, 1]
} // struct ControllerParameters
public class VBuffer
{
public struct Parameters
{
public Vector4 resolution ;
public Vector2 sliceCount ;
public Vector4 depthEncodingParams ;
public Vector4 depthDecodingParams ;
public Parameters ( int w , int h , int d , ControllerParameters controlParams )
{
resolution = new Vector4 ( w , h , 1.0f / w , 1.0f / h ) ;
sliceCount = new Vector2 ( d , 1.0f / d ) ;
depthEncodingParams = Vector4 . zero ; // C# doesn't allow function calls before all members have been init
depthDecodingParams = Vector4 . zero ; // C# doesn't allow function calls before all members have been init
Update ( controlParams ) ;
}
public void Update ( ControllerParameters controlParams )
{
float n = controlParams . vBufferNearPlane ;
float f = controlParams . vBufferFarPlane ;
float c = 2 - 2 * controlParams . depthSliceDistributionUniformity ; // remap [0, 1] -> [2, 0]
depthEncodingParams = ComputeLogarithmicDepthEncodingParams ( n , f , c ) ;
depthDecodingParams = ComputeLogarithmicDepthDecodingParams ( n , f , c ) ;
}
} // struct Parameters
const int k_NumFrames = 2 ; // Double-buffer history and feedback
const int k_NumBuffers = 4 ; // See the list below
long m_ViewID = - 1 ; // -1 is invalid; positive for Game Views, 0 otherwise
RenderTexture [ ] m_Textures = null ;
RenderTargetIdentifier [ ] m_Identifiers = null ;
long m_ViewID = - 1 ; // (m_ViewID > 0) if valid
RenderTexture [ ] m_Textures = null ;
RenderTargetIdentifier [ ] m_Identifiers = null ;
Parameters [ ] m_Params = null ; // For the current and the previous frame
public long GetViewID ( )
{
return m_ViewID ;
}
public bool IsValid ( )
{
return m_ViewID > 0 & & m_Textures ! = null & & m_Textures [ 0 ] ! = null ;
}
public Parameters GetParameters ( uint frameIndex )
{
return m_Params [ frameIndex & 1 ] ;
}
public void SetParameters ( Parameters parameters , uint frameIndex )
{
m_Params [ frameIndex & 1 ] = parameters ;
}
Debug . Assert ( m_ViewID > = 0 ) ;
Debug . Assert ( IsValid ( ) ) ;
Debug . Assert ( m_ViewID > = 0 ) ;
Debug . Assert ( IsValid ( ) ) ;
public RenderTargetIdentifier GetLightingHistoryBuffer ( ) // From the previous frame
public RenderTargetIdentifier GetLightingHistoryBuffer ( uint frameIndex ) // From the previous frame
Debug . Assert ( m_ViewID > 0 ) ; // Game View only
return m_Identifiers [ k_IndexHistory + ( Time . renderedFrameCount & 1 ) ] ;
Debug . Assert ( IsValid ( ) ) ;
return m_Identifiers [ k_IndexHistory + ( frameIndex & 1 ) ] ;
public RenderTargetIdentifier GetLightingFeedbackBuffer ( ) // For the next frame
public RenderTargetIdentifier GetLightingFeedbackBuffer ( uint frameIndex ) // For the next frame
Debug . Assert ( m_ViewID > 0 ) ; // Game View only
return m_Identifiers [ k_IndexFeedback - ( Time . renderedFrameCount & 1 ) ] ;
Debug . Assert ( IsValid ( ) ) ;
return m_Identifiers [ k_IndexFeedback - ( frameIndex & 1 ) ] ;
public void Create ( long viewID , int w , int h , int d )
public void Create ( long viewID , int w , int h , int d , ControllerParameters controlParams )
Debug . Assert ( viewID > = 0 ) ;
Debug . Assert ( viewID > 0 ) ;
// Only Game Views need history and feedback buffers.
bool isGameView = viewID > 0 ;
int n = isGameView ? 4 : 2 ;
m_Textures = new RenderTexture [ n ] ;
m_Identifiers = new RenderTargetIdentifier [ n ] ;
m_Textures = new RenderTexture [ k_NumBuffers ] ;
m_Identifiers = new RenderTargetIdentifier [ k_NumBuffers ] ;
m_Params = new Parameters [ k_NumFrames ] ;
for ( int i = 0 ; i < n ; i + + )
for ( int i = 0 ; i < k_NumBuffers ; i + + )
m_Textures [ i ] = new RenderTexture ( w , h , 0 , RenderTextureFormat . ARGBHalf , RenderTextureReadWrite . Linear ) ;
m_Textures [ i ] = new RenderTexture ( w , h , 0 , RenderTextureFormat . ARGBHalf , RenderTextureReadWrite . Linear ) ;
m_Textures [ i ] . hideFlags = HideFlags . HideAndDontSave ;
m_Textures [ i ] . filterMode = FilterMode . Trilinear ; // Custom
m_Textures [ i ] . dimension = TextureDimension . Tex3D ; // TODO: request the thick 3D tiling layout
m_Identifiers [ i ] = new RenderTargetIdentifier ( m_Textures [ i ] ) ;
}
// Start with the same parameters for both frames. Then incrementally update them.
Parameters parameters = new Parameters ( w , h , d , controlParams ) ;
m_Params [ 0 ] = parameters ;
m_Params [ 1 ] = parameters ;
}
public void Destroy ( )
for ( int i = 0 , n = m_Textures . Length ; i < n ; i + + )
for ( int i = 0 ; i < k_NumBuffers ; i + + )
{
if ( m_Textures [ i ] ! = null )
{
m_ViewID = - 1 ;
m_Textures = null ;
m_Identifiers = null ;
}
public void GetResolution ( ref int w , ref int h , ref int d )
{
Debug . Assert ( m_Textures ! = null ) ;
Debug . Assert ( m_Textures [ 0 ] ! = null ) ;
Debug . Assert ( m_Identifiers ! = null ) ;
w = m_Textures [ 0 ] . width ;
h = m_Textures [ 0 ] . height ;
d = m_Textures [ 0 ] . volumeDepth ;
m_Params = null ;
public long GetViewID ( )
{
return m_ViewID ;
}
public bool IsValid ( )
{
return m_ViewID > = 0 & & m_Textures ! = null & & m_Textures [ 0 ] ! = null ;
}
ComputeShader m_VolumeVoxelizationCS = null ;
ComputeShader m_VolumetricLightingCS = null ;
static ComputeShader m_VolumeVoxelizationCS = null ;
static ComputeShader m_VolumetricLightingCS = null ;
List < VBuffer > m_VBuffers = null ;
List < OrientedBBox > m_VisibleVolumeBounds = null ;
List < DensityVolumeData > m_VisibleVolumeData = null ;
public const int k_MaxVisibleVolumeCount = 5 1 2 ;
List < VBuffer > m_VBuffers = null ;
List < OrientedBBox > m_VisibleVolumeBounds = null ;
List < DensityVolumeData > m_VisibleVolumeData = null ;
public const int k_MaxVisibleVolumeCount = 5 1 2 ;
static ComputeBuffer s_VisibleVolumeBoundsBuffer = null ;
static ComputeBuffer s_VisibleVolumeDataBuffer = null ;
float m_VBufferNearPlane = 0.5f ; // Distance in meters; dynamic modifications not handled by reprojection
float m_VBufferFarPlane = 6 4.0f ; // Distance in meters; dynamic modifications not handled by reprojection
const float k_LogScale = 0.5f ; // Tweak constant, controls the logarithmic depth distribution
static ComputeBuffer s_VisibleVolumeBoundsBuffer = null ;
static ComputeBuffer s_VisibleVolumeDataBuffer = null ;
public void Build ( HDRenderPipelineAsset asset )
{
CoreUtils . SafeRelease ( s_VisibleVolumeDataBuffer ) ;
}
public void ResizeVBuffer ( HDCamera camera , int screenWidth , int screenHeight )
public void ResizeVBufferAndUpdateProperties ( HDCamera camera , uint frameIndex )
long viewID = camera . GetViewID ( ) ;
var controller = camera . camera . GetComponent < VolumetricLightingController > ( ) ;
Debug . Assert ( viewID > = 0 ) ;
if ( camera . camera . cameraType = = CameraType . SceneView )
{
// HACK: since it's not possible to add a component to a scene camera,
// we take one from the "main" camera (if present).
Camera mainCamera = Camera . main ;
if ( mainCamera ! = null )
{
controller = mainCamera . GetComponent < VolumetricLightingController > ( ) ;
}
}
if ( controller = = null ) return ;
int screenWidth = ( int ) camera . screenSize . x ;
int screenHeight = ( int ) camera . screenSize . y ;
long viewID = camera . GetViewID ( ) ;
Debug . Assert ( viewID > 0 ) ;
int w = 0 , h = 0 , d = 0 ;
ComputeVBufferResolutionAndScale ( preset , screenWidth , screenHeight , ref w , ref h , ref d ) ;
if ( vBuffer ! = null )
{
int width = 0 , height = 0 , depth = 0 ;
vBuffer . GetResolution ( ref width , ref height , ref depth ) ;
VBuffer . Parameters frameParams = vBuffer . GetParameters ( frameIndex ) ;
if ( w = = width & & h = = height & & d = = depth )
if ( w = = frameParams . resolution . x & &
h = = frameParams . resolution . y & &
d = = frameParams . sliceCount . x )
// Everything matches, nothing to do here.
// The resolution matches.
// Depth parameters may have changed, so update those.
frameParams . Update ( controller . parameters ) ;
vBuffer . SetParameters ( frameParams , frameIndex ) ;
return ;
}
}
m_VBuffers . Add ( vBuffer ) ;
}
vBuffer . Create ( viewID , w , h , d ) ;
vBuffer . Create ( viewID , w , h , d , controller . parameters ) ;
Debug . Assert ( viewID > = 0 ) ;
Debug . Assert ( viewID > 0 ) ;
VBuffer vBuffer = null ;
// Since a single voxel corresponds to a tile (e.g. 8x8) of pixels,
// the VBuffer can potentially extend past the boundaries of the viewport.
// The function returns the fraction of the {width, height} of the VBuffer visible on screen.
// Note: for performance reasons, scale is unused (implicitly 1). The error is typically under 1%.
// Note: for performance reasons, the scale is unused (implicitly 1). The error is typically under 1%.
static Vector2 ComputeVBufferResolutionAndScale ( VolumetricLightingPreset preset ,
int screenWidth , int screenHeight ,
ref int w , ref int h , ref int d )
float n = nearPlane ;
float f = farPlane ;
depthParams . x = Mathf . Log ( c , 2 ) * ( 1.0f / Mathf . Log ( c * ( f - n ) + 1 , 2 ) ) ;
c = Mathf . Max ( c , 0.001f ) ; // Avoid NaNs
depthParams . x = Mathf . Log ( c , 2 ) * depthParams . y ;
depthParams . z = n - 1.0f / c ; // Same
depthParams . w = 0.0f ;
float n = nearPlane ;
float f = farPlane ;
c = Mathf . Max ( c , 0.001f ) ; // Avoid NaNs
depthParams . y = c * ( f - n ) + 1 ;
depthParams . y = Mathf . Log ( c * ( f - n ) + 1 , 2 ) ;
depthParams . z = n - 1.0f / c ; // Same
depthParams . w = 0.0f ;
return ( 1.0f / ( 4.0f * Mathf . PI ) ) * 1.5f * ( 1.0f - g * g ) / ( 2.0f + g * g ) ;
}
public void PushGlobalParams ( HDCamera camera , CommandBuffer cmd )
public void PushGlobalParams ( HDCamera camera , CommandBuffer cmd , uint frameIndex )
if ( visualEnvironment = = null | | visualEnvironment . fogType ! = FogType . Volumetric ) return ;
if ( visualEnvironment . fogType ! = FogType . Volumetric ) return ;
// Modify the near plane.
// Warning: it can screw up the reprojection. However, we have to do it in order for clustered lighting to work correctly.
m_VBufferNearPlane = camera . camera . nearClipPlane ;
// VisualEnvironment sets global fog parameters: _GlobalAsymmetry, _GlobalScattering, _GlobalExtinction.
Debug . Assert ( vBuffer ! = null ) ;
int w = 0 , h = 0 , d = 0 ;
vBuffer . GetResolution ( ref w , ref h , ref d ) ;
if ( vBuffer = = null )
{
// Set the neutral black texture.
cmd . SetGlobalTexture ( HDShaderIDs . _VBufferLighting , CoreUtils . blackVolumeTexture ) ;
return ;
}
// Get the interpolated asymmetry value.
var fog = VolumeManager . instance . stack . GetComponent < VolumetricFog > ( ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferResolution , new Vector4 ( w , h , 1.0f / w , 1.0f / h ) ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferSliceCount , new Vector4 ( d , 1.0f / d ) ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferDepthEncodingParams , ComputeLogarithmicDepthEncodingParams ( m_VBufferNearPlane , m_VBufferFarPlane , k_LogScale ) ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferDepthDecodingParams , ComputeLogarithmicDepthDecodingParams ( m_VBufferNearPlane , m_VBufferFarPlane , k_LogScale ) ) ;
cmd . SetGlobalTexture ( HDShaderIDs . _VBufferLighting , vBuffer . GetLightingIntegralBuffer ( ) ) ;
var currFrameParams = vBuffer . GetParameters ( frameIndex ) ;
var prevFrameParams = vBuffer . GetParameters ( frameIndex - 1 ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferResolution , currFrameParams . resolution ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferSliceCount , currFrameParams . sliceCount ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferDepthEncodingParams , currFrameParams . depthEncodingParams ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferDepthDecodingParams , currFrameParams . depthDecodingParams ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferPrevResolution , prevFrameParams . resolution ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferPrevSliceCount , prevFrameParams . sliceCount ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferPrevDepthEncodingParams , prevFrameParams . depthEncodingParams ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferPrevDepthDecodingParams , prevFrameParams . depthDecodingParams ) ;
cmd . SetGlobalTexture ( HDShaderIDs . _VBufferLighting , vBuffer . GetLightingIntegralBuffer ( ) ) ;
}
public DensityVolumeList PrepareVisibleDensityVolumeList ( HDCamera camera , CommandBuffer cmd )
if ( preset = = VolumetricLightingPreset . Off ) return densityVolumes ;
if ( visualEnvironment = = null | | visualEnvironment . fogType ! = FogType . Volumetric ) return densityVolumes ;
if ( visualEnvironment . fogType ! = FogType . Volumetric ) return densityVolumes ;
VBuffer vBuffer = FindVBuffer ( camera . GetViewID ( ) ) ;
if ( vBuffer = = null ) return densityVolumes ;
using ( new ProfilingSample ( cmd , "Prepare Visible Density Volume List" ) )
{
m_VisibleVolumeData . Clear ( ) ;
// Collect all visible finite volume data, and upload it to the GPU.
HomogeneousDensityVolume [ ] volumes = Object . FindObjectsOfType ( typeof ( HomogeneousDensityVolume ) ) as HomogeneousDensityVolume [ ] ;
HomogeneousDensityVolume [ ] volumes = DensityVolumeManager . manager . GetAllVolumes ( ) ;
// Only test active finite volumes.
if ( volume . enabled )
{
// TODO: cache these?
var obb = OrientedBBox . Create ( volume . transform ) ;
// TODO: cache these?
var obb = OrientedBBox . Create ( volume . transform ) ;
// Handle camera-relative rendering.
obb . center - = camOffset ;
// Handle camera-relative rendering.
obb . center - = camOffset ;
// Frustum cull on the CPU for now. TODO: do it on the GPU.
if ( GeometryUtils . Overlap ( obb , camera . frustum , 6 , 8 ) )
{
// TODO: cache these?
var data = volume . parameters . GetData ( ) ;
// Frustum cull on the CPU for now. TODO: do it on the GPU.
if ( GeometryUtils . Overlap ( obb , camera . frustum , 6 , 8 ) )
{
// TODO: cache these?
var data = volume . parameters . GetData ( ) ;
m_VisibleVolumeBounds . Add ( obb ) ;
m_VisibleVolumeData . Add ( data ) ;
}
m_VisibleVolumeBounds . Add ( obb ) ;
m_VisibleVolumeData . Add ( data ) ;
}
}
// Fill the struct with pointers in order to share the data with the light loop.
densityVolumes . bounds = m_VisibleVolumeBounds ;
densityVolumes . bounds = m_VisibleVolumeBounds ;
densityVolumes . density = m_VisibleVolumeData ;
return densityVolumes ;
public void VolumeVoxelizationPass ( DensityVolumeList densityVolumes , HDCamera camera , CommandBuffer cmd , FrameSettings settings )
public void VolumeVoxelizationPass ( DensityVolumeList densityVolumes , HDCamera camera , CommandBuffer cmd , FrameSettings settings , uint frameIndex )
if ( visualEnvironment = = null | | visualEnvironment . fogType ! = FogType . Volumetric ) return ;
if ( visualEnvironment . fogType ! = FogType . Volumetric ) return ;
VBuffer vBuffer = FindVBuffer ( camera . GetViewID ( ) ) ;
if ( vBuffer = = null ) return ;
using ( new ProfilingSample ( cmd , "Volume Voxelization" ) )
{
// Use the workaround by running the full shader with 0 density
}
VBuffer vBuffer = FindVBuffer ( camera . GetViewID ( ) ) ;
Debug . Assert ( vBuffer ! = null ) ;
int w = 0 , h = 0 , d = 0 ;
vBuffer . GetResolution ( ref w , ref h , ref d ) ;
float vFoV = camera . camera . fieldOfView * Mathf . Deg2Rad ;
Vector4 resolution = new Vector4 ( w , h , 1.0f / w , 1.0f / h ) ;
Matrix4x4 transform = HDUtils . ComputePixelCoordToWorldSpaceViewDirectionMatrix ( vFoV , resolution , camera . viewMatrix , false ) ;
var frameParams = vBuffer . GetParameters ( frameIndex ) ;
Vector4 resolution = frameParams . resolution ;
float vFoV = camera . camera . fieldOfView * Mathf . Deg2Rad ;
// Compose the matrix which allows us to compute the world space view direction.
Matrix4x4 transform = HDUtils . ComputePixelCoordToWorldSpaceViewDirectionMatrix ( vFoV , resolution , camera . viewMatrix , false ) ;
cmd . SetComputeTextureParam ( m_VolumeVoxelizationCS , kernel , HDShaderIDs . _VBufferDensity , vBuffer . GetDensityBuffer ( ) ) ;
cmd . SetComputeBufferParam ( m_VolumeVoxelizationCS , kernel , HDShaderIDs . _VolumeBounds , s_VisibleVolumeBoundsBuffer ) ;
cmd . SetComputeMatrixParam ( m_VolumeVoxelizationCS , HDShaderIDs . _VBufferCoordToViewDirWS , transform ) ;
cmd . SetComputeIntParam ( m_VolumeVoxelizationCS , HDShaderIDs . _NumVisibleDensityVolumes , numVisibleVolumes ) ;
int w = ( int ) resolution . x ;
int h = ( int ) resolution . y ;
// The shader defines GROUP_SIZE_1D = 8.
cmd . DispatchCompute ( m_VolumeVoxelizationCS , kernel , ( w + 7 ) / 8 , ( h + 7 ) / 8 , 1 ) ;
return coords ;
}
public void VolumetricLightingPass ( HDCamera camera , CommandBuffer cmd , FrameSettings settings )
public void VolumetricLightingPass ( HDCamera camera , CommandBuffer cmd , FrameSettings settings , uint frameIndex )
if ( visualEnvironment = = null | | visualEnvironment . fogType ! = FogType . Volumetric ) return ;
if ( visualEnvironment . fogType ! = FogType . Volumetric ) return ;
VBuffer vBuffer = FindVBuffer ( camera . GetViewID ( ) ) ;
if ( vBuffer = = null ) return ;
VBuffer vBuffer = FindVBuffer ( camera . GetViewID ( ) ) ;
Debug . Assert ( vBuffer ! = null ) ;
// Only available in the Play Mode because all the frame counters in the Edit Mode are broken.
bool enableClustered = settings . lightLoopSettings . enableTileAndCluster ;
bool enableReprojection = Application . isPlaying & & camera . camera . cameraType = = CameraType . Game ;
: "VolumetricLightingBruteforce" ) ;
}
int w = 0 , h = 0 , d = 0 ;
vBuffer . GetResolution ( ref w , ref h , ref d ) ;
var frameParams = vBuffer . GetParameters ( frameIndex ) ;
Vector4 resolution = frameParams . resolution ;
float vFoV = camera . camera . fieldOfView * Mathf . Deg2Rad ;
float vFoV = camera . camera . fieldOfView * Mathf . Deg2Rad ;
Vector4 resolution = new Vector4 ( w , h , 1.0f / w , 1.0f / h ) ;
Matrix4x4 transform = HDUtils . ComputePixelCoordToWorldSpaceViewDirectionMatrix ( vFoV , resolution , camera . viewMatrix , false ) ;
Matrix4x4 transform = HDUtils . ComputePixelCoordToWorldSpaceViewDirectionMatrix ( vFoV , resolution , camera . viewMatrix , false ) ;
Vector2 [ ] xySeq = GetHexagonalClosePackedSpheres7 ( ) ;
// | x | x | x | x | x | x | x |
float [ ] zSeq = { 7.0f / 1 4.0f , 3.0f / 1 4.0f , 1 1.0f / 1 4.0f , 5.0f / 1 4.0f , 9.0f / 1 4.0f , 1.0f / 1 4.0f , 1 3.0f / 1 4.0f } ;
int rfc = Time . renderedFrameCount ;
int sampleIndex = rfc % 7 ;
int sampleIndex = ( int ) frameIndex % 7 ;
Vector4 offset = new Vector4 ( xySeq [ sampleIndex ] . x , xySeq [ sampleIndex ] . y , zSeq [ sampleIndex ] , rfc ) ;
Vector4 offset = new Vector4 ( xySeq [ sampleIndex ] . x , xySeq [ sampleIndex ] . y , zSeq [ sampleIndex ] , frameIndex ) ;
// Get the interpolated asymmetry value.
var fog = VolumeManager . instance . stack . GetComponent < VolumetricFog > ( ) ;
cmd . SetComputeTextureParam ( m_VolumetricLightingCS , kernel , HDShaderIDs . _VBufferLightingIntegral , vBuffer . GetLightingIntegralBuffer ( ) ) ; // Write
if ( enableReprojection )
{
cmd . SetComputeTextureParam ( m_VolumetricLightingCS , kernel , HDShaderIDs . _VBufferLightingFeedback , vBuffer . GetLightingFeedbackBuffer ( ) ) ; // Write
cmd . SetComputeTextureParam ( m_VolumetricLightingCS , kernel , HDShaderIDs . _VBufferLightingHistory , vBuffer . GetLightingHistoryBuffer ( ) ) ; // Read
cmd . SetComputeTextureParam ( m_VolumetricLightingCS , kernel , HDShaderIDs . _VBufferLightingFeedback , vBuffer . GetLightingFeedbackBuffer ( frameIndex ) ) ; // Write
cmd . SetComputeTextureParam ( m_VolumetricLightingCS , kernel , HDShaderIDs . _VBufferLightingHistory , vBuffer . GetLightingHistoryBuffer ( frameIndex ) ) ; // Read
int w = ( int ) resolution . x ;
int h = ( int ) resolution . y ;
// The shader defines GROUP_SIZE_1D = 8.
cmd . DispatchCompute ( m_VolumetricLightingCS , kernel , ( w + 7 ) / 8 , ( h + 7 ) / 8 , 1 ) ;