Search code examples
copenglmatrixperspectivecamera

OpenGL MVP matrix miscalculation


I've been following some OpenGL tutorials and my MVP matrix calculations don't seem to be working like they should. I suspect I've made an error somewhere in one of my functions, but I cannot seem to pinpoint it. Help would be appreciated, as I am still a beginner in the realm of 3D matrix transformations.

The functions I am using as part of my MVP calculations:

float ToRadians(float Degrees)
{
    float Result = Degrees * (PI32 / 180.0f);

    return (Result);
}

mat4 Rotate(float Angle, vec3 Axis)
{
    // Creates an identity matrix
    mat4 Result = Mat4d(1.0f);

    float SinTheta = sinf(ToRadians(Angle));
    float CosTheta = cosf(ToRadians(Angle));

    Result.Elements[0][0] = (Axis.X * Axis.X * (1.0f - CosTheta)) + CosTheta;
    Result.Elements[0][1] = (Axis.X * Axis.Y * (1.0f - CosTheta)) - (Axis.Z * SinTheta);
    Result.Elements[0][2] = (Axis.X * Axis.Z * (1.0f - CosTheta)) + (Axis.Y * SinTheta);

    Result.Elements[1][0] = (Axis.Y * Axis.X * (1.0f - CosTheta)) + (Axis.Z * SinTheta);
    Result.Elements[1][1] = (Axis.Y * Axis.Y * (1.0f - CosTheta)) + CosTheta;
    Result.Elements[1][2] = (Axis.Y * Axis.Z * (1.0f - CosTheta)) - (Axis.X * SinTheta);

    Result.Elements[2][0] = (Axis.Z * Axis.X * (1.0f - CosTheta)) - (Axis.Y * SinTheta);
    Result.Elements[2][1] = (Axis.Z * Axis.Y * (1.0f - CosTheta)) + (Axis.X * SinTheta);
    Result.Elements[2][2] = (Axis.Z * Axis.Z * (1.0f - CosTheta)) + CosTheta;

    return (Result);
}

mat4 Translate(vec3 Translation)
{
    mat4 Result = Mat4d(1.0f);

    Result.Elements[3][0] = Translation.X;
    Result.Elements[3][1] = Translation.Y;
    Result.Elements[3][2] = Translation.Z;

    return (Result);
}

mat4 Perspective(float FOV, float AspectRatio, float Near, float Far)
{
    mat4 Result = Mat4d(1.0f);

    float TanThetaOver2 = tanf(FOV * (PI32 / 360.0f));

    Result.Elements[0][0] = 1.0f / TanThetaOver2;
    Result.Elements[1][1] = AspectRatio / TanThetaOver2;
    Result.Elements[2][3] = -1.0f;
    Result.Elements[2][2] = (Near + Far) / (Near - Far);
    Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far);
    Result.Elements[3][3] = 0.0f;

    return (Result);
}

The relevant sections of my main body:

//  Model matrix
mat4 MatModel = Rotate(-45.0f, Vec3(1.0f, 0.0f, 0.0f));
//  View matrix
mat4 MatView = Translate(Vec3(0.0f, 0.0f, -3.0f));
//  Projection matrix
mat4 MatProjection = Perspective(45.0f,
                                 (GLfloat) 1024 / (GLfloat) 768,
                                 0.1f, 100.0f);

GLuint LocModel = glGetUniformLocation(ShaderProgramID, "Model");
GLuint LocView = glGetUniformLocation(ShaderProgramID, "View");
GLuint LocProjection = glGetUniformLocation(ShaderProgramID, "Projection");

glUniformMatrix4fv(LocModel, 1, GL_TRUE, (GLfloat*) MatModel.Elements);
glUniformMatrix4fv(LocView, 1, GL_TRUE, (GLfloat*) MatView.Elements);
glUniformMatrix4fv(LocProjection, 1, GL_TRUE, (GLfloat*) MatProjection.Elements);

//  Bind VAO and draw from EBO
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);

This is my vertex shader:

#version 330 core

layout (location = 0) in vec3 Position;
layout (location = 1) in vec2 CTexCoord;

out vec2 STexCoord;

uniform mat4 Model;
uniform mat4 View;
uniform mat4 Projection;

void main()
{
    gl_Position = Projection * View * Model * vec4(Position, 1.0f);
    STexCoord = CTexCoord;
}

In the shader, without the Projection and View matrices, the rotation seems to go fine, but things look weird when the View matrix is added. With the addition of the Projection matrix, nothing gets rendered.

Images for comparison:

Model / rotation matrix only applied

Model / rotation and View / translation matrices applied (does this look normal?)

With a full MVP matrix applied, nothing shows except the background. (Sorry, cannot post more than 2 links right now...)

Thanks in advance! :)


Solution

  • Your call to glUniformMatrix sets transposed to GL_TRUE meaning that the matrices are row-major. That means that OpenGL expects the elements in each row of the matrix to be adjacent in memory. You are setting the translation elements in the view matrix like this:

    Result.Elements[3][0] = Translation.X;
    Result.Elements[3][1] = Translation.Y;
    Result.Elements[3][2] = Translation.Z;
    

    Since you are telling OpenGL that your matrix is row-major, that means that the first subscript in Elements is row and the last subscript in Elements is column. This means you are setting column 1 to 3 in the 4th column to the translation. This will affect the w component of gl_Position and you get weirdness. I can see you are doing the same mistake for your projection matrix. Based on this I assume you made the mat4 to be column-major. If this is the case I suggest changing the transposed argument in glUniformMatrix to GL_FALSE. If not you should swap row/column in the translate and projection building as such:

    mat4 Translate(vec3 Translation)
    {
       mat4 Result = Mat4d(1.0f);
    
       Result.Elements[0][3] = Translation.X;
       Result.Elements[1][3] = Translation.Y;
       Result.Elements[2][3] = Translation.Z;
    
       return (Result);
    }