But in Zig!
Translating the first bit of code from the book into Zig, we have created our first image!
After some more Stuff™ was written, there are now rays and colors and
viewports and cameras!
It's a lot more impressive behind the scenes than the produced image
lets on!
I'm now able to add spheres to my scene! Following the book, I was able to produce a simple image of a red sphere on my gradient.
Before moving on to the next chapter, I wanted to add some depth to the very flat sphere, so I decided to darken it based on the distance to the camera.
After that, I wanted to have some more color, so I tried turning the
collision normal into a color.
First, I used the absolute value:
Then I added (1, 1, 1) to the normal and divided it by 2:
I also tried just norming the vector again after shifting it, but I like the previous one better than this:
Couldn't resist and wanted to see if I could add a naive light source. I could.
Turns out chapter 6 wanted me to do what I already did at the end of chapter 5 on my own!
Later, a list of renderable objects was introduced, allowing me to have multiple objects in the image at the same time!
Before I did that, I actually made two red spheres using my simple light source.
Chapter 7 was about doing some refactoring which didn't apply to my
implementation, as the camera was already in its own file, just under a
different name.
In chapter 8, however, I added something cool: Antialiasing! Through
random sampling of a small square around each target pixel, and
subsequent averaging of the sampled values, a less jagged edge is
produced, which looks a lot nicer.
Look at the direct comparison!
At the beginning of this chapter, a couple of randomization methods are
implemented, which are then used to enable diffuse materials.
Here is a first example, using the two spheres from before.
It turns out the image is a lot darker than it should be, due to a
phenomenon called "shadow acne."
The point at which a ray intersects a sphere is never return perfectly,
due to floating point errors. If the returned value is ever so slightly
below the sphere's surface, casting another ray from that point will lead
to another intersection immediately.
This problem is solved by simply ignoring any collisions for some very
small values of t
.
Here is the result of that fix.
After implementing this simple uniform reflection, it was replaced with more realistic lambertian reflection instead, which resulted in this image.
Setting the reflectance to 10%, 30%, 50%, 70%, and finally 90% shows the gamut of the renderer.
Here is the gamut after applying gamma correction, showing the brightness more correctly.
After lots of refactoring, there are now different materials.
In addition to the original lambertian diffuse reflector, there is now
also metal!
At the end of the chapter, fuzzy reflections were introduced, enabling spheres to look like this.
This chapter introduced dielectrics, which can refract light, instead of
reflecting it.
The first refractive material introduced is glass.
Before moving on in the book, I decided to try to place a second sphere inside the glass sphere, with the inverse of the glass' refractive index, to see what that would look like.
The Way the book tested the partial reflection of rays at shallow angles was to model the sphere as an air bubble in water.
After introducing Schlick's approximation for reflectance, the book uses a hollow glass sphere to show this, which is exactly what I did myself two picture above from here, except now with reflectance.
The next step in making the camera customizable is to change the field of view.
After making implementing arbitrary camera positions (and fixing a sign error), I was able to move the camera to wherever I want.
In the book itself, two other angles were used as examples.
Experimenting a bit, I think this is my favorite angle.
One final feature the book introduces is defocus blur, which emits rays
not from the center of the camera, but from a small disk around the
center instead.
Resulting images have a nice blur.
Before the book is over, there was one last render, which took about 12 minutes on my system.
But it wouldn't be a true final render if it was actually final.
So there's an even more final render, with all three of the big spheres
made of glass!
Not sure why I did this.