0
\$\begingroup\$

Let's say that I have standard left-handed WorldToView and ViewToScreen row-major matrices in the same vein as the DirectX D3DXMatrixPerspectiveLH functions, like so:

EXVector vView = NormalizeVector(LookAt() - m_Position);

float fDotProduct = DotProduct(m_WorldUp, vView);

EXVector vUp = NormalizeVector(m_WorldUp - fDotProduct * vView);

// Generate x basis vector (crossproduct of y&z basis vectors)
EXVector vRight = CrossProduct(vUp, vView);

// Generate the matrix
row0.Set(vRight.x, vUp.x, vView.x, 0);
row1.Set(vRight.y, vUp.y, vView.y, 0);
row2.Set(vRight.z, vUp.z, vView.z, 0);
row3.Set(-DotProduct(m_Position, vRight),
         -DotProduct(m_Position, vUp),
         -DotProduct(m_Position, vView), 1);

EXMatrix mWorldToView(row0, row1, row2, row3);

Here's the left-handed mViewToScreen / projection matrix:

float zNear  =    0.001f;
float zFar   = 1000.000f;
float Aspect = DisplayHeight() / DisplayWidth();

float sinfov, cosfov; qsincosf(VFOV() / 2, sinfov, cosfov);
float cotfov = cosfov / sinfov; // cos(n) / sin(n) = 1/tan(n)

float w = Aspect  * (cotfov);
float h =   1.0f  * (cotfov);
float Q = zFar / (zFar - zNear);
float R = -Q * zNear;

row0.Set(w, 0, 0, 0);
row1.Set(0, h, 0, 0);
row2.Set(0, 0, Q, 1);
row3.Set(0, 0, R, 0);

EXMatrix mViewToScreen(row0, row1, row2, row3);

Many things like map/object culling depend on this fact:

// View to cull matrix
Q = (       zFar + zNear) / (zFar - zNear);
R = (2.0f * zFar * zNear) / (zFar - zNear);

row0.Set(w, 0, 0, 0);
row1.Set(0, h, 0, 0);
row2.Set(0, 0, Q, 1);
row3.Set(0, 0, R, 0);

EXMatrix mViewToCull(row0, row1, row2, row3);

Does anyone know how to derive the infamous "reverse depth with infinite far plane" from the original blog post for mViewToScreen and mViewToCull? Every single reference I have found (nlguillemot and nVidia, for example) is in the right-handed space.

Normally I am able to figure things out visually, but 4D matrices are a headache.

\$\endgroup\$
1
\$\begingroup\$

Well, I feel stupid now. Turns out I had to use a positive 1 in the last member of the third row instead of the -1 displayed in the right-handed examples, that fixes it. The best thing being that I don't even have to touch the cull matrix.

This sign flip was one of the first things I tried, but didn't work because the solution was mixed with other problems. I have just found it again by accident while reverting my changes and after testing every other combination you can imagine:

row0.Set(w, 0,     0,  0);
row1.Set(0, h,     0,  0);
row2.Set(0, 0,     0,  1 /* -1 */);
row3.Set(0, 0, zNear,  0);

EXMatrix mViewToScreen(row0, row1, row2, row3);

And, of course, remember to depth-test your fragments with GL_GREATER (or GL_GEQUAL) instead of GL_LESS (or GL_LEQUAL) and clearing depth to 0.0f, instead of 1.0f (which is the default value for the horizon). That's all the same for everyone.

Pair that with a floating point buffer and you should have improved depth precision and no far clipping.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.