Oblique Near-Plane Clipping & reversed depth buffer

gjaegy

Newcomer
Hi,

I am trying to modify my projection matrix in order to perform near-plane clipping (http://www.terathon.com/code/oblique.html).

However, in one of my project I use the reversed depth buffer trick (similar to what Humus explains here : http://www.humus.name/index.php?page=Comments&ID=255&start=0).

Now, basically, without any modification, the trick doesn't work.

So, I have tried to modify to technique, mainly, I tried to modify the clip-space corner calculation, and replaced (I am using the D3D variant):

Code:
v = (sgn(vPlaneEq.x), sgn(vPlaneEq.y), 1.0, 1.0);
with
Code:
v = (sgn(vPlaneEq.x), sgn(vPlaneEq.y), 0.0, 1.0);

However, this doesn't properly work, the depth values seem to be inverted a second time (while the oblique frustum modification should produce this I think).

Has anybody already tried to mix these both techniques ? I can't really figure out what's wrong...
 
Hmm, I think I have it.

Still have to test, but at least it is looking good visually. Need to check the depth distribution though.


Code:
void ObliqueFrustumCulling( imPlaneTemplate<T>& _oClipPlane, imBool _bReversed )
{
	imSVector4DTemplate<T> vPlaneEq;
	_oClipPlane.GetEquation(vPlaneEq);

	// Calculate the clip-space corner point opposite the clipping plane
	// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
	// transform it into camera space by multiplying it
	// by the inverse of the projection matrix
	imSVector4DTemplate<T> q;
	q.x = (sgn(vPlaneEq.x) - m_Values[2][0]) / m_Values[0][0];
	q.y = (sgn(vPlaneEq.y) - m_Values[2][1]) / m_Values[1][1];
	q.z = 1.0f;
	q.w = (1.0f - m_Values[2][2]) / m_Values[3][2];

	// Calculate the scaled plane vector
	imSVector4DTemplate<T> c = vPlaneEq * (1.0f / imSVector4DTemplate<T>::DotProduct(vPlaneEq, q));

	// HERE ARE  THE CHANGES
	if (_bReversedZBuffer)
	{
		m_Values[0][2] = -c.x;
		m_Values[1][2] = -c.y;
		m_Values[2][2] = -c.z;
		m_Values[3][2] = c.w;
	}
	else
	{
		m_Values[0][2] = c.x;
		m_Values[1][2] = c.y;
		m_Values[2][2] = c.z;
		m_Values[3][2] = c.w;
	}
};
 
Humus suggested me a much simpler, and hence much better, solution. Thanks a lot man :)

Basically, he suggested to apply the oblique near-plane clipping on the regular projection matrix (i.e. without any depth flip).

Depth flipping is applied after that, by multiplying the projection matrix by a simple Z flip matrix (z = w - z as I am using Direct3D).

Code:
imSMatrix4D matProjReversedZ, matFlipZ;
matFlipZ.SetIdentity();
matFlipZ.m_33 = -1.0f;
matFlipZ.m_43 = 1.0f;

imSMatrix4D::Multiply(*m_pMatrixProjection, matFlipZ, *m_pMatrixProjectionForShader);

So easy, so cheap, so less error prone.

And my whole pipeline is much simpler now, as I am not tracking both projection and reversed depth projection matrices anymore; I am applying the depth flip to the projection matrix just before feeding the shader. Much easier than building the projection matrix by swapping zfar/znear, which causes many side effects at the engine level, and add confusion to the developer as soon as he has to use the projection matrix.

This was the kind of day where you realize you are somewhat stupid :) Don't worry, I feel better today !
 
Asking here because this seems to be the only thread referencing reversed depth.

I'm using a 32F reversed depth buffer, with a GE test, but I'm not getting early-z rejection, at least on Kepler. It works in the same app if I use a non-reversed buffer. I'm measuring shaded pixels using nsight.

Just wondering if you or anyone else encountered this, or have any insight into the early-z conditions for different hardware.

Cheers
 
hmmm, interesting. I haven't checked this to be honest, I would be interested by an answer as well.
Does that means that greater or equal breaks the early-z rejection, is that what you are saying ?
 
I made a synthetic test that shows it's working at least in some cases w/ GreaterEqual.

The case where it's failing in our game is drawing a bunch of cards w/ alpha discard after depth pre-pass. Same shaders and everything get early-z with non-reversed depth.

I'll update when I figure it out.
 
OK, so I was losing hi-z when I hit some an internal maximum for unique depth configurations.

It required me to use GE testing for the main scene, and also switch RTs during rendering to make a copy of the colour buffer. If I disabled either of those it would come back.

An unused depth buffer was mistakenly being bound in place of the main one when doing the copy pass, and binding NULL instead seems to get everything working properly.

I guess the moral of the story is that sanity checking early-z / hi-z isn't a bad idea.
 
Back
Top