You will notice that the reflectivity of smooth surfaces vary with angle. This is especially easy to see on water:
|Note how the reflectivity increases as the angle of the viewer increases as the incident angle of the viewing direction to the normal increases. Work by Escher.|
The fraction of how much light is transmitted and how much is reflected adds to one. This is true tracing the light in either direction:
So in a ray tracer we would have the color of the ray be
color = 0.77*color(refracted_ray) + 0.23*color(reflected_ray)
But how do we compute those reflected and refracted constants?
These are given by the Fresnel Equations. For a non-metal like water, all that you need for them is the refractive index of the material. Back in the dark ages, I was very excited about these equations and used them in my ray tracer. Here they are from that wikipedia page:
Here R is the reflectance, theta is the angle to the normal of the incident direction, n1 is the refractive index of the material the incident light is in, and n2 is the refractive index of the material the light is going into. But there are two of them?! One is for one type of polarization, and one is for the other. The "types" are relative to the surface normal. This is a cool topic but we would need would delve into some pretty serious optics we wont use (though many applications do where optical accuracy is needed and Alexander Wilkie has been doing great work in that area). So what if I don't have polarization in my ray tracer. I used to just average them:
R = (R_s + R_p) /2
So how do those look? Here is from that same wikipedia page as above:
Well there are some cool things there. Especially that R_p goes to zero for one angle. This is Brewster's Angle and why I have polarized sun glasses-- so I can get x-ray vision into water! But I wont simulate that in a ray tracer.
So what does R = (R_s + R_p) /2 look like for the example above. I'll type it into grapher on my mac, where x is in theta_i in radians:
Wow that is certainly a complicated expensive function for such a smooth curve. The x axis in in radians so 90 degrees is x = PI/2 = 1.57. So 0 to 1.57 is the interesting part.
Now I ran that code for years. But Christophe Schlick has some sweet papers in the 1990s about using simple approximations to smooth functions in rendering. I mean why are we solving the approximate equation (we threw away polarization) with exact, expensive equations??! His equation for approximate fresnel reflection was almost immediately adopted by all of us. Here it is from that wikipedia page:
OK that is close! And a lot easier and cheaper.
So where do you get n1 and n2? If one of the surfaces is air use 1.0 for that. From that wikipedia article on refractive index above we have:
When in doubt, use 1.5.
Now what happens if you want the rainbows you can see along edges in glass? That is dispersion and it is a pain in RGB renderers... you probably need more samples than 3 in color and you need to convert to a spectral renderer. This has been done a few times (here is a fun one with lovely images) but I wont here!
Note that this same equation is used not only for transparent surfaces but also for the reflective coat of "diffuse-specular" surfaces. Here is one way to think of these surfaces-- a chunk of glass/plastic/whatever with junk in it:
Now what about metals? Does the above apply? No, they are nothing like the Fig 2.11 above. All the action is at the surface. But the Fresnel equations are more complex-- literally-- the refractive index of metals is complex! This is sometimes written as real refractice index and conductivity (the real and imaginary part).
Here are the equations:
Wow. Those are even worse (with k, the conductivity set to zero, you get the non-conductor ones). So for some applications, this is needed. But not for most graphics programs. I rarely use them. Note that they are real valued. So can we use Fresnel? The answer is yes! So where do I get n and k? I implemented them in the dark ages and tried it for a few metals. Here is one:
That looks like we can probably use the Schlick equations but with different base cases for R, G, B. So where do we get the normal incident reflectance? We could by evaluating normal incidence. But the data isn't widely available and a lot of metals are alloys of unknown composition. So we make R0 up (like grab them from an image-- it's an RGB color, but be careful about gamma!)
vec3 R0 = (guess by looking)
vec3 R = R0 + (1-R0)*pow(1-cosine,5)
But with RGB not just floats. This is because the refractive index for metals can vary a lot with wavelength (that is why some metals like gold are colored).
Now most graphics programs have two Schlick Equations running around-- one for dielectrics that are float and set by a refractive index, and one that is RGB and set by an RGB R0. This seems odd at first, because it is, but it makes sense if you go searching for the refractive index of glass, then gold (try it!).
Naty H writes:
For the curious-- does light refract into metal? Yes it does, but it gets absorbed quickly. This is why a gold film is transparent if it is thin enough. Here is information on refraction for metals (I have never implemented this) using Snell's law: