Perspective Correct Texture Mapping

So I'm trying to correctly map my textures in my software renderer using the u, v coordinates but I can't seem to get it working. I got affine texture mapping working. This is what I can produce using it: You can see from the "tilt" in the texture that it's not quite right. If I add more triangles to the mesh it becomes more correct. From my understanding these are not perspective correct textures. But this perspective divide thing is confusing me. I read that I needed to divide the u, v coordinates by the w component (perspective divide). I'm guessing that this is the w component from the vertex after multiplying with the projection matrix. But I couldn't get that too work. So I looked a little bit more into the problem. First, here's what is happening to one vertex of my triangles.

float4 pt1 = worldViewProj * mesh->vertices[mesh->indices[i]];
float2 uv1 = mesh->texCoords[mesh->indices[i]];
triangle.p1 = float3((pt1.x / pt1.w + 0.5f) * screen->GetScreenWidth(), (pt1.y / pt1.w + 0.5f) * screen->GetScreenHeight(), pt1.w);

I multiply it with the wVP and then do my perspective divide with x and y. The + 0.5f is to center it in homogeneous space and then multiply with the screen dimensions to get it in screen space. I then stuff the w component of the vertex as the z value so it will be sent to the triangle drawing function and can be used for things like the depth buffer.

To my understanding the z value from the vertex gets put into w after multiplying with the projection matrix. This makes sense since m32 = 1 in my projection matrix. So after multiplication with the projection matrix w = z. I first tried dividing the texture coordinates per vertex by w. Like so:

float2 uv1 = mesh->texCoords[mesh->indices[i]] / w;

Then in the triangle drawing function I used the bary centric coordinates to interpolate across these the uv coordinates. This did not work. Since w = z. When the texture is far from the camera (ie z is a large value. With my world translations applied z = ~700) then the texture coordinates shrink a lot and in every render I just get the (0, 0) value of my texture.

But I also tried this instead in my triangle drawing function:

float2 texCoord = (texCoord1 * u + texCoord2 * v + texCoord3 * w) / z;

(Here u, v, and w are the bary centric coordinates. Each texCoord is the texture Coordinate at that vertex) The difference here is that the interpolated z value is being divided at every pixel instead of it being divided at the vertices. Either way I can't seem to get it too work. Again the z value is too large and I end up getting (0, 0).

So I'm investigating what I need to divide u and v by. Obviously I'm doing something wrong. Is it really the w component of the vertex after multiplying with the projection matrix? Is my w value wrong? Could the projection matrix be causing this? I've been googling around for awhile now... x / w and y / w are [-1, 1] (homogeneous) but z isn't [-1, 1]. Is it supposed to be? My perspective divide for x and y obviously works.

Here's how I built my projection matrix just in case it's wrong?:

float tanHalfFov = tanf(fov * 0.5f * 3.14f / 180.0f);
float s = 1.0f / (tanHalfFov * aspectRatio);
float zRange = farZ - nearZ;

// Set the projection matrix
proj.m00 = s;    proj.m01 = 0.0f;            proj.m02 = 0.0f;          proj.m03 = 0.0f;

proj.m10 = 0.0f; proj.m11 = aspectRatio * s; proj.m12 = 0.0f;          proj.m13 = 0.0f;

proj.m20 = 0.0f; proj.m21 = 0.0f;            proj.m22 = farZ / zRange; proj.m23 = farZ * nearZ / zRange;

proj.m30 = 0.0f; proj.m31 = 0.0f;            proj.m32 = 1.0f;          proj.m33 = 0.0f;
• The projection matrix transforms points into clip space, which is a 2x2x2 cube centered at the origin, not what you're calling "screen space". Homogeneous coordinates means you have a w in addition to your spatial coordinates(x,y and possibly z). It has nothing to do with normalization. You never divide by Z - always W. The point of W is that you can perspective divide and keep depth (again, in clip space), and nicely lets you add translation into your matrix rather than having a separate step. You're mixing up a lot of different concepts. – 3Dave Oct 5 '16 at 22:23