Pixelate:Issue 13/Matrix Math Part Two: Projection Matrices

From Allegro Wiki

Jump to: navigation, search
Matrix Math Part Two: Projection Matrices
Original author: Adrian Danis (aka RamboBones)
some mail
Website:
zip:

Contents

Introduction

Well welcome to the second spoon feeding session on matrices. Last time we looked at translation, rotation and scaling matrices, as well as how to multiply two matrices together and multiply a point through a matrix. Today we're going to check out the 4'th column in the matrix as well as projection matrices which use it.

Code Base

For this article I'm going to be using a nearly identical code base as the previous one, so please read it if you haven't done so already. It can be found here. There will however be some new functions in the CMatrix class, these are


CVector4 CMatrix::ApplyMatrixW(const CVector3 &in);
CVector3 CMatrix::ApplyMatrix_W(const CVector3 &in);
void CMatrix::CreateProjectionMatrix(float near,floar far,float aspect,float fovx,float fovy);

We do some tricking naming in that just because C++ won't allow functions with the same name to have different return types, as well as take the same parameters. You can rename them to whatever you want. And of course we'll need our CVector4 class


class CVector4
{
public:
	CVector4(){}
	CVector4(float X,float Y,float Z,float W) : x(X),y(Y),z(Z),w(W){}
	float x,y,z,w;
};

Four Dimensional Vectors

What are four dimensional vectors? A four dimensional vector is one that has a time, or 'W', component attached to it. It might be defined something like this

class CVector4
{
public:
	float x,y,z,w;
};

Very simple. Now, if you remember doing matrices you would've noticed that when we 'multiplied a point through a matrix' we were simply doing matrix multiplication, since the two matrices you multiply can be different shapes, provided the width is the same as the height. HUH?!? I hear you ask, but how is a 3 value wide vector the same height as a 4 value high matrix. Well quite simply because you ignore the bottom row as that is merely translation information, which isn't part of the normal multiplying of a matrix. Okay, your keeping up with me right? That means we have some virtual 3x3 actual matrix and a bottom row of translation values, but what about first 3 values in the far right column? Well guess what... make that virtual matrix 3x3 matrix, a 4x3 matrix. Yes that's right, remember how I said that the width merely neaded to be the same as the height for multiplying two matrices together? Well they are, the only difference is that although we multiply a point of 3 values through, we get 4 values out!

Okay, you can ingore that entire paragraph above with very little worry, although it'd be good if you understood it. Now because multiplying a CVector3 through a matrix to get another CVector3 is nearly identical to the code neaded to get a CVector4 I'll just be giving straight code here, read above paragraph for a real explanation.


OutX=InX*Matrix[0]+InY*Matrix[4]+InZ*Matrix[8]+Matrix[12];
OutY=InX*Matrix[1]+InY*Matrix[5]+InZ*Matrix[9]+Matrix[13];
OutZ=InX*Matrix[2]+InY*Matrix[6]+InZ*Matrix[10]+Matrix[14];
OutW=InX*Matrix[3]+InY*Matrix[7]+InZ*Matrix[11]+Matrix[15];

And of course for the people just reading through this looking for the code, you'll get some


CVector4 CMatrix::ApplyMatrixW(const CVector3 &in)
{
	return CVector4(in.x*MatrixValues[0]+in.y*MatrixValues[4]+in.z*MatrixValues[8]+MatrixValues[12],in.x*MatrixValues[1]+in.y*MatrixValues[5]+in.z*MatrixValues[9]+MatrixValues[13],in.x*MatrixValues[2]+in.y*MatrixValues[6]+in.z*MatrixValues[10]+MatrixValues[14],in.x*MatrixValues[3]+in.y*MatrixValues[7]+in.z*MatrixValues[11]+MatrixValues[15]);
}

Now I'm seeing a shedload of head scratching going on. Just what the hell are you going to do with a four dimensional vector! Well keep your heads on, all you need is a little bit of...

OutX=OutX/OutW;
OutY=OutY/OutW;
OutZ=OutZ/OutW;

And yes this is where our ApplyMatrix_W function comes in, it'll use the W coordinate, but give you back a nice three dimensional vector.


CVector3 CMatrix::ApplyMatrix_W(const CVector3 &in)
{
	CVector4 Out=ApplyMatrixW(in);
	return CVector3(Out.x/Out.w,Out.y/Out.w,Out.z/Out.w);
}

And there you have it, what could be simpler?

Projection Matrix

Okay, as a finish up to this article I'm going to give you a basic projection matrix. If you've been programming in OpenGL or DirectX or other 3D API's you'll be familiar with Frustums, now I'm not going to cover how to create a matrix from a frustum, instead we'll be looking at how to create a matrix from an aspect ratio, near and far planes as well as two field of view values. The matrix is quite simple and is

float ProjectionMatrix[16]={
    (1/tan(FovX/2))/Aspect,      0      ,            0          ,0,
               0          ,1/tan(FovY/2),            0          ,0,
               0          ,      0      , (Near+Far)/(Near-Far) ,-1,
               0          ,      0      ,(2*Near*Far)/(Near-Far),0};

And code

void CMatrix::CreateProjectionMatrix(float near,floar far,float aspect,float fovx,float fovy)
{
	MatrixValues[0]=1/tan(fovx/2)/aspect;
	MatrixValues[1]=0;
	MatrixValues[2]=0;
	MatrixValues[3]=0;

	MatrixValues[4]=0;
	MatrixValues[5]=1/tan(fovy/2);
	MatrixValues[6]=0;
	MatrixValues[7]=0;

	MatrixValues[8]=0;
	MatrixValues[9]=0;
	MatrixValues[10]=(near+far)/(near-far);
	MatrixValues[11]=-1;

	MatrixValues[12]=0;
	MatrixValues[13]=0;
	MatrixValues[14]=(2*near*far)/(near-far);
	MatrixValues[15]=0;
}

Conclusion

And their you have it, everything you probably never wanted to know about matrices. I hope you liked reading the two articles I've written and I most definately hope you find some use for them.

Issue 13: The Diet Issue
Previous:
Thinking Indie
Present:
Matrix Math Part Two: Projection Matrices
Next:
Exception Handling
Personal tools
Adsense