namespace UnityEngine.Experimental.Rendering.HDPipeline
{
[GenerateHLSL]
public struct VolumeProperties
{
public Bounds bounds ; // Position and dimensions in meters
public Color albedo ; // Single scattering albedo [0, 1]
public float meanFreePath ; // In meters [1, inf]. Should be chromatic - this is an optimization!
public float asymmetry ; // Single global parameter for all volumes. TODO: UX
public VolumeParameters ( )
{
asymmetry = 0.0f ;
}
public bool IsVolumeUnbounded ( )
albedo . b = Mathf . Clamp01 ( albedo . b ) ;
meanFreePath = Mathf . Max ( meanFreePath , 1.0f ) ;
asymmetry = Mathf . Clamp ( asymmetry , - 1.0f , 1.0f ) ;
}
public VolumeProperties GetProperties ( )
}
} // class VolumeParameters
public partial class HDRenderPipeline : RenderPipelin e
public class VolumetricLightingModul e
{
public enum VolumetricLightingPreset
{
Count
} ;
VolumetricLightingPreset m_VolumetricLightingPreset
{ get { return ( VolumetricLightingPreset ) Math . Min ( ShaderConfig . s_VolumetricLightingPreset , ( int ) VolumetricLightingPreset . Count ) ; } }
ComputeShader m_VolumetricLightingCS { get { return m_Asset . renderPipelineResources . volumetricLightingCS ; } }
}
class VBuffer
{
public int viewID = - 1 ; // -1 is invalid; positive for Game Views, 0 otherwise
Debug . Assert ( viewID > 0 ) ; // Game View only
return lightingRTID [ 1 + ( ( Time . renderedFrameCount + 1 ) & 1 ) ] ;
}
} ;
List < VBuffer > m_VBuffers = 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
public void Create ( int viewID , int w , int h , int d )
{
Debug . Assert ( viewID > = 0 ) ;
Debug . Assert ( w > 0 & & h > 0 & & d > 0 ) ;
// Warning: different views can use the same camera!
int GetViewID ( HDCamera camera )
{
Debug . Assert ( camera ! = null ) ;
// Clean up first.
Destroy ( ) ;
if ( camera . camera . cameraType = = CameraType . Game )
{
int viewID = camera . camera . GetInstanceID ( ) ;
Debug . Assert ( viewID > 0 ) ;
return viewID ;
// The required number of buffers depends on the view type.
bool isGameView = viewID > 0 ;
int n = isGameView ? 3 : 1 ;
this . viewID = viewID ;
this . lightingRTEX = new RenderTexture [ n ] ;
this . lightingRTID = new RenderTargetIdentifier [ n ] ;
for ( int i = 0 ; i < n ; i + + )
{
this . lightingRTEX [ i ] = new RenderTexture ( w , h , 0 , RenderTextureFormat . ARGBHalf , RenderTextureReadWrite . Linear ) ;
this . lightingRTEX [ i ] . filterMode = FilterMode . Trilinear ; // Custom
this . lightingRTEX [ i ] . dimension = TextureDimension . Tex3D ; // TODO: request the thick 3D tiling layout
this . lightingRTEX [ i ] . volumeDepth = d ;
this . lightingRTEX [ i ] . enableRandomWrite = true ;
this . lightingRTEX [ i ] . Create ( ) ;
this . lightingRTID [ i ] = new RenderTargetIdentifier ( this . lightingRTEX [ i ] ) ;
}
else
public void Destroy ( )
return 0 ;
if ( this . lightingRTEX ! = null )
{
for ( int i = 0 , n = this . lightingRTEX . Length ; i < n ; i + + )
{
this . lightingRTEX [ i ] . Release ( ) ;
}
}
this . viewID = - 1 ;
this . lightingRTEX = null ;
this . lightingRTID = null ;
} // class VBuffer
public VolumetricLightingPreset preset { get { return ( VolumetricLightingPreset ) Math . Min ( ShaderConfig . s_VolumetricLightingPreset , ( int ) VolumetricLightingPreset . Count ) ; } }
ComputeShader m_VolumetricLightingCS = null ;
List < VBuffer > m_VBuffers = 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
public void Build ( HDRenderPipelineAsset asset )
{
if ( preset = = VolumetricLightingPreset . Off ) return ;
m_VolumetricLightingCS = asset . renderPipelineResources . volumetricLightingCS ;
m_VBuffers = new List < VBuffer > ( 1 ) ;
VBuffer FindVBuffer ( int viewID )
public void Cleanup ( )
Debug . Assert ( viewID > = 0 ) ;
if ( preset = = VolumetricLightingPreset . Off ) return ;
VBuffer vBuffer = null ;
m_VolumetricLightingCS = null ;
if ( m_VBuffers ! = null )
for ( int i = 0 , n = m_VBuffers . Count ; i < n ; i + + )
int n = m_VBuffers . Count ;
for ( int i = 0 ; i < n ; i + + )
{
if ( viewID = = m_VBuffers [ i ] . viewID )
{
vBuffer = m_VBuffers [ i ] ;
}
}
m_VBuffers [ i ] . Destroy ( ) ;
return vBuffer ;
m_VBuffers = null ;
void ResizeVBuffer ( int viewID , int screenWidth , int screenHeight )
public void ResizeVBuffer ( HDCamera camera , int screenWidth , int screenHeight )
int viewID = camera . GetViewID ( ) ;
if ( preset = = VolumetricLightingPreset . Off ) return ;
ComputeVBufferResolutionAndScale ( screenWidth , screenHeight , ref w , ref h , ref d ) ;
ComputeVBufferResolutionAndScale ( preset , screenWidth , screenHeight , ref w , ref h , ref d ) ;
VBuffer vBuffer = FindVBuffer ( viewID ) ;
return ;
}
}
// Otherwise, we have to (re)create the VBuffer.
CreateVBuffer ( viewID , w , h , d , vBuffer ) ;
}
void CreateVBuffer ( int viewID , int w , int h , int d , VBuffer vBuffer )
{
Debug . Assert ( viewID > = 0 ) ;
if ( vBuffer ! = null )
{
// Clean up first.
DestroyVBuffer ( vBuffer ) ;
}
// Grow the array.
// Not found - grow the array.
if ( m_VBuffers = = null )
{
m_VBuffers = new List < VBuffer > ( 1 ) ;
}
bool isGameView = viewID > 0 ;
int numBuffers = isGameView ? 3 : 1 ;
vBuffer . viewID = viewID ;
vBuffer . lightingRTEX = new RenderTexture [ numBuffers ] ;
vBuffer . lightingRTID = new RenderTargetIdentifier [ numBuffers ] ;
for ( int i = 0 ; i < numBuffers ; i + + )
{
vBuffer . lightingRTEX [ i ] = new RenderTexture ( w , h , 0 , RenderTextureFormat . ARGBHalf , RenderTextureReadWrite . Linear ) ;
vBuffer . lightingRTEX [ i ] . filterMode = FilterMode . Trilinear ; // Custom
vBuffer . lightingRTEX [ i ] . dimension = TextureDimension . Tex3D ; // TODO: request the thick 3D tiling layout
vBuffer . lightingRTEX [ i ] . volumeDepth = d ;
vBuffer . lightingRTEX [ i ] . enableRandomWrite = true ;
vBuffer . lightingRTEX [ i ] . Create ( ) ;
vBuffer . lightingRTID [ i ] = new RenderTargetIdentifier ( vBuffer . lightingRTEX [ i ] ) ;
}
vBuffer . Create ( viewID , w , h , d ) ;
void DestroyVBuffer ( VBuffer vBuffer )
VBuffer FindVBuffer ( int viewID )
if ( vBuffer = = null ) return ;
Debug . Assert ( viewID > = 0 ) ;
VBuffer vBuffer = null ;
if ( vBuffer . lightingRTEX ! = null )
if ( m_VBuffers ! = null )
int n = vBuffer . lightingRTEX . Length ;
int n = m_VBuffers . Count ;
vBuffer . lightingRTEX [ i ] . Release ( ) ;
if ( viewID = = m_VBuffers [ i ] . viewID )
{
vBuffer = m_VBuffers [ i ] ;
}
vBuffer . viewID = - 1 ;
vBuffer . lightingRTEX = null ;
vBuffer . lightingRTID = null ;
return vBuffer ;
public static int ComputeVBufferTileSize ( VolumetricLightingPreset preset )
static int ComputeVBufferTileSize ( VolumetricLightingPreset preset )
{
switch ( preset )
{
}
}
public static int ComputeVBufferSliceCount ( VolumetricLightingPreset preset )
static int ComputeVBufferSliceCount ( VolumetricLightingPreset preset )
{
switch ( preset )
{
// 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.
Vector2 ComputeVBufferResolutionAndScale ( float screenWidth , float screenHeight ,
ref int w , ref int h , ref int d )
static Vector2 ComputeVBufferResolutionAndScale ( VolumetricLightingPreset preset ,
int screenWidth , int screenHeight ,
ref int w , ref int h , ref int d )
int t = ComputeVBufferTileSize ( m_VolumetricLightingPreset ) ;
int t = ComputeVBufferTileSize ( preset ) ;
w = ( ( int ) screenWidth + t - 1 ) / t ;
h = ( ( int ) screenHeight + t - 1 ) / t ;
d = ComputeVBufferSliceCount ( m_VolumetricLightingPreset ) ;
w = ( screenWidth + t - 1 ) / t ;
h = ( screenHeight + t - 1 ) / t ;
d = ComputeVBufferSliceCount ( preset ) ;
return new Vector2 ( screenWidth / ( w * t ) , screenHeight / ( h * t ) ) ;
return new Vector2 ( ( float ) screenWidth / ( float ) ( w * t ) , ( float ) screenHeight / ( float ) ( h * t ) ) ;
public static Vector4 ComputeLogarithmicDepthEncodingParams ( float nearPlane , float farPlane )
static Vector4 ComputeLogarithmicDepthEncodingParams ( float nearPlane , float farPlane )
{
Vector4 depthParams = new Vector4 ( ) ;
return depthParams ;
}
// Returns NULL if a global fog component does not exist, or is not enabled.
public static HomogeneousFog GetGlobalFogComponent ( )
public void PushGlobalParams ( HDCamera camera , CommandBuffer cmd )
HomogeneousFog globalFogComponent = null ;
if ( preset = = VolumetricLightingPreset . Off ) return ;
HomogeneousFog [ ] fogComponents = Object . FindObjectsOfType ( typeof ( HomogeneousFog ) ) as HomogeneousFog [ ] ;
foreach ( HomogeneousFog fogComponent in fogComponents )
{
if ( fogComponent . enabled & & fogComponent . volumeParameters . IsVolumeUnbounded ( ) )
{
globalFogComponent = fogComponent ;
break ;
}
}
return globalFogComponent ;
}
public void SetVolumetricLightingData ( HDCamera camera , CommandBuffer cmd )
{
HomogeneousFog globalFogComponent = GetGlobalFogComponent ( ) ;
HomogeneousFog globalFogComponent = HomogeneousFog . GetGlobalFogComponent ( ) ;
// TODO: may want to cache these results somewhere.
VolumeProperties globalFogProperties = ( globalFogComponent ! = null ) ? globalFogComponent . volumeParameters . GetProperties ( )
cmd . SetGlobalFloat ( HDShaderIDs . _GlobalFog_Extinction , globalFogProperties . extinction ) ;
cmd . SetGlobalFloat ( HDShaderIDs . _GlobalFog_Asymmetry , globalFogComponent ! = null ? globalFogComponent . volumeParameters . asymmetry : 0 ) ;
Vector2 scale = ComputeVBufferResolutionAndScale ( camera . screenSize . x , camera . screenSize . y , ref w , ref h , ref d ) ;
Vector2 scale = ComputeVBufferResolutionAndScale ( preset , ( int ) camera . screenSize . x , ( int ) camera . screenSize . y , ref w , ref h , ref d ) ;
VBuffer vBuffer = FindVBuffer ( GetViewID ( camera ) ) ;
VBuffer vBuffer = FindVBuffer ( camera . GetViewID ( ) ) ;
Debug . Assert ( vBuffer ! = null ) ;
cmd . SetGlobalVector ( HDShaderIDs . _VBufferResolution , new Vector4 ( w , h , 1.0f / w , 1.0f / h ) ) ;
// The returned {x, y} coordinates (and all spheres) are all within the (-0.5, 0.5)^2 range.
// The pattern has been rotated by 15 degrees to maximize the resolution along X and Y:
// https://www.desmos.com/calculator/kcpfvltz7c
Vector2 [ ] GetHexagonalClosePackedSpheres7 ( )
static Vector2 [ ] GetHexagonalClosePackedSpheres7 ( )
{
Vector2 [ ] coords = new Vector2 [ 7 ] ;
return coords ;
}
void VolumetricLightingPass ( HDCamera camera , CommandBuffer cmd )
public void VolumetricLightingPass ( HDCamera camera , CommandBuffer cmd , FrameSettings frameSettings )
if ( m_VolumetricLightingP reset = = VolumetricLightingPreset . Off ) return ;
if ( p reset = = VolumetricLightingPreset . Off ) return ;
VBuffer vBuffer = FindVBuffer ( GetViewID ( camera ) ) ;
VBuffer vBuffer = FindVBuffer ( camera . GetViewID ( ) ) ;
if ( GetGlobalFogComponent ( ) = = null )
if ( HomogeneousFog . GetGlobalFogComponent ( ) = = null )
{
// Clear the render target instead of running the shader.
// CoreUtils.SetRenderTarget(cmd, GetVBufferLightingIntegral(viewOffset), ClearFlag.Color, CoreUtils.clearColorAllBlack);
// Use the workaround by running the full shader with no volume.
}
bool enableClustered = m_F rameSettings. lightLoopSettings . enableTileAndCluster ;
bool enableClustered = f rameSettings. lightLoopSettings . enableTileAndCluster ;
bool enableReprojection = Application . isPlaying & & camera . camera . cameraType = = CameraType . Game ;
int kernel ;
}
int w = 0 , h = 0 , d = 0 ;
Vector2 scale = ComputeVBufferResolutionAndScale ( camera . screenSize . x , camera . screenSize . y , ref w , ref h , ref d ) ;
Vector2 scale = ComputeVBufferResolutionAndScale ( preset , ( int ) camera . screenSize . x , ( int ) camera . screenSize . y , ref w , ref h , ref d ) ;
float vFoV = camera . camera . fieldOfView * Mathf . Deg2Rad ;
// Compose the matrix which allows us to compute the world space view direction.
cmd . DispatchCompute ( m_VolumetricLightingCS , kernel , ( w + 1 5 ) / 1 6 , ( h + 1 5 ) / 1 6 , 1 ) ;
}
}
} // class HDRenderPipelin e
} // class VolumetricLightingModul e
} // namespace UnityEngine.Experimental.Rendering.HDPipeline