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.
pvw * p1
in DirectX with the same values. If its same as yourwvp * 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. $\endgroup$