niagara
niagara copied to clipboard
Fauly occlusion with camera
Hello Zeux ! I've been dealing with a bug in the "fast/compact" sphere projection

vec4 ProjectedSphereToAABB( vec3 viewSpaceCenter, float r, float projWidth, float projHeight )
{
vec2 cXZ = viewSpaceCenter.xz;
vec2 vXZ = vec2( sqrt( dot( cXZ, cXZ ) - r * r ), r );
vec2 minX = mat2( vXZ.x, vXZ.y, -vXZ.y, vXZ.x ) * cXZ;
vec2 maxX = mat2( vXZ.x, -vXZ.y, vXZ.y, vXZ.x ) * cXZ;
vec2 cYZ =viewSpaceCenter.yz;
vec2 vYZ = vec2( sqrt( dot( cYZ, cYZ ) - r * r ), r );
vec2 minY = mat2( vYZ.x, vYZ.y, -vYZ.y, vYZ.x ) * cYZ;
vec2 maxY = mat2( vYZ.x, -vYZ.y, vYZ.y, vYZ.x ) * cYZ;
// NOTE: quick and dirty projection
vec4 aabb = vec4( ( minX.x / minX.y ) * projWidth,
( minY.x / minY.y ) * projHeight,
( maxX.x / maxX.y ) * projWidth,
( maxY.x / maxY.y ) * projHeight );
// NOTE: from NDC to texture UV space
aabb = aabb.xwzy * vec4( 0.5, -0.5, 0.5, -0.5 ) + vec4( 0.5 );
return aabb;
}
Basically , I was getting occlusion culling on "the object in question" but only in a "Bermuda Tiangle" region, and it shouldn't have happen because it was visible. The issue is produced by the quick and dirty projection, namely it lacks the 1/zNear as a factor:
vec4 aabb = vec4( ( minX.x / minX.y ) * projWidth / zNear,
( minY.x / minY.y ) * projHeight/ zNear,
( maxX.x / maxX.y ) * projWidth / zNear,
( maxY.x / maxY.y ) * projHeight / zNear );
Took me a while to figure out :) Cheers !
In your renderer, how do you define projWidth? Typically znear shouldn't affect the projection results, but if you define projWidth/projHeight as the view-space bounds of the camera slice at znear (so the size of the front clipped plane of the viewing frustum), then yeah - you'd need to divide.
I prefer the formulation where projection width/height define the size of the image plane at Z=1; this definition doesn't depend on znear, which means you can vary znear to adjust for clipping without changing the projection math.
inline DirectX::XMMATRIX PerspInvDepthInfFovLH( float fovYRads, float aspectRatioWH, float zNear )
{
float sinFov;
float cosFov;
DirectX::XMScalarSinCos( &sinFov, &cosFov, fovYRads * 0.5f );
float h = cosFov / sinFov;
float w = h / aspectRatioWH;
DirectX::XMMATRIX proj;
proj.r[ 0 ] = DirectX::XMVectorSet( w, 0, 0, 0 );
proj.r[ 1 ] = DirectX::XMVectorSet( 0, h, 0, 0 );
proj.r[ 2 ] = DirectX::XMVectorSet( 0, 0, 0, zNear );
proj.r[ 3 ] = DirectX::XMVectorSet( 0, 0, 1, 0 );
return proj;
}
projWidth would be w, projHeight would be h From the nvidia paper I figured it out. They explicitly multiply the extreme points on the sphere by proj and it would result in 1/znear as being a scaling coef.
I'm not sure I know how to toggle between Z=1 formulation and what I currently have.
if( visible && LATE_PASS ){
vec3 viewSpaceCenter = ( globs.view * vec4( center.x, center.y, center.z, 1.0f ) ).xyz;
if( viewSpaceCenter.z > radius + cullInfo.zNear ){
vec4 aabb = ProjectedSphereToAABB( viewSpaceCenter, radius,
cullInfo.projWidth / cullInfo.zNear,
cullInfo.projHeight / cullInfo.zNear );
......
and ProjectedSphereToAABB is identical to what niagara does.