Part 7a - Implementation (cont.)
The last part in this Implementation series of posts will be on different types of surface materials. Similar to lights in the previous post, materials have some overlap with the underlying library, Babylon.js (mainly in naming conventions and parameter names, i.e. color, roughness, opacity, etc.), but how these materials are handled in our path tracer requires further investigation.
There are almost countless types of materials found in the real world, but just like the light source types, luckily all materials end up falling into a handful of broad categories for path tracing purposes. I will discuss each broad category below, which will cover all conceivable materials. You might think that 5 or 6 categories is too limiting for describing millions of possible surfaces, but what ends up differentiating these seemingly countless materials is their parameters and numerous parameter combinations like color (that can come from a texture image), normalMap(also from a texture), metallic or not, index of refraction, roughness, clearCoat, etc.
What follows is a brief description of the main categories to which all materials belong.
Let’s start with the easiest surface to define and to path trace - an emissive material, or aka - a light! Wait I thought we covered lights already? Well yes we did, but it was more focused on their shape or form than than their actual material and emissive properties. When we have a light type in place, like a directional light or a point light, we need to give some sort of value for how much power is steadily radiating from its surface. What I failed to mention in the previous post is that with lights, you can either define their power or intensity with real world units and measurements (joules, lumens, or Watts), or it can be with relative units. Since most three.js and Babylon.js scenes have relative measurement units, like 10 instead of 10mm or 10cm, it makes sense for the lights to also give off radiation in relative units of power, like 100 or 1000. Keep in mind that these are arbitrary relative numbers, just like your 3d scene relative dimension units, that must be experimented with a little to your tastes. I know that some serious professional rendering packages offer real world light units and measurements with light bulb specifications in Watts or lumens, but to use this physical data effectively, the whole scene would need to be changed in order to accommodate a cm or meter basis. Therefore, we just simply assign a relative power to the light surface, so if 1 is your unit (which it is in my case usually), then an intensity of 10 would be 10 times as bright, an intensity of 100 would be 10 times as bright as the 10-power light, and so on.
That all aside, light surfaces are usually defined with an intensity in RGB values. So a bright reddish light would be (10, 1, 0.5) or similar. The reason I said these were simple surfaces is that these 3 rgb numbers are all we need to totally define an emissive surface’s radiance! Additionally, in path tracing when you encounter a light or a shape with an emissive material, you are done tracing for that frame! Since we are ray tracing in reverse vs. the direction found in nature, rays do not ‘bounce’ back off from a bright light once you find it. One of the best sayings in path tracing theory is “Always follow the light!”. We won’t see a thing until our rays encounter a surface with an emissive material. And once we do find a light, we’re done!
The next easiest material is the Metals category. Metal (aka specular) materials are also simply defined by an RGB specular reflectance. In the real world, the metal that comes very close to a pure white (unaltered and non-tinted) reflection is aluminum, with relative RGB reflectance of (0.99,0.99,0.98) or something like that, I can’t exactly recall. But anyway, you can give a metal surface a value of white and be close enough to the eye to look like an aluminum backed mirror. What’s interesting about metals is that they tint the outgoing ray when it has reflected off the metal surface. So if you have a blue metal sphere, any rays reflecting will be tinted blue, and therefore the entire reflected image on the sphere’s surface will be also tinted blue. That is why gold throughout history is so prized and coveted - in addition to being rare, it reflects light and tints reflected light in a fiery red/orange color spectrum, which makes its surface reflections brilliant, fiery, warm and captivating to human eyes.
In code, Metals also have the most simple 1-line ray reflectance function that uses the glsl intrinsic reflect(incomingRay, surfaceNormal) function. The angle of the outgoing ray matches that of the incoming ray, everything being oriented about the surface normal.
The next broad category has a fancy name: dielectric materials. That means that this type of material does not conduct electricity or is a poor conductor, if anything. In fact, not counting emissive materials (lights) and dismissing rarely encountered semiconductor materials, we can even further reduce the number of rendering material categories to 2! - metals and dialectrics. Metals conduct electricity, dialectrics do not. Metals reflect practically 100% of all incoming light, dialectrics reflect some, and let the remainder pass through their surfaces - all according to their fresnel reflectance and viewing angle. Metals tint their reflected scenes by multiplying the reflected rays by their surface color reflectance, dialectrics leave the reflections un-tinted, or you can think of the rays getting multiplied by pure white (1.0,1.0,1.0) which is like multiplying a number by 1.
Only a small percentage of real world materials are metals that conduct electricity.
Most materials in nature are dialectrics, or insulators (do not conduct electricity). Well-known examples of dialectrics include water, glass, plastic, skin, rubber, wood, concrete, even gasses like air, etc. - pretty much everything except metal!
When we are path tracing dialectrics, a percentage of the rays get reflected off the surface (and left un-tinted, remember), and the remaining percentage of rays must be refracted, which is a fancy word for letting the rays pass through the surface to the other side. The interesting phenomenon is that if the dialectric surface is transparent, and the rays pass through, they are colored (or tinted) by the rgb color of the surface interior! So there is a dual nature to dialectrics like murky water and colored plastic or colored glass. Imagine a perfectly smooth, shiny red plastic ball - if you look at the sides of it from a glancing angle, the reflected scene will be unaltered white, not tinted red, but left whatever color it’s going to be. Same with a still, blue lake as viewed from a glancing angle- the mountains and trees and clouds in the mirror-like reflection are not tinted blue, but remain unchanged. Now if you are able to see through the surface and the rays encounter particles in a medium that they are traveling through, like colored glass, or a murky green algae-infestesed pool, then whatever is on the other side of the surface boundary will indeed be colored, and the deeper the rays go into the medium, the more color tinted they get. Usually in ray tracing we further group dialectrics into transparent materials like clear plastic, clear glass, colored glass, gems and water, and then we place all the other thicker, more opaque mediums like skin, rubber, wood, marble, chalk, wax, etc., into an ideal diffuse group, (or subsurface scattering group if you have a very sophisticated renderer), all of which have refraction and subsurface scattering of rays just below the surface which gives them their warmth and depth, and sometimes even their apparent glow.
When it comes to tracing rays through the first group of dialectrics (water, glass, plastic, diamond, precious stones, etc.), the code gets a little more mathematically intense, as it has to first calculate the Fresnel reflectance, which gives the correct proportion of light that must be refracted (or allowed to pass and subsequently tinted) vs. the proportion of light that must be reflected and left un-tinted. This all relies on the angle that we are viewing the surface from, as well as the material’s index of refraction, or IoR for short. The IoR serves 2 purposes - first it gives the relative amount of reflected rays that will bounce off the surface like from an aluminum mirror. The higher the IoR, the more pronounced this reflection becomes at grazing angles. The second purpose is that the IoR controls how much the refracted rays will get bent when they pass through the surface boundary. The higher the number, the more the rays are bent. If you consider an expertly cut diamond, it has one of the highest index of refraction values of all clear materials found on earth, and with that high number comes bright, strong reflections at grazing angles, plus if the rays do pass through, they are so severely bent that they end up getting bounced around many times inside the diamond and even may undergo total internal reflection. This severe refraction, bright grazing reflection, and multiple total internal reflections resulting from its high IoR, gives diamonds their bright, sparkling, fiery, brilliant look.
Now we move on to the Diffuse category. Diffuse materials are often a convenient, idealistic abstraction of what really happens in nature with thick, opaque dialectrics - subsurface scattering. Going back to the red ball example, let’s instead make it a dull red, rubber ball - like a worn gym kickball. If we were to examine what happens with rays entering the ball’s surface, we would find that they bounce around randomly, interact with the microscopic paint particles in the material, and then exit somewhere different than they entered. This is true of most opaque dialectrics that have some thickness to them. However, even though it is possible, subsurface scattering is very difficult and expensive time-wise to model in a path tracer. So usually we idealize these materials into a broad Diffuse category. Instead of interacting underneath the material’s surface and progressively picking up the surface color, light rays are immediately tinted the surface color and then randomly reflected from the surface to continue on their journey through the scene. The diffuse reflection directions are chosen inside an imaginary tiny hemisphere that is oriented around the surface normal. The flat bottom of the hemisphere lies on the surface, while its top dome apex coincides with the surface normal’s tip. The surface normal is normalized and is of length 1, and so are the dimensions of the hemisphere - radius 1, and apex dome height of 1. Diffuse surfaces like chalk have many microscopic bumps and crevices, which make incoming rays reflect in seemingly random directions all over the hemisphere. Contrast this with the smooth V shape of the incoming and outgoing reflection ray when it encounters a smooth metallic surface.
In code, just like the metal surface tinting the outgoing reflected rays, so does our idealized diffuse model (but we know the real truth - rays undergo subsurface scattering underneath the material’s surface boundary, causing them to pick up the reflected color of the object). In path tracing, we follow the life of a ray as it bounces around the scene. If it hits a rough / or dull diffuse object, we must randomly select an outgoing reflection direction somewhere in the hemisphere. Oops, there are those words “randomly select” again! And by now we all know what that means: our friend, noise is back!
Luckily for us there are well-established, clever sampling strategies for ideal diffuse surfaces, so that all paths that are randomly chosen will make the most contribution to the final color and lighting of the diffuse surface. No samples are wasted.
The final category that I’d like to talk about is ClearCoat: or diffuse underneath / ClearCoat on top. 2 examples that come to mind are billiard balls and a heavily polished wood table. If you view the edges of these objects, or view their surfaces from a glancing angle, you will see a pure, un-tinted reflection. But at the same time you can see through the clear coating (polish), and if you view the surface parallel to the surface normal, it looks like a diffuse object underneath at the same time. Again with anything clear, like ClearCoat materials, you have to specify an IoR for the clear coating itself.
When path tracing ClearCoat materials, first a reflection ray is cast out, based on the viewing angle and reflective properties of the IoR. Then, since the material has an idealized Diffuse surface underneath, we must treat it just like we did with normal Diffuse materials- making the incoming ray pick a new random scattering path direction when it interacts with the microscopically rough (or subsurface) material.
Well I apologize - this part 7 post is getting way too long. I will return soon with part 7b, which will cover roughness(on metals and glass, etc.), and how it affects materials when path tracing. Also I’ll briefly touch upon material BRDF sampling and how we can sample a rough metal material or frosted glass for instance, just like we randomly sampled a spherical area light all over its surface before.
I’ll be back soon!