I have to write my own software 3d rasterizer, and so far I am able to project my 3d model made of triangles into 2d space:
I rotate, translate and project my points to get a 2d space representation of each triangle. Then, I take the 3 triangle points and I implement the scanline algorithm (using linear interpolation) to find all points[x][y] along the edges(left and right) of the triangles, so that I can scan the triangle horizontally, row by row, and fill it with pixels.
This works. Except I have to also implement z-buffering. This means that knowing the rotated&translated z coordinates of the 3 vertices of the triangle, I must interpolate the z coordinate for all other points I find with my scanline algorithm.
The concept seems clear enough, I first find Za and Zb with these calculations:
var Z_Slope = (bottom_point_z - top_point_z) / (bottom_point_y - top_point_y);
var Za = top_point_z + ((current_point_y - top_point_y) * Z_Slope);
Then for each Zp I do the same interpolation horizontally:
var Z_Slope = (right_z - left_z) / (right_x - left_x);
var Zp = left_z + ((current_point_x - left_x) * Z_Slope);
And if current z is closer to the viewer than the previous z at that index THEN write the color to the color buffer AND write the new z to the z buffer. (my coordinate system is x: left -> right; y: top -> bottom; z: your face -> computer screen;)
The problem is, it goes haywire. The project is here and if you select the "Z-Buffered" radio button, you'll see the results... (note that I use the painter's algorithm (-only- to draw the wireframe) in "Z-Buffered" mode for debugging purposes)
PS: I've read here that you must turn the z's into their reciprocals (meaning z = 1/z
) before you interpolate. I tried that, and it appears that there's no change.
What am I missing? (could anyone clarify, precisely where you must turn z into 1/z and where(if) to turn it back?)
[EDIT] Here's some data on what maximum and minimum z values I get:
max z: 1; min z: -1; //<-- obvious, original z of the vertices of the triangles
max z: 7.197753398761272; min z: 3.791703256899924; //<-- z of the points that were drawn to screen (you know, after rotation, translation), by the scanline with zbuffer, gotten with interpolation but not 1/z.
max z: 0.2649908532179404; min z: 0.13849507306889008;//<-- same as above except I interpolated 1/z instead of z.
//yes, I am aware that changing z to 1/z means flipping the comparison in the zBuffer check. otherwise nothing gets drawn.
Before I go into painstaking debugging, can someone confirm that my concept so far is correct?
[EDIT2]
I have solved the z-buffering. As it turns out, the drawing order wasn't messed up at all. The z coordinates were being calculated correctly.
The problem was, in an attempt to increase my frame rate, I was drawing 4px/4px boxes, every 4th pixel, instead of actual pixels on screen. So I was drawing 16px per pixel, but checking the z buffer for only one of them. I'm such a boob.
TL/DR: The question still stands: How/why/when do you have to use the reciprocal of Z (as in 1/z) instead of Z? Because right now, everything works either way. (there's no noticeable difference).