Displaying 3D data
Now that we have a way to represent the orientation and position as well as to model a perspective camera, we can start to actually display some 3D data.
The shader side doesn't actually require much change, we can just define the view and projection matrices and augment the shader from the previous section to use those.
On the pipeline side,there are two important things we have to add, but luckily most of our work is already done! First off, we need to add the perspective division and viewport transform. We will just put that in its own function and call that in the line and triangle processing functions.
The other change involves clipping. One issue of our pinhole camera model is, that if we have points behind our camera, they will be projected to the front, but mirrored! Additionally, there is an issue with points on the plane containing the camera, since those points will have as homogeneous coordinates, which will cause issues in our code. The solution is to clip all our primitives at the near plane of our clipping volume. That way, all problematic cases won't even arrive at the final rendering. But we have to make sure, to do the clipping before the perspective division for that to work.
It might sound like a difficult problem to clip the lines and triangles with their 4D coordinates, but as it turns out, the way we implemented everything so far allows us to do that without any change! We just have to specify the appropriate clipping planes. Usually, the whole clipping volume is clipped, but only one is really necessary to prevent the problematic cases: The near plane.
So in NDC the camera looks into the direction. Therefore the normal of our plane is , pointing to the inside. The plane is located at , thus the distance is , but by our definition of planes from the section about clipping, we need to negate the distance. The plane is thus defined by the 4D vector .
We don't work in 3D though, but in 4D before the perspective divide. But the plane definition doesn't change when we multiply by a number, for example the coordinate of our points. So this plane still works for homogeneous vectors!
So there is nothing new to implement for the clipping, we just add this plane by default to the clip planes.
We will see though, that there is still something weird happening, but that is for the next section, so don't worry, if it looks weird, compare it with the solution given below!
(Actually there will be two weird things, but one is more pressing, so we handle it first).
Exercise:
-
Add the near clipping plane (done)
-
Implement a perspective divide/viewport transform function in
viewport_transformin rasterizer.js- Call the function in
process_lineandprocess_triangleafter the clipping for each point - As mentioned in the code, the last coordinate of the vector would be 1, as we divide the whole vector by its previous value w. But as it will come in handy, we will store the value 1/w in the last coordinate after the divide.
- Call the function in
-
Define the 3D Scene
- Define a 3D shape. We can use the
create_cube_geometry()to create the geometry (done) - Define view and projection matrices to place our camera (done)
- Add view and projection to our vertex shader and use them (in shaders.js)
- Define a 3D shape. We can use the
Now, if you look at your solution (and the one provided below), you might be slightly confused by what you are seeing. There is obviously a 3D effect going on, but it seems the shapes of the cubes are weirdly overlapping itself and each other. The issue is caused by our current process: We go through each object and write the result into the image. But that results in things being drawn last showing up in the front. What we actually want is seeing the object that is "in front" in 3D to show up on the image and those that it occludes not showing up.
This will be fixed in the next section.
Solution: