niagara icon indicating copy to clipboard operation
niagara copied to clipboard

Fauly occlusion with camera

Open Eichenherz opened this issue 4 years ago • 2 comments
trafficstars

Hello Zeux ! I've been dealing with a bug in the "fast/compact" sphere projection doc1 doc2

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 !

Eichenherz avatar Feb 18 '21 17:02 Eichenherz

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.

zeux avatar Mar 01 '21 04:03 zeux

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.

Eichenherz avatar Mar 03 '21 17:03 Eichenherz