Let’s continue working on our ray tracer and pick up where we left off last week. First, as you are already used to, we’ll do more code cleaning. I went ahead and replaced all classes with structs, and also used proper naming conventions (such as capitalization of types) this time. You can see the modified code in this week’s repository. To keep this article short, I am not going over the cleaning procedure this time, but you will notice the transformations were rather minimal.

Last time we looked at how lambertian and metal materials are rendered. The last type of material we need to look into is called dielectric and you can recognize it when looking at water or at objects made of glass. When dielectrics are hit by a ray, the ray splits in two: a reflected (bounced) ray and a refracted (propagated) ray. Refraction is described by Snell’s law. Let’s see how this law translates into code. In material.swift create a refract() function:

Next, let’s create the Dielectric struct. Notice that attenuation is always 1 because dielectrics never absorb anything from an incident ray:

We first compute an outward normal depending on whether the dot product between the ray and the hit point is positive or not, and then we use that to compute the refracted ray. In case it comes out nil, we reflect the ray, otherwise we refract it. In pixel.swift replace the second metal sphere with a dielectric one:

In the main playground page, see the generated new image:

Glass surfaces have reflectivity that varies with the angle. When you look at it perpendicularly, the reflectivity is the lowest possible, if any. The smaller the viewing angle gets, the higher the reflectivity is, and other objects from the world are mirrored more clearly by the glass surface. This effect can be computed with the Schlick polynomial approximation:

The scatter() function needs to be adapted to use this approximation:

Note that we are now refracting based on a reflectivity threshold we set to 1. There is an easy way (trick) to get a hollow glass surface. If the radius is negative, even though the geometry remains unaffected, the normal will point inward and the result will be a nice looking hollow glass sphere. Let’s add one more dielectric sphere with a negative radius:

You should be able to see the hollow glass sphere now. Before wrapping up, we need to do one more thing - fix the camera, so we can look at objects from different angles and distances. First, we need a field of view for our camera. Then, we need a lookFrom point and a lookAt point to set the direction our camera will look. Finally, we need an up vector so we can rotate the camera around its direction, and always know where up is. In ray.swift let’s replace our old camera with this one:

In pixel.swift replace the line where we make a call to the camera, with this code:

In the main playground page, see the generated new image:

The source code is posted on Github as usual.

Until next time!