0
votes

I have created a basic Math Library for an assignment, involving a Matrix3 and Matrix4 class, alongside some others. For OpenGL, to project a cube onto the screen, I have created a lookAt function and a Perspective function for the Projection * View * Model algorithm that yields a proper view for the scene. They are designed to be replacements for the corresponding functions in the GLM library/other libraries, since they have to interface with my Matrix classes in the future.

The lookAt function yields correct results, the Projection function is yielding close results, with some temporary manual tweaking to get it to provide the same results. However, when I multiply them together with the Projection * View (lookat) * Model (an Identity matrix), my library yield completely different results to GLM's (the MVP Matrix4). Mine match up with a graphics calculator, the GLM one doesn't. The GLM one actually displays the cube on the screen correctly, my one looks as if the cube has imploded. The Matrices themselves have minimal to no similarity.

GLM Library

        // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
    glm::mat4 ProjectionGLM = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
    // Camera matrix
    glm::mat4 ViewGLM = glm::lookAt(
        glm::vec3(4, 3, 3), // Camera is at (4,3,3), in World Space
        glm::vec3(0, 0, 0), // and looks at the origin
        glm::vec3(0, 1, 0)  // Head is up (set to 0,-1,0 to look upside-down)
        );
    // Model matrix : an identity matrix (model will be at the origin)
    glm::mat4 ModelGLM = glm::mat4(1.0f);  // Changes for each model !
                                        // Our ModelViewProjection : multiplication of our 3 matrices
    glm::mat4 MVP = ProjectionGLM * ViewGLM * ModelGLM; // Remember, matrix multiplication is the other way around
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

Output Matricies (MVP Matrix4)

Custom Library

    Vector3 vectornew1(4, 3, 3);
    Vector3 vectornew2(0, 0, 0); 
    Vector3 vectornew3(0, 1, 0);

    // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
    Matrix4 Projection = Matrix4::Perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
    Projection = Matrix4::Transpose(Projection);
    // Camera matrix
    Matrix4 View = Matrix4::lookAt(
        vectornew1, // Camera is at (4,3,3), in World Space
        vectornew2, // and looks at the origin
        vectornew3  // Head is up (set to 0,-1,0 to look upside-down)
        );
    // Model matrix : an identity matrix (model will be at the origin)
    Matrix4 Model = Matrix4::Identity();  // Changes for each model !
                                        // Our ModelViewProjection : multiplication of our 3 matrices
    Matrix4 MVP = Projection * View * Model; // Remember, matrix multiplication is the other way around
    glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP.data[0][0]);

Custom Matrix4 Multiplication code:

temp.data[0][0] = this->data[0][0] * Other.data[0][0] + this->data[0][1] * Other.data[1][0] + this->data[0][2] * Other.data[2][0] + this->data[0][3] * Other.data[3][0];
temp.data[0][1] = this->data[0][0] * Other.data[0][1] + this->data[0][1] * Other.data[1][1] + this->data[0][2] * Other.data[2][1] + this->data[0][3] * Other.data[3][1];
temp.data[0][2] = this->data[0][0] * Other.data[0][2] + this->data[0][1] * Other.data[1][2] + this->data[0][2] * Other.data[2][2] + this->data[0][3] * Other.data[3][2];
temp.data[0][3] = this->data[0][0] * Other.data[0][3] + this->data[0][1] * Other.data[1][3] + this->data[0][2] * Other.data[2][3] + this->data[0][3] * Other.data[3][3];

temp.data[1][0] = this->data[1][0] * Other.data[0][0] + this->data[1][1] * Other.data[1][0] + this->data[1][2] * Other.data[2][0] + this->data[1][3] * Other.data[3][0];
temp.data[1][1] = this->data[1][0] * Other.data[0][1] + this->data[1][1] * Other.data[1][1] + this->data[1][2] * Other.data[2][1] + this->data[1][3] * Other.data[3][1];
temp.data[1][2] = this->data[1][0] * Other.data[0][2] + this->data[1][1] * Other.data[1][2] + this->data[1][2] * Other.data[2][2] + this->data[1][3] * Other.data[3][2];
temp.data[1][3] = this->data[1][0] * Other.data[0][3] + this->data[1][1] * Other.data[1][3] + this->data[1][2] * Other.data[2][3] + this->data[1][3] * Other.data[3][3];

temp.data[2][0] = this->data[2][0] * Other.data[0][0] + this->data[2][1] * Other.data[1][0] + this->data[2][2] * Other.data[2][0] + this->data[2][3] * Other.data[3][0];
temp.data[2][1] = this->data[2][0] * Other.data[0][1] + this->data[2][1] * Other.data[1][1] + this->data[2][2] * Other.data[2][1] + this->data[2][3] * Other.data[3][1];
temp.data[2][2] = this->data[2][0] * Other.data[0][2] + this->data[2][1] * Other.data[1][2] + this->data[2][2] * Other.data[2][2] + this->data[2][3] * Other.data[3][2];
temp.data[2][3] = this->data[2][0] * Other.data[0][3] + this->data[2][1] * Other.data[1][3] + this->data[2][2] * Other.data[2][3] + this->data[2][3] * Other.data[3][3];

temp.data[3][0] = this->data[3][0] * Other.data[0][0] + this->data[3][1] * Other.data[1][0] + this->data[3][2] * Other.data[2][0] + this->data[3][3] * Other.data[3][0];
temp.data[3][1] = this->data[3][0] * Other.data[0][1] + this->data[3][1] * Other.data[1][1] + this->data[3][2] * Other.data[2][1] + this->data[3][3] * Other.data[3][1];
temp.data[3][2] = this->data[3][0] * Other.data[0][2] + this->data[3][1] * Other.data[1][2] + this->data[3][2] * Other.data[2][2] + this->data[3][3] * Other.data[3][2];
temp.data[3][3] = this->data[3][0] * Other.data[0][3] + this->data[3][1] * Other.data[1][3] + this->data[3][2] * Other.data[2][3] + this->data[3][3] * Other.data[3][3];

What could GLM be doing with the Matrix multiplication to yield vastly different results? My method matched up with graphics calculators and yields correct results with identity matrices.

1
Check whether the matrices are transposed in any of the calculations. Also, check in which order the matrix multiplications are performed.Daerst
vastly different results What results? I don't see them anywhereBorgleader
This is minor but in several places you wrote GEM, the name of the library is GLMBorgleader
Thankyou for that, don't know why GEM is stuck in my head. I checked to see if Transposing the custom MVP didn't help produce close results, but swapping View and Projection did cause the matrices to become very similar, except for some minor differences. The cube isn't rendering for my matrix, but that is likely unrelated. I can't believe I missed that. I'll check why the cube isn't rendering, but that basically solved the core math problem I was having. Thankyou :)user3906336
The GLM source is available for comparison. Are your matrices in column-major order?molbdnilo

1 Answers

0
votes

I tried your multiply in my engine (also based on glm, or at least tested against it) and the results were correct when I swapped over this and Other. So this should be correct:

temp.data[0][0] = Other.data[0][0] * this->data[0][0] + Other.data[0][1] * this->data[1][0] + Other.data[0][2] * this->data[2][0] + Other.data[0][3] * this->data[3][0];
temp.data[0][1] = Other.data[0][0] * this->data[0][1] + Other.data[0][1] * this->data[1][1] + Other.data[0][2] * this->data[2][1] + Other.data[0][3] * this->data[3][1];
temp.data[0][2] = Other.data[0][0] * this->data[0][2] + Other.data[0][1] * this->data[1][2] + Other.data[0][2] * this->data[2][2] + Other.data[0][3] * this->data[3][2];
temp.data[0][3] = Other.data[0][0] * this->data[0][3] + Other.data[0][1] * this->data[1][3] + Other.data[0][2] * this->data[2][3] + Other.data[0][3] * this->data[3][3];
temp.data[1][0] = Other.data[1][0] * this->data[0][0] + Other.data[1][1] * this->data[1][0] + Other.data[1][2] * this->data[2][0] + Other.data[1][3] * this->data[3][0];
temp.data[1][1] = Other.data[1][0] * this->data[0][1] + Other.data[1][1] * this->data[1][1] + Other.data[1][2] * this->data[2][1] + Other.data[1][3] * this->data[3][1];
temp.data[1][2] = Other.data[1][0] * this->data[0][2] + Other.data[1][1] * this->data[1][2] + Other.data[1][2] * this->data[2][2] + Other.data[1][3] * this->data[3][2];
temp.data[1][3] = Other.data[1][0] * this->data[0][3] + Other.data[1][1] * this->data[1][3] + Other.data[1][2] * this->data[2][3] + Other.data[1][3] * this->data[3][3];
temp.data[2][0] = Other.data[2][0] * this->data[0][0] + Other.data[2][1] * this->data[1][0] + Other.data[2][2] * this->data[2][0] + Other.data[2][3] * this->data[3][0];
temp.data[2][1] = Other.data[2][0] * this->data[0][1] + Other.data[2][1] * this->data[1][1] + Other.data[2][2] * this->data[2][1] + Other.data[2][3] * this->data[3][1];
temp.data[2][2] = Other.data[2][0] * this->data[0][2] + Other.data[2][1] * this->data[1][2] + Other.data[2][2] * this->data[2][2] + Other.data[2][3] * this->data[3][2];
temp.data[2][3] = Other.data[2][0] * this->data[0][3] + Other.data[2][1] * this->data[1][3] + Other.data[2][2] * this->data[2][3] + Other.data[2][3] * this->data[3][3];
temp.data[3][0] = Other.data[3][0] * this->data[0][0] + Other.data[3][1] * this->data[1][0] + Other.data[3][2] * this->data[2][0] + Other.data[3][3] * this->data[3][0];
temp.data[3][1] = Other.data[3][0] * this->data[0][1] + Other.data[3][1] * this->data[1][1] + Other.data[3][2] * this->data[2][1] + Other.data[3][3] * this->data[3][1];
temp.data[3][2] = Other.data[3][0] * this->data[0][2] + Other.data[3][1] * this->data[1][2] + Other.data[3][2] * this->data[2][2] + Other.data[3][3] * this->data[3][2];
temp.data[3][3] = Other.data[3][0] * this->data[0][3] + Other.data[3][1] * this->data[1][3] + Other.data[3][2] * this->data[2][3] + Other.data[3][3] * this->data[3][3];