Bottom Row of 4x4 Matrix

Started by
16 comments, last by caibbor 11 years, 4 months ago
I am using Octave for this, and I can recommend it. I attached a zip file to this post that contains 4 simple macros to help you experiment with matrices. It helped me to quickly experiment and gain understanding.
rot: Create a rotation matrix.
scale(x,y,z): Create a scaling matrix
transl(x,y,z): Create a translation matrix
Shearz: Create a shear matrix in z.

Examples:
octave:3> scale(1,2)
ans =
1 0 0 0
0 2 0 0
0 0 1 0
0 0 0 1
octave:4> v=[1;2;3;1]
v =
1
2
3
1
octave:6> scale(1,2)*v
ans =
1
4
3
1
octave:7> rot(pi/2)
ans =
1.00000 0.00000 0.00000 0.00000
0.00000 0.00000 -1.00000 0.00000
-0.00000 1.00000 0.00000 0.00000
0.00000 0.00000 0.00000 1.00000
octave:8> rot(pi/2)*v
ans =
1
-3
2
1
octave:9> inverse(rot(pi/2))
ans =
1.00000 0.00000 -0.00000 -0.00000
0.00000 0.00000 1.00000 -0.00000
0.00000 -1.00000 0.00000 -0.00000
0.00000 0.00000 0.00000 1.00000
octave:11> T=transl(1);
octave:19> S=scale(2);
octave:20> S*T*v
ans =
4
2
3
1
octave:21> T*S*v
ans =
3
2
3
1
[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/
Advertisement
thank you, larspensjo. I was delighted to find octave available for my linux distro. but how about projection/perspective matrices?

I presume the way to invert the perspective section of a matrix is to just negate the values, just the same as negating the translation parts. is this correct?

thank you, larspensjo. I was delighted to find octave available for my linux distro. but how about projection/perspective matrices?

I presume the way to invert the perspective section of a matrix is to just negate the values, just the same as negating the translation parts. is this correct?

You cannot invert the individual sections of a matrix just like that. It just happens to work for translation under very specific circumstances. Likewise, you can invert other sections under other specific conditions.

If you have a matrix consisting of the perspective division component only, then you can invert it by negating the bottom row just like you do with translation. But if you have other components of the projection matrix, and a usual projection matrix does have both translation and non-uniform scaling, then you cannot do that anymore.

But take a look at how OpenGL's matrices are commonly designed here in appendix F. It lists their inverse also.

But take a look at how OpenGL's matrices are commonly designed here in appendix F. It lists their inverse also.


I tried implementing my own fucntions to generate a matrix based on those equations, as well as an inverse:
Image23.gif

Multiplying a matrix together with it's own inverse should result in an identity matrix. I'm getting mostly identity, except col 3 row 4 is always -5.9, and col 4 row 3 is always -1.2. I've tripple-checked that my matrices match the ones in the image.

I think the problem is in my matrix multiplication. I know how to multiply the 3x3 rotation section of the matrix and then add the translation, but what do I do with the perspective section?

Edit: I've been googling and even looked into a few CG programming books. nobody seems to say how to handle the bottom row of a GL 4x4 when multiplying two matrices together...
You have two 4x4 matrices so you have to multiply them as 4x4 matrices. The bottom row is just like any other row in the matrix, there is nothing special about it at all.
alright, then riddle me this.

(all in column-major order)

The projection matrix I created is:
0.074978 0.000000 0.000000 0.000000 0.000000 0.099970 0.000000 0.000000 0.000000 0.000000 -1.000100 -1.000000 0.000000 0.000000 -0.200010 0.000000

It's inverse I calculated is:
13.337285 0.000000 0.000000 0.000000 0.000000 10.002963 0.000000 0.000000 0.000000 0.000000 0.000000 -4.999750 0.000000 0.000000 -1.000000 20001000.000000

an multiplying those matrices together should be identy, but instead I get:
1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 -20000994.000000 1.000000

All code related to this is shown below. I've been up and down and all the methods, as far as I can tell, look correct.
[source lang="cpp"]// Mat4f class is a 4x4 matrix of floats.
// Mat4f::data is a float[16], and is column-major

// returns float& intentionally, method is private
inline float& Mat4f::M( const int col, const int row ) {
assert( col < dim && row < dim && col >= 0 && row >= 0);
return data[row+col*4];
}

void Mat4f::Multiply( const Mat4f& mat, Mat4f& out ) const {
assert ( &mat != &out );
int r,c;

for ( c=0; c<4; c++ ) {
for ( r=0; r<4; r++ ) {
out.M(c,r) =
M(r,0) * mat.M(0,c) +
M(r,1) * mat.M(1,c) +
M(r,2) * mat.M(2,c) +
M(r,3) * mat.M(3,c);
}
}
}

// calculation from: http://www.glprogramming.com/red/images/Image23.gif
void my_glFrustum( float l, float r, float b, float t, float n, float f, Mat4f& out ) {
float n2 = n * 2.0f;
out.data[0]=n2/(r-l);
out.data[1]=0.0f;
out.data[2]=0.0f;
out.data[3]=0.0f;

out.data[4]=0.0f;
out.data[5]=n2/(t-b);
out.data[6]=0.0f;
out.data[7]=0.0f;

out.data[8]=(r+l)/(r-l);
out.data[9]=(t+b)/(t-b);
out.data[10]=-(f+n)/(f-n);
out.data[11]=-1.0f;

out.data[12]=0.0f;
out.data[13]=0.0f;
out.data[14]=-(n2*f)/(f-n);
out.data[15]=0.0f;
}

// calculation from: http://www.glprogramming.com/red/images/Image23.gif
void my_glFrustum_inv( float l, float r, float b, float t, float n, float f, Mat4f& out ) {
float n2 = n * 2.0f;
out.data[0]=(r-l)/n2;
out.data[1]=0.0f;
out.data[2]=0.0f;
out.data[3]=0.0f;

out.data[4]=0.0f;
out.data[5]=(t-b)/n2;
out.data[6]=0.0f;
out.data[7]=0.0f;

out.data[8]=0.0f;
out.data[9]=0.0f;
out.data[10]=0.0f;
out.data[11]=-(f-n)/(n2*f);

out.data[12]=(r+l)/n2;
out.data[13]=(t+b)/n2;
out.data[14]=-1.0f;
out.data[15]=(f+n)/n2*f;
}

void my_gluPerspective( float fov, float x,float y,float near,float far, Mat4f& out, bool invert = false );
void my_gluPerspective( float fov, float x,float y,float near,float far, Mat4f& out, bool invert ) {
// inspired by http://www.cse.unsw....e/frustsrc.html
float aspect = x / y;
float t = 1.0f/tanf(fov * 3.141f / 360.0f);
if ( invert ) {
my_glFrustum_inv(-t * aspect, t * aspect, -t, t, near, far, out);
} else {
my_glFrustum(-t * aspect, t * aspect, -t, t, near, far, out);
}
}

inline void my_gluPerspective_inv( float fov, float x,float y,float near,float far, Mat4f& out ) {
my_gluPerspective( fov, x, y, near, far, out, true );
}

int main ( void ) {
Mat4f should_be_identity;
Mat4f projection;
Mat4f projection_inverse;

my_gluPerspective(90,800,600,0.1f,2000,projection);
my_gluPerspective_inv(90,800,600,0.1f,2000,projection_inverse);

projection.Multiply(projection_inverse, should_be_identity);
for ( int i=0; i<16; i++ )
printf("%f ", should_be_identity.data );

/*
expected output:
1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000

actual output:
1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 -20000994.000000 1.000000
*/
return 0;
}
[/source]
Your element 15 of the inverse is incorrect. The denominator is missing a parenthesis to ensure that the multiplication by f doesn't end up in the numerator.
"I always do that. I always mess up some mundane detail." - Michael Bolton, Office Space

thanks. :P

This topic is closed to new replies.

Advertisement