Matrix problems

Started by
1 comment, last by MichaelDeCicco 11 years, 12 months ago
I am putting together a little math library for a larger project, and I'm having trouble with my matrix math. This isn't the first time I've had this problem. The last time I had this problem I just used the GLM library instead of writing it all myself though, but now I think it's important for me to understand it well enough to do it myself.

I've made some regular old Vector2,3, and 4 classes to use as rows in my Matrix2,3, and 4 classes. My matrix classes are row major. My main concern right now is the Matrix4 class. Since all the Vector classes are pretty standard, I don't think it's necessary to put them here. Here's my Matrix4 class:


template <typename ValueType>
class Matrix4
{
public:
Matrix4()
{
Identity(1.0f);
}
Matrix4(const Matrix4 <ValueType>&Mat)
{
*this = Mat;
}
~Matrix4()
{
}

void DebugPrint() const
{
for(int i = 0;i < 4;i++)System::USystem::GetSystem()->Log(Ungine::System::LOG_MESSAGE,"%f\t%f\t%f\t%f\n",Rows[0],Rows[1],Rows[2],Rows[3]);
}

Matrix4 <ValueType>Transpose() const
{
Matrix4 <ValueType>Ret;

Ret[0][0] = Rows[0][0]; Ret[0][1] = Rows[1][0]; Ret[0][2] = Rows[2][0]; Ret[0][3] = Rows[3][0];
Ret[1][0] = Rows[0][1]; Ret[1][1] = Rows[1][1]; Ret[1][2] = Rows[2][1]; Ret[1][3] = Rows[3][1];
Ret[2][0] = Rows[0][2]; Ret[2][1] = Rows[1][2]; Ret[2][2] = Rows[2][2]; Ret[2][3] = Rows[3][2];
Ret[3][0] = Rows[0][3]; Ret[3][1] = Rows[1][3]; Ret[3][2] = Rows[2][3]; Ret[3][3] = Rows[3][3];

return Ret;
}
void operator =(const Matrix4 <ValueType>&Mat)
{
for(int i = 0;i < 4;i++) for(int j = 0;j < 4;j++) Rows[j] = Mat[j];
}
Matrix4 <ValueType>operator *(const Matrix4 <ValueType>&Mat) const
{
Matrix4 Ret;

Ret[0][0] = Rows[0].DotProduct(Mat.GetColumn(0));
Ret[0][1] = Rows[0].DotProduct(Mat.GetColumn(1));
Ret[0][2] = Rows[0].DotProduct(Mat.GetColumn(2));
Ret[0][3] = Rows[0].DotProduct(Mat.GetColumn(3));

Ret[1][0] = Rows[1].DotProduct(Mat.GetColumn(0));
Ret[1][1] = Rows[1].DotProduct(Mat.GetColumn(1));
Ret[1][2] = Rows[1].DotProduct(Mat.GetColumn(2));
Ret[1][3] = Rows[1].DotProduct(Mat.GetColumn(3));

Ret[2][0] = Rows[2].DotProduct(Mat.GetColumn(0));
Ret[2][1] = Rows[2].DotProduct(Mat.GetColumn(1));
Ret[2][2] = Rows[2].DotProduct(Mat.GetColumn(2));
Ret[2][3] = Rows[2].DotProduct(Mat.GetColumn(3));

Ret[3][0] = Rows[3].DotProduct(Mat.GetColumn(0));
Ret[3][1] = Rows[3].DotProduct(Mat.GetColumn(1));
Ret[3][2] = Rows[3].DotProduct(Mat.GetColumn(2));
Ret[3][3] = Rows[3].DotProduct(Mat.GetColumn(3));

return Ret;
}
Matrix4 <ValueType>operator *=(const Matrix4 <ValueType>&Mat)
{
*this = *this * Mat;
return *this;
}
Vector4 <ValueType>operator *(const Vector4<ValueType>&Vec) const
{
Vector4 <ValueType>Ret;

Ret.x = Rows[0].DotProduct(Vec);
Ret.y = Rows[1].DotProduct(Vec);
Ret.z = Rows[2].DotProduct(Vec);
Ret.w = Rows[3].DotProduct(Vec);

return Ret;
}
Vector3 <ValueType>operator *(const Vector3<ValueType>&Vec) const
{
Vector3 <ValueType>Ret;

Ret.x = Rows[0].DotProduct(Vec);
Ret.y = Rows[1].DotProduct(Vec);
Ret.z = Rows[2].DotProduct(Vec);

return Ret;
}

void Identity(ValueType id = 1.0f)
{
Rows[0] = Vector4 <ValueType>(id,0.0f,0.0f,0.0f);
Rows[1] = Vector4 <ValueType>(0.0f,id,0.0f,0.0f);
Rows[2] = Vector4 <ValueType>(0.0f,0.0f,id,0.0f);
Rows[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,id);
}
void Translate(const Vector3 <ValueType>&Trans)
{
Matrix4 <ValueType> Mat;

Mat[0] = Vector4 <ValueType>(1.0f,0.0f,0.0f,Trans.x);
Mat[1] = Vector4 <ValueType>(0.0f,1.0f,0.0f,Trans.y);
Mat[2] = Vector4 <ValueType>(0.0f,0.0f,1.0f,Trans.z);
Mat[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,1.0f );

*this = *this * Mat;
}
void Rotate(const Vector3 <ValueType>&Axis,ValueType Angle)
{
Matrix4 Mat;

ValueType hAngle = Angle * 0.5f;

ValueType SinAngle = Sin(hAngle);
ValueType CosAngle = Cos(hAngle);

ValueType w = CosAngle;
ValueType x = Axis.x * SinAngle;
ValueType y = Axis.y * SinAngle;
ValueType z = Axis.z * SinAngle;

ValueType xSq = x * x;
ValueType ySq = y * y;
ValueType zSq = z * z;
ValueType xy = x * y;
ValueType xz = x * z;
ValueType xw = x * w;
ValueType yw = y * w;
ValueType zw = z * w;
ValueType zy = z * y;

Mat[0][0] = 1.0f - 2.0f * (ySq + zSq);
Mat[0][1] = 2.0f * (xy + zw);
Mat[0][2] = 2.0f * (xz - yw);
Mat[0][3] = 0.0f;
Mat[1][0] = 2.0f * (xy - zw);
Mat[1][1] = 1.0f - 2.0f * (xSq + zSq);
Mat[1][2] = 2.0f * (zy + xw);
Mat[1][3] = 0.0f;
Mat[2][0] = 2.0f * (xz + yw);
Mat[2][1] = 2.0f * (zy - xw);
Mat[2][2] = 1.0f - 2.0f * (xSq + ySq);
Mat[2][3] = 0.0f;

Mat[3][0] = 0.0f;
Mat[3][1] = 0.0f;
Mat[3][2] = 0.0f;
Mat[3][3] = 1.0f;

*this = *this * Mat;
}
void Scale(ValueType Scale)
{
Matrix4 <ValueType> Mat;

Mat[0] = Vector4 <ValueType>(Scale,0.0f,0.0f,0.0f);
Mat[1] = Vector4 <ValueType>(0.0f,Scale,0.0f,0.0f);
Mat[2] = Vector4 <ValueType>(0.0f,0.0f,Scale,0.0f);
Mat[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,Scale);

*this = *this * Mat;
}
void Scale(const Vector4 <ValueType> &Scale)
{
Matrix4 <ValueType> Mat;

Mat[0] = Vector4 <ValueType>(Scale.x,0.0f,0.0f,0.0f);
Mat[1] = Vector4 <ValueType>(0.0f,Scale.y,0.0f,0.0f);
Mat[2] = Vector4 <ValueType>(0.0f,0.0f,Scale.z,0.0f);
Mat[3] = Vector4 <ValueType>(0.0f,0.0f,0.0f,Scale.w);

*this = *this * Mat;
}
ValueType Determinant() const
{
abort();
}
bool Inverse(Matrix4 <ValueType>*Mat)
{
abort();
return false;
}

ValueType *Ptr()
{
return &Rows[0].x;
}
Vector4 <ValueType>GetColumn(Index Idx) const
{
return Vector4 <ValueType>(Rows[0][Idx],Rows[1][Idx],Rows[2][Idx],Rows[3][Idx]);
}
Vector4 <ValueType>&operator[](Index Idx)
{
return Rows[Idx];
}
Vector4 <ValueType>operator[](Index Idx) const
{
return Rows[Idx];
}
protected:
Vector4 <ValueType>Rows[4];
};


I realized I had a problems when I was trying to use this class to build a MVP matrix for a camera. No matter how I do it, what order I multiply in, whether or not the end MVP matrix or the individual matrices are transposed, what I see on the screen is just a very stretched out polygon. I am attempting to render a simple square at 0,0,0,0 with a perspective and orthographic projection.

Here's part of the render function where I am testing the matrix class:

System::USystem::GetSystem()->SetAngleType(Ungine::System::AT_DEGREE);

Matrix4f Model, View, Projection, MVP;
static f32 Val = 0.0f;
Val += 0.01f;
//Model.Translate(Vector3f(768 / 2,1024 / 2,0));
//Model.Rotate(Vector3f(0,1,1).Normalize(),Val / 2.0f);
//Model.Scale(Vector4f((2 + Math::Sin(Val)) * 0.5f,(2 + Math::Sin(Val)) * 0.5f,(2 + Math::Sin(Val)) * 0.5f,1.0f));//;Rotate(Vector3f(0,0,1),Val);
View = LookAt(Vector3f(0,0,-20.0f),Vector3f(0,0,0),Vector3f(0,1,0));

Projection = Perspectivef(50.0f,768.0f / 1024.0f,0.1f,1000.0f);
//Projection = Orthographicf(0.0f,768.0f,0.0f,1024.0f,0.0f,1000.0f);

MVP = Projection * View * Model;

glUniformMatrix4fv(MVP_Loc,1,false,MVP.Ptr());



And here are the projection and LookAt functions:

Matrix4f Perspectivef(f32 FieldOfView,f32 AspectRatio,f32 Near,f32 Far)
{
Matrix4f Mat;

f32 TanFov = Tan(FieldOfView * 0.5f);

f32 a = 1.0f / (AspectRatio * TanFov);
f32 b = 1.0f / TanFov;
f32 c = Far / (Far - Near);
f32 d = -(2.0f * Near * Far) / (Far - Near);

Mat[0] = Vector4f(a , 0.0f , 0.0f , 0.0f);
Mat[1] = Vector4f(0.0f , b , 0.0f , 0.0f);
Mat[2] = Vector4f(0.0f , 0.0f , c , d );
Mat[3] = Vector4f(0.0f , 0.0f , 1.0f , 0.0f);

return Mat;
}
Matrix4f Orthographicf(f32 Left,f32 Right,f32 Top,f32 Bottom,f32 Near,f32 Far)
{
Matrix4f Mat;

f32 dW = Left - Right;
f32 dH = Top - Bottom;
f32 dL = Far - Near;

f32 Tx = (Right + Left) / (Right - Left);
f32 Ty = (Far + Near) / (Far - Near);
f32 Tz = (Top + Bottom) / (Top - Bottom);

Mat[0] = Vector4f(2.0f / (dW),0.0f ,0.0f ,Tx );
Mat[1] = Vector4f(0.0f ,2.0f / (dH),0.0f ,Ty );
Mat[2] = Vector4f(0.0f ,0.0f ,-(2.0f / dL),Tz );
Mat[3] = Vector4f(0.0f ,0.0f ,0.0f ,1.0f);

return Mat;
}
Matrix4f LookAt(const Vector3f &Eye,const Vector3f &Target,const Vector3f &Up)
{
Matrix4f Mat;

Vector3f Forward, Side, _Up;
Forward = (Target - Eye).Normalize();
Side = Forward.CrossProduct(Up).Normalize();
_Up = Side.CrossProduct(Forward).Normalize();
Mat[0] = Vector4f(Side ,0.0f );
Mat[1] = Vector4f(_Up ,0.0f );
Mat[2] = Vector4f(Forward * -1.0f,0.0f );
Mat[3] = Vector4f(0.0f ,0.0f,0.0f,1.0f);
Mat.Translate(Eye * -1.0f);

return Mat;
}


It's a bunch of code to look through, but if anyone has the patience, could you tell me if you see anything that looks wrong?
Matrix4f is just Matrix4 <f32>. Also, sorry about the large amount of whitespace before each line, that's just from the indentation from two namespaces that I didn't include in the code, for some reason they got larger when I submitted this thread.

Thank you for your time!
Advertisement
The fact that the results on the screen don't look right is a very high-level description of the problem. In order to debug your code it is much more useful to create an example with numbers that are simple enough that you can follow the computation by hand, and then step in with a debugger and see where the computations depart from what you expected.
Thank you, I learned that I should be a little more patient and more analytical before asking questions. As it turns out, my math was right but I forgot to divide gl_Position by gl_Position.w in the vertex shader!

How annoying.

This topic is closed to new replies.

Advertisement