Notes for Wednesday February 24 class -- shadows and reflections

Shadows

Cast shadows are an example of secondary rays. Once you have determined which surface your primary ray has hit, you can then send a ray from that surface to each light source. If this secondary ray misses all other objects in the scene, then the surface is being lit by that light source, and you can mix in the diffuse and specular components of Phong reflectance.

But if the secondary ray hits any other object, then the surface is not being lit by that light source, and you should not mix in the diffuse or specular components of Phong reflectance.

This secondary ray is formed by starting the ray at a point just outside the surface point S, such as S + εN, where ε is some very small number, and shooting the ray into the direction of the light LDir[i].

In the example code, I have implemented cast shadows for you.

 

Reflection

Reflections are created by first computing the mirror reflection direction from the primary ray, then shooting a secondary ray from a point just outside the surface:
V' = S + ε N
into that direction. Whatever object is first hit by this secondary reflection ray, if any, is then rendered.

Given the direction vector -W from the surface back to the view point, the direction of the reflection ray is formed by our usual method of computing a reflection:

W' = 2 (-W • N) N - (-W)

The first object, if any, that this reflection ray hits is then rendered, and that rendered color is mixed into the final color of the object at this pixel.

In the example code, I have described the step by step algorithm for implementing reflection. Your job will be to replace this description by an actual code implementation.

 

Refraction

Refraction occurs when light goes through a transparent object, such as glass or plastic or water or diamond. The light bends each time it encounters the interface between the air and the refracting material.

In ray tracing we trace light backward from the eye. When a ray from the eye encounters a transparent refracting object, the ray changes direction, due to refraction, which follows Snell's Law:

n1 sin(θ1) = n2 sin(θ2)
where n1 and 2 are the indices of refraction for the two materials (eg: air and glass), and θ1 and θ2 are the incoming and outgoing angles of the light path.

Index of refraction measures how slow light becomes when it is traveling through any particular material. The index of refraction of air is essentially 1.0, because the speed of light through air is very near to the speed of light in a vacuum.

The index of refraction of water is about 1.33, of various kinds of glass is about 1.50, and of diamond is about 2.42.

The general idea for rendering a sphere made of refracting transparent material is that you first see where your ray V+tW hits the front of the sphere (the first, smaller root of the quadratic equation).

Then you use Snell's Law to figure out the direction of a ray inside the sphere. You form that ray, and find the point of intersection with the back of the sphere (the second, larger root of the quadratic equation).

Finally you use Snell's Law again to figure out the direction of the ray that emerges out from the rear of the sphere. You find out what object is hit by that emerging ray, if any, shade it, and use that color as the color of your glass sphere.

In the example code, I have described the step by step algorithm for implementing refraction. Your job will be to replace this description by an actual code implementation.

Update on Feb 28:

Some people have been having trouble implementing refraction from the hint in the code, so here is a more detailed hint.

The first time your ray intersects your sphere, you can use the raySphere() function. But for the second intersection, when the already refracted ray exits the sphere and refracts again on its way out, you will want to implement a modified function raySphereExit(), which takes the second root of the quadratic equation rather than the first root, by adding the sqrt(D) term rather than subtracting it.

To refract a ray W that enters a surface which has a surface normal N, you want to carefully break down W into component Wv that is aligned with N and Wu that is perpendicular to N.

You can get Wv just by projecting W onto N:

Wv = N * (W • N)

The perpendicular vector is then whatever is left after Wv is removed from W:

Wu = W - Wv

Because the ray will be refracting, you are going to rescale both Wv and Wu.

The new Wu' is a scaled version of Wu, according to Snell's law:

Wu' = Wu*n1/n2
where n1 is the index of refraction before the ray passes the surface and n2 is the index of refraction after the ray passes the surface.

Because you have rescaled Wu, you now need to rescale Wv, so that the emerging refracted ray will be of unit length. You can do this as follows:

                                      ___________
Wv' = normalize(Wv) * √1 - (Wu' • Wu')
The emerging refracted ray is now just the sum of Wu' and Wv':
W' = Wu' + Wv'

If you've done it right, you should get a result that looks something like the image to the right. In this case, I have set refractive index n to 1.5, which is a typical refractive index for glass.

 

 

Homework

Due next Wednesday before the start of class

Change the code so that spheres and surface reflectance properties are defined in Javascript, rather than at each pixel (like I did in class with lights).

Implement mirror reflection, following the notes in the code.

For extra credit, implement refraction, following the notes in the code. Note that this is much more difficult than reflection. I'm not expecting everyone to be able to do this; consider it a stretch goal.

For your homework, you can start with this code base: hw3.zip