Irrlicht's buildProjectionMatrixPerspectiveRH calculation

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
galf
Posts: 10
Joined: Sun Nov 08, 2015 8:24 am

Irrlicht's buildProjectionMatrixPerspectiveRH calculation

Post by galf »

Hi,

Environment: windows 7 64 bit, c++ visual studio 2012, no clr, opengl driver.

This is perhaps a stupid question but It's not so clear to me.

here is an explanation with all the calculation for how to implement perspective projection in opengl:
http://www.songho.ca/opengl/gl_projectionmatrix.html

As part of my work I implemented the projection matrix
as songho suggested (I need to do so cause I use irrlicht inside another framework).

I set my camera node to my new projection matrix and... surprise.... all my models are gone.

A quick look at irrlicht implementation revealed my problem:

Code: Select all

 
// Builds a right-handed perspective projection matrix based on a field of view
template <class T>
inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFovRH(
            f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar)
{
        const f64 h = reciprocal(tan(fieldOfViewRadians*0.5));
        _IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero
        const T w = static_cast<T>(h / aspectRatio);
 
        _IRR_DEBUG_BREAK_IF(zNear==zFar); //divide by zero
        M[0] = w;
        M[1] = 0;
        M[2] = 0;
        M[3] = 0;
 
        M[4] = 0;
        M[5] = (T)h;
        M[6] = 0;
        M[7] = 0;
 
        M[8] = 0;
        M[9] = 0;
        M[10] = (T)(zFar/(zNear-zFar)); // DirectX version
//      M[10] = (T)(zFar+zNear/(zNear-zFar)); // OpenGL version
        M[11] = -1;
 
        M[12] = 0;
        M[13] = 0;
        M[14] = (T)(zNear*zFar/(zNear-zFar)); // DirectX version
//      M[14] = (T)(2.0f*zNear*zFar/(zNear-zFar)); // OpenGL version
        M[15] = 0;
 
#if defined ( USE_MATRIX_TEST )
        definitelyIdentityMatrix=false;
#endif
        return *this;
}
 
I used the opengl version and irrlicht use diretcx version.
I'm using the opengl driver so I assumed the settings suppose to be in opengl....

So the link above explains to me why opengl projection works, what I don't understand is why this projection works?
(is there a directX equivelent to songho explenation?)

Thanks
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Irrlicht's buildProjectionMatrixPerspectiveRH calculatio

Post by mongoose7 »

OpenGL (and Direct3D?) requires column-major matrices. So Irrlicht uses the transposes of the row-major matrices. If you transpose your projection matrix, does it work?
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: Irrlicht's buildProjectionMatrixPerspectiveRH calculatio

Post by Mel »

DX requires row major matrices (bitches couldn't use an unified matrix model...) The major drawback, though, can be the multiplication order, as actually reversing a matrix multiplication order, can be interpreted as multiplying by the transpose of the matrix in the forward order, so it may come down to a multiplication order.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
galf
Posts: 10
Joined: Sun Nov 08, 2015 8:24 am

Re: Irrlicht's buildProjectionMatrixPerspectiveRH calculatio

Post by galf »

Hi,

Thanks for the replies.
Actually the first thing that I tried (with the opengl version)
was to use the transpose matrix which did not work.

My current problem is I don't know why things works...

I found a projection that does work, but I don't understand why .
Here is the projection matrix that works (the parameters is the same as the input of glFrustum,
meaning the input is: Left ,Right,Bottom,Top,Near,Far of the frustum.
In the code they represented as frusL,frusR,frusB,frusT,frusN,frusF ):

Code: Select all

 
matrixProjection = cv::Matx44f::eye();
 
float fn_dt =frusF-frusN;
 
 // check denominators are not zero
if ((frusT ==0) || (frusR ==0) || (fn_dt ==0))
  return;
 
matrixProjection.val[0]  = frusN / frusR;
matrixProjection.val[5]  = frusN / frusT;
matrixProjection.val[10] = (frusF+frusN) / fn_dt;
matrixProjection.val[11] = (frusF *frusN) / fn_dt;
matrixProjection.val[14] = -1;
matrixProjection.val[15] = 0;
 
 


(Yes it's LH, I know. it still does not explain it...)

I noticed That The difference between the two standards (DX and OpenGL) seems
to be more then row-major vs column-major metrices.
In buildProjectionMatrixPerspectiveFovRH code, for M[14] calculation you don't
multiply by 2 in DX version while in openGL you do (and there is a mathematical explanation for it (for opengl)
in here : http://www.songho.ca/opengl/gl_projectionmatrix.html ).

Is there a mathematical reason for my projection to work or the fact it works is just pure luck?
(The matrix I used is from songho (the final result of the Perspective Projection explenation) exept from the multiplication by 2 )

buildProjectionMatrixPerspectiveFovRH code:

Code: Select all

 
// Builds a right-handed perspective projection matrix based on a field of view
template <class T>
inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFovRH(
            f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar)
{
        const f64 h = reciprocal(tan(fieldOfViewRadians*0.5));
        _IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero
        const T w = static_cast<T>(h / aspectRatio);
 
        _IRR_DEBUG_BREAK_IF(zNear==zFar); //divide by zero
        M[0] = w;
        M[1] = 0;
        M[2] = 0;
        M[3] = 0;
 
        M[4] = 0;
        M[5] = (T)h;
        M[6] = 0;
        M[7] = 0;
 
        M[8] = 0;
        M[9] = 0;
        M[10] = (T)(zFar/(zNear-zFar)); // DirectX version
//      M[10] = (T)(zFar+zNear/(zNear-zFar)); // OpenGL version
        M[11] = -1;
 
        M[12] = 0;
        M[13] = 0;
        M[14] = (T)(zNear*zFar/(zNear-zFar)); // DirectX version
//      M[14] = (T)(2.0f*zNear*zFar/(zNear-zFar)); // OpenGL version
        M[15] = 0;
 
#if defined ( USE_MATRIX_TEST )
        definitelyIdentityMatrix=false;
#endif
        return *this;
}
 
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Irrlicht's buildProjectionMatrixPerspectiveRH calculatio

Post by mongoose7 »

Well, the projection matrix appears to be mapping Z to -Z for some reason. But taking that as a given, you can see that
z = -near --> z = -1 (after division by w, in the usual way)
z = -far --> z = 1
The OpenGL projection matrix maps the frustum into the cube (-1, -1, -1)-(1, 1, 1). I'm not going to work through the details, but there is a question in my mind about whether z = near should go to -1 or to 0. Maybe this is the difference between OpenGL and Direct3D? Anyway, keep in mind that the calculation isn't complete until you divide the coordinates by the new value of W.
BTW You know you can do a desk check? Just see where various vertices end up after multiplying by the projection matrix, like (left, top, near), for example.
galf
Posts: 10
Joined: Sun Nov 08, 2015 8:24 am

Re: Irrlicht's buildProjectionMatrixPerspectiveRH calculatio

Post by galf »

Hi,

Thanks for the replies.
I found a way to take my framework's
settings (that configured in opengl) and inject it
directly to irrlicht (as is).

Here is my solution:

Code: Select all

 
 
// R= rotation matrix T=Translation, M= ModelView matrix
cv::Matx33f R_ = R.t();
M(0,0)=modelScale*R_(0,0);
M(1,0)=R_(1,0);
M(2,0)=R_(2,0);
M(3,0)=T(0);
M(0,1)=R_(0,1);
M(1,1)=modelScale*R_(1,1);
M(2,1)=R_(2,1);
M(3,1)=T(1);
M(0,2)=-R_(0,2);
M(1,2)=-R_(1,2);
M(2,2)=-modelScale*R_(2,2);
M(3,2)=-T(2);
M(3,3)=1.0f;
 
// mTrans is my implementation to IDummyTransformationSceneNode
// that enables me to inject transformation to the node tree (By RelativeTransformationMatrix) - 
// I insert it as a parent node to the node I want to transform
mTrans->SetGLpose(M);
 
float mat[16];
glGetFloatv(GL_PROJECTION_MATRIX, mat);
irr::core::matrix4 proj;    
proj.setM(mat);
 
// mCamera is irrlicht's camera node
this->mCamera->setProjectionMatrix(proj);
 
// continue to draw
 
Hope it helps.

Thanks again
Post Reply