Now we have a working camera, and triangles are drawn from the most distant to the nearest. We’re getting very close to a complete renderer. The main issue remaining is that when we walk into one of the cubes, everything goes haywire. This is down to the division by z in the projection function. If z is 0, then infinity is returned, and we get odd results. If z is negative, then we get warped triangles and inverted rendering. What we need to do is detect when triangles are too close to the screen, or are behind the camera and skip rendering them.
As always, click here to give it a try, and here to download it. Here are the previous posts:
- Projection
- Wireframe
- Rotation
- Culling
- Inside Test
- Scanline Rasterisation
- Texture Mapping
- Camera
- Painter’s Algorithm
The first step takes place after all of the transformations on the vertices, but before projection. We now have the final z coordinate before we decide where the point is on screen. If that z value falls below a certain amount we then skip projection, and instead add the value false to the table of projected vertices. The reason we don’t just skip the value is because we need to preserve the index of each element so that the triangle table continues to reference the correct vertex. If we didn’t do this then we would end up with corrupted triangles that are using the wrong vertex data.
I chose the value 0.2 to clip at here rather than 0 simply through playing around. Clipping triangles at 0 felt too close, and left more large obscuring triangles on screen.
if v.z>0.2 then
add(proj,project(v))
else
add(proj,false)
end
After adding the false values into the projected vertex list we then detect it when building the draw list of triangles. If any of the points of the triangle we are adding to the list falls behind the near plane then we skip the entire triangle. This means that we only sort and then render the triangles that are visible in front of us.
if a and b and c then
local z=a.z+b.z+c.z
add(lst,{a,b,c,z=z})
end
This leaves us with a working renderer. We can walk right up to the cubes, and in fact walk right through them without any corruption on screen. After this the remaining changes are about performance and simplicity. I think the next step will be around replacing our chain of rendering function calls with 4x4 matrix transformations. Doing this allows us to bake the set of calls needed per model, and then do a single matrix multiplication per vertex which itself contains all of the translation and rotation calls needed for both the model and the view.