[Serializable]
public class VolumeParameters
{
public Bounds bounds ; // Position and dimensions in meters
public bool isLocal ; // Enables voxelization
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
bounds = new Bounds ( Vector3 . zero , Vector3 . positiveInfinity ) ;
isLocal = true ;
public bool IsVolumeUnbounded ( )
public bool IsLocalVolume ( )
return bounds . size . x = = float . PositiveInfinity & &
bounds . size . y = = float . PositiveInfinity & &
bounds . size . z = = float . PositiveInfinity ;
return isLocal ;
}
public Vector3 GetAbsorptionCoefficient ( )
public void Constrain ( )
{
bounds . size = Vector3 . Max ( bounds . size , Vector3 . zero ) ;
albedo . r = Mathf . Clamp01 ( albedo . r ) ;
albedo . g = Mathf . Clamp01 ( albedo . g ) ;
albedo . b = Mathf . Clamp01 ( albedo . b ) ;
public long viewID = - 1 ; // -1 is invalid; positive for Game Views, 0 otherwise
public RenderTexture [ ] lightingRTEX = null ;
public RenderTargetIdentifier [ ] lightingRTID = null ;
public RenderTexture densityRTEX = null ;
public RenderTargetIdentifier densityRTID = - 1 ; // RenderTargetIdentifier cannot be NULL
public RenderTargetIdentifier GetLightingIntegralBuffer ( ) // Of the current frame
{
{
Debug . Assert ( viewID > 0 ) ; // Game View only
return lightingRTID [ 1 + ( ( Time . renderedFrameCount + 1 ) & 1 ) ] ;
}
public RenderTargetIdentifier GetDensityBuffer ( )
{
Debug . Assert ( viewID > = 0 ) ;
return densityRTID ;
}
public void Create ( long viewID , int w , int h , int 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 ( ) ) ;
}
struct OrientedBBox
{
// TODO: in the shader, merge axes and extents, and 16-byte align the data structure.
Vector3 center ;
Vector3 right , up , forward ; // X, Y, Z, normalized
Vector3 extents ; // Size of the half-diagonal
public static OrientedBBox Create ( Transform t )
{
return Create ( t , Vector3 . zero ) ;
}
public static OrientedBBox Create ( Transform t , Vector3 offset )
{
OrientedBBox obb = new OrientedBBox ( ) ;
obb . center = t . position + offset ;
obb . right = t . right ;
obb . up = t . up ;
obb . forward = t . forward ;
obb . extents = t . localScale * 0.5f ;
return obb ;
}
// Returns 'true' if the OBB intersects (or is inside) the frustum, 'false' otherwise.
public bool OverlapsFrustum ( Plane [ ] frustumPlanes , int numPlanes ,
Vector3 [ ] frustumCorners , int numCorners )
{
bool overlap = true ;
// Test the OBB against frustum planes. Frustum planes have inward-facing.
// The OBB is outside if it's entirely behind one of the frustum planes.
// See "Real-Time Rendering", 3rd Edition, 16.10.2.
for ( int i = 0 ; overlap & & ( i < numPlanes ) ; i + + )
{
Vector3 n = frustumPlanes [ i ] . normal ;
float d = frustumPlanes [ i ] . distance ;
// Max projection of the half-diagonal onto the normal (always positive).
float maxHalfDiagProj = extents . x * Mathf . Abs ( Vector3 . Dot ( n , right ) )
+ extents . y * Mathf . Abs ( Vector3 . Dot ( n , up ) )
+ extents . z * Mathf . Abs ( Vector3 . Dot ( n , forward ) ) ;
// Negative distance -> center behind the plane (outside).
float centerToPlaneDist = Vector3 . Dot ( n , center ) + d ;
// outside = maxHalfDiagProj < -centerToPlaneDist
// outside = maxHalfDiagProj + centerToPlaneDist < 0
// overlap = overlap && !outside
overlap = overlap & & ( maxHalfDiagProj + centerToPlaneDist > = 0 ) ;
}
if ( numCorners = = 0 ) return overlap ;
// Test the frustum corners against OBB planes. The OBB planes are outward-facing.
// The frustum is outside if all of its corners are entirely in front of one of the OBB planes.
// See "Correct Frustum Culling" by Inigo Quilez.
// We can exploit the symmetry of the box by only testing against 3 planes rather than 6.
Plane [ ] planes = new Plane [ 3 ] ;
planes [ 0 ] . normal = right ;
planes [ 0 ] . distance = extents . x ;
planes [ 1 ] . normal = up ;
planes [ 1 ] . distance = extents . y ;
planes [ 2 ] . normal = forward ;
planes [ 2 ] . distance = extents . z ;
for ( int i = 0 ; overlap & & ( i < 3 ) ; i + + )
{
Plane plane = planes [ i ] ;
// We need a separate counter for the "box fully inside frustum" case.
bool outsidePos = true ; // Positive normal
bool outsideNeg = true ; // Reversed normal
// Merge 2 loops. Continue as long as all points are outside either plane.
for ( int j = 0 ; ( outsidePos | | outsideNeg ) & & ( j < numCorners ) ; j + + )
{
float proj = Vector3 . Dot ( plane . normal , frustumCorners [ j ] - center ) ;
outsidePos = outsidePos & & ( proj > plane . distance ) ;
outsideNeg = outsideNeg & & ( - proj > plane . distance ) ;
}
overlap = overlap & & ! ( outsidePos | | outsideNeg ) ;
}
return overlap ;
}
}
public void VoxelizeDensityVolumes ( HDCamera camera , CommandBuffer cmd )
{
if ( camera . camera . cameraType ! = CameraType . SceneView ) return ;
Vector3 camPosition = camera . camera . transform . position ;
Vector3 camOffset = Vector3 . zero ; // World-origin-relative
if ( ShaderConfig . s_CameraRelativeRendering ! = 0 )
{
camOffset = - camPosition ; // Camera-relative
}
HomogeneousFog [ ] fogComponents = Object . FindObjectsOfType ( typeof ( HomogeneousFog ) ) as HomogeneousFog [ ] ;
foreach ( HomogeneousFog fogComponent in fogComponents )
{
// Only test active finite volumes.
if ( fogComponent . enabled & & fogComponent . volumeParameters . IsLocalVolume ( ) )
{
// Convert to the camera-relative coordinates if necessary.
// TODO: cache these?
var obb = OrientedBBox . Create ( fogComponent . transform , camOffset ) ;
// Frustum cull on the CPU for now. TODO: do it on the GPU.
if ( ! obb . OverlapsFrustum ( camera . frustumPlanes , 6 , camera . frustumCorners , 8 ) )
{
Debug . Log ( "Culled." ) ;
}
}
}
}
// Ref: https://en.wikipedia.org/wiki/Close-packing_of_equal_spheres