<p>I'm working on a simple software renderer and have a working implementation so far. I'm curious as to why it's actually working since I would expect the multiplication ordering for my world, view and projection matrices to be reversed. When working with DirectX and OpenGL in the past I have multiplied them as projection * view * world, but with my implementation it's working with worldMatrix * viewMatrix * projectionMatrix. Some code:</p>
<pre><code>void SceneRenderer::DrawMesh(Mesh&amp; mesh, const SRGraphicsContext&amp; gfx)
{
    const Mat4x4f&amp; worldMatrix = mesh.GetWorldMatrix();
    const Mat4x4f&amp; viewMatrix = camera-&gt;GetViewMatrix();
    const Mat4x4f&amp; projectionMatrix = camera-&gt;GetProjectionMatrix();
    const Mat4x4f&amp; wvp = worldMatrix * viewMatrix * projectionMatrix;

    const Vert3df&amp; cameraPos = camera-&gt;GetPosition();

    const float screenWidth = static_cast&lt;float&gt;(gfx.frameBuffer-&gt;GetWidth());
    const float screenHeight = static_cast&lt;float&gt;(gfx.frameBuffer-&gt;GetHeight());

    Tri2di rasterTri;
    for (Tri3df&amp; tri : mesh.GetTriangles())
    {
        Vert4df p1c =  Vert4df(tri.p1, 1),
                p2c = Vert4df(tri.p2, 1),
                p3c = Vert4df(tri.p3, 1);

        p1c = wvp * p1c;
        p2c = wvp * p2c;
        p3c = wvp * p3c;
        
        p1c.DivideByW();
        p2c.DivideByW();
        p3c.DivideByW();

        rasterTri.p1.x = static_cast&lt;int&gt;((p1c.x + 1.0f) / 2.0f * screenWidth);
        rasterTri.p1.y = static_cast&lt;int&gt;((p1c.y + 1.0f) / 2.0f * screenHeight);

        rasterTri.p2.x = static_cast&lt;int&gt;((p2c.x + 1.0f) / 2.0f * screenWidth);
        rasterTri.p2.y = static_cast&lt;int&gt;((p2c.y + 1.0f) / 2.0f * screenHeight);

        rasterTri.p3.x = static_cast&lt;int&gt;((p3c.x + 1.0f) / 2.0f * screenWidth);
        rasterTri.p3.y = static_cast&lt;int&gt;((p3c.y + 1.0f) / 2.0f * screenHeight);

        rasterizer-&gt;DrawTriangle(rasterTri, gfx);
    }

void Camera::LookAt(const Vert3df&amp; at)
{
    lookAt = at;

    auto zAxis = (lookAt - position).Normalize();
    auto xAxis = up.Cross(zAxis).Normalize();
    auto yAxis = zAxis.Cross(xAxis);

    viewMatrix.Zero();
    viewMatrix(0, 0) = xAxis.x;
    viewMatrix(0, 1) = yAxis.x;
    viewMatrix(0, 2) = zAxis.x;
    viewMatrix(0, 3) = 0.0f;
    viewMatrix(1, 0) = xAxis.y;
    viewMatrix(1, 1) = yAxis.y;
    viewMatrix(1, 2) = zAxis.y;
    viewMatrix(1, 3) = 0.0f;
    viewMatrix(2, 0) = xAxis.z;
    viewMatrix(2, 1) = yAxis.z;
    viewMatrix(2, 2) = zAxis.z;
    viewMatrix(2, 3) = 0.0f;
    viewMatrix(3, 0) = -xAxis.Dot(position);
    viewMatrix(3, 1) = -yAxis.Dot(position);
    viewMatrix(3, 2) = -zAxis.Dot(position);
    viewMatrix(3, 3) = 1.0f;
}

void Camera::UpdateProjectionMatrix()
{
    const float aspectRatio = screenWidth / (float)screenHeight;

    float tanHalfFOV = std::tan(fov / 2.0f);
    float zRange = nearPlane - farPlane;

    projectionMatrix.Zero();
    projectionMatrix(0, 0) = 1.0f / (tanHalfFOV * aspectRatio);
    projectionMatrix(1, 1) = 1.0f / tanHalfFOV;
    projectionMatrix(2, 2) = (-nearPlane - farPlane) / zRange;
    projectionMatrix(2, 3) = 1; // Left handed, invert for right handed
    projectionMatrix(3, 2) = (2 * nearPlane * farPlane) / zRange;
}
</code></pre>
<p>Any idea why the ordering might be reversed in my case? I've compared my Mat4x4f implementation to DirectX's XMMAtrix and get the same results when multiplying 2 arbitrary matrices.</p>

CC BY-SA 4.0
2020-12-01T10:53:08.143Z
if you can compare check the result of `pvw * p1` in DirectX with the same values. If its same as your `wvp * p1` then your implementation is opposite. If it's not the same, then your implementation might be fine and the problem might be somewhere else I guess.
gallickgunner
CC BY-SA 4.0
2020-12-01T12:55:47.760Z
In matrix mathematics transposing the matrix reverses the calculation order. So $A * B = ( B^T * A^T)^T$
joojaa
CC BY-SA 4.0
2020-12-02T03:22:56.837Z
I saw a mention of that in another thread, but I don't believe I'm transposing the matrices anywhere. Does DirectX or OpenGl implicitly transpose these matrices somewhere?
user13873040
CC BY-SA 4.0
2020-12-04T16:34:22.727Z
You dont have to transpose a matrix to get the effect. All you have to do is build the matrix differently with different assumptions. A column major build order results in a matrix that is seeminly transposed version of a row major matrix. So all that needs to happen is that your not aware of the convention used. Buth are mathematically equivalent and as correct. I mean  you could also have a convention where the translate part is first, or second and not last like is customary. But no one does that. But its entirely calid to use different building rule
joojaa
CC BY-SA 4.0
2020-12-04T16:39:41.977Z
I mean its entirely valid to think from world to object than it is from object to world. Mathematicians prefer inside out because it follows the logic of function composition but you dont have to its natural for humans to think of other objects from outside in.
joojaa
CC BY-SA 4.0
2021-01-08T01:19:36.183Z
I've updated my question to include how I'm building the view/perspective matrices. I've substituted the code with XMMatrixLookAtLH and XMMatrixPerspectiveFovLH, but the implementation is still broken when I multiply as pers * view * world.
user13873040
<transformations><mathematics><projections><matrix>
Model View Projection Matrix Multiplcation Order
user13873040
# Model View Projection Matrix Multiplcation Order

(Asked by: username_0 on 2020-12-01)

I'm working on a simple software renderer and have a working implementation so far. I'm curious as to why it's actually working since I would expect the multiplication ordering for my world, view and projection matrices to be reversed. When working with DirectX and OpenGL in the past I have multiplied them as projection \* view \* world, but with my implementation it's working with worldMatrix \* viewMatrix \* projectionMatrix. Some code:

```
void SceneRenderer::DrawMesh(Mesh& mesh, const SRGraphicsContext& gfx)
{
    const Mat4x4f& worldMatrix = mesh.GetWorldMatrix();
    const Mat4x4f& viewMatrix = camera->GetViewMatrix();
    const Mat4x4f& projectionMatrix = camera->GetProjectionMatrix();
    const Mat4x4f& wvp = worldMatrix * viewMatrix * projectionMatrix;

    const Vert3df& cameraPos = camera->GetPosition();

    const float screenWidth = static_cast<float>(gfx.frameBuffer->GetWidth());
    const float screenHeight = static_cast<float>(gfx.frameBuffer->GetHeight());

    Tri2di rasterTri;
    for (Tri3df& tri : mesh.GetTriangles())
    {
        Vert4df p1c =  Vert4df(tri.p1, 1),
                p2c = Vert4df(tri.p2, 1),
                p3c = Vert4df(tri.p3, 1);

        p1c = wvp * p1c;
        p2c = wvp * p2c;
        p3c = wvp * p3c;
        
        p1c.DivideByW();
        p2c.DivideByW();
        p3c.DivideByW();

        rasterTri.p1.x = static_cast<int>((p1c.x + 1.0f) / 2.0f * screenWidth);
        rasterTri.p1.y = static_cast<int>((p1c.y + 1.0f) / 2.0f * screenHeight);

        rasterTri.p2.x = static_cast<int>((p2c.x + 1.0f) / 2.0f * screenWidth);
        rasterTri.p2.y = static_cast<int>((p2c.y + 1.0f) / 2.0f * screenHeight);

        rasterTri.p3.x = static_cast<int>((p3c.x + 1.0f) / 2.0f * screenWidth);
        rasterTri.p3.y = static_cast<int>((p3c.y + 1.0f) / 2.0f * screenHeight);

        rasterizer->DrawTriangle(rasterTri, gfx);
    }

void Camera::LookAt(const Vert3df& at)
{
    lookAt = at;

    auto zAxis = (lookAt - position).Normalize();
    auto xAxis = up.Cross(zAxis).Normalize();
    auto yAxis = zAxis.Cross(xAxis);

    viewMatrix.Zero();
    viewMatrix(0, 0) = xAxis.x;
    viewMatrix(0, 1) = yAxis.x;
    viewMatrix(0, 2) = zAxis.x;
    viewMatrix(0, 3) = 0.0f;
    viewMatrix(1, 0) = xAxis.y;
    viewMatrix(1, 1) = yAxis.y;
    viewMatrix(1, 2) = zAxis.y;
    viewMatrix(1, 3) = 0.0f;
    viewMatrix(2, 0) = xAxis.z;
    viewMatrix(2, 1) = yAxis.z;
    viewMatrix(2, 2) = zAxis.z;
    viewMatrix(2, 3) = 0.0f;
    viewMatrix(3, 0) = -xAxis.Dot(position);
    viewMatrix(3, 1) = -yAxis.Dot(position);
    viewMatrix(3, 2) = -zAxis.Dot(position);
    viewMatrix(3, 3) = 1.0f;
}

void Camera::UpdateProjectionMatrix()
{
    const float aspectRatio = screenWidth / (float)screenHeight;

    float tanHalfFOV = std::tan(fov / 2.0f);
    float zRange = nearPlane - farPlane;

    projectionMatrix.Zero();
    projectionMatrix(0, 0) = 1.0f / (tanHalfFOV * aspectRatio);
    projectionMatrix(1, 1) = 1.0f / tanHalfFOV;
    projectionMatrix(2, 2) = (-nearPlane - farPlane) / zRange;
    projectionMatrix(2, 3) = 1; // Left handed, invert for right handed
    projectionMatrix(3, 2) = (2 * nearPlane * farPlane) / zRange;
}

```

Any idea why the ordering might be reversed in my case? I've compared my Mat4x4f implementation to DirectX's XMMAtrix and get the same results when multiplying 2 arbitrary matrices.

**Tags:** transformations, mathematics, projections, matrix

> **Comments:**
> **username_1**: if you can compare check the result of `pvw * p1` in DirectX with the same values. If its same as your `wvp * p1` then your implementation is opposite. If it's not the same, then your implementation might be fine and the problem might be somewhere else I guess.  
> **username_2**: In matrix mathematics transposing the matrix reverses the calculation order. So $A * B = ( B^T * A^T)^T$  
> **username_0**: I saw a mention of that in another thread, but I don't believe I'm transposing the matrices anywhere. Does DirectX or OpenGl implicitly transpose these matrices somewhere?  
> **username_2**: You dont have to transpose a matrix to get the effect. All you have to do is build the matrix differently with different assumptions. A column major build order results in a matrix that is seeminly transposed version of a row major matrix. So all that needs to happen is that your not aware of the convention used. Buth are mathematically equivalent and as correct. I mean  you could also have a convention where the translate part is first, or second and not last like is customary. But no one does that. But its entirely calid to use different building rule  
> **username_2**: I mean its entirely valid to think from world to object than it is from object to world. Mathematicians prefer inside out because it follows the logic of function composition but you dont have to its natural for humans to think of other objects from outside in.  
> **username_0**: I've updated my question to include how I'm building the view/perspective matrices. I've substituted the code with XMMatrixLookAtLH and XMMatrixPerspectiveFovLH, but the implementation is still broken when I multiply as pers * view * world.  

---

## 0 answers

