I have started on getting a multiple importance sampling system going in my ray-tracer project for the light sampling. Rather than having a special "light" object type, I've decided to simply make all lights emissive meshes. There is a pre-processing step before BVH construction during which an internal "lightlist" is built - the renderer checks every triangle in the scene, and if any assigned material has the "emits light" flag enabled a pointer to the triangle is added to the lightlist.
During shader evaluation, lights are looped through from the lightlist and sampled directly. I had 2 possible ways to implement this in an unbiased and energy conserving way:
1. Two separate loops - a "bxdf sample" loop and a "light sample" loop. During bxdf sampling all rays that strike the light will return black (the bxdf sampling loop only returns indirect light contribution). During light sampling all rays will be exclusively directed at the lights and only return a value if striking a light. The math works out to be incredibly simple - the light loop and bxdf loop can happen independently and each one can have an arbitrary amount of samples. It looks like this:
Total_Bxdf_Irradiance += Bxdf_Irradiance / number_of_bxdf_samples
for each light:
Total_Light_Irradiance += (Light_Irradiance / distance_to_light_squared) / number_of_light_samples
Total_Irradiance += Total_Bxdf_Irradiance + Total_Light_Irradiance
2. One loop which randomly chooses either a bxdf sample or a light sample, shoots that out, then downweighs it probabilistically based on the probability density function (pdf) of that sampling strategy. This is called multiple importance sampling and the math is more tricky than the previous method. Assuming "ratio" is a zero-to-one ratio value between how many bxdf samples vs light samples are sent out, it looks like this:
if random_number(0..1) < ratio:
Total_Irradiance += Bxdf_Irradiance / ( PDF_Bxdf * ratio + PDF_Light * (1 - ratio) )
Total_Irradiance += Light_Irradiance / ( PDF_Bxdf * ratio + PDF_Light * (1 - ratio) )
Total_Irradiance /= number_of_samples
I have decided to try and implement the second method as it is more challenging, and avoids fireflies where light sources get near objects. For the moment I have a hacked solution and have not nailed the PDF calculation of the light sampling yet. I also wish to add a "probabilistic light selection" rather than always sampling every single light in the scene, and I am not sure how this will effect the PDF of the light sampling. Hopefully it is simple and the PDF remains unchanged, but I need to find and read more papers about this or flip through PBRT again and see if they cover this.
Occasionally (very often), my raytracer project will have some bugs.
As the code is all about outputting images, the bugs are often also graphical in nature.
In this case the diffuse shader, rather than tracing outwards in a hemisphere oriented to the surface normal, was tracing in a flat 2d disk shape oriented with the world Y-axis. Sometimes bugs bring a cool surprise.
Here is another one. I am not sure what caused this but it certainly looks broken. Somehow the wing of the angel has reflected at a much larger scale in the back..
I wanted to start a blog for a long time, a place for my to collect all of my random thoughts and projects, but I always found an excuse to put it off. Now I’ve finally committed to the idea!
Diffuse = Spec?
Something that has always been inconsistent between different lighting artists and TD’s that I’ve talked with is their explanations of “diffuse”.
Every major renderer has a shader that follows this template:
Diffuse and spec are modeling the same phenomenon, light reflecting off a surface, so diffuse is basically a more optimized/efficient shortcut for “very rough spec”.
Sometimes people will take it a step further with the following conclusion:
A single material cannot be smooth and rough at the same time; the surface can only have one shape. Therefore the “spec” in shaders must be another, separate material (gloss/clearcoat/moisture) with a smooth shape, layered on top of a material with a bumpy shape:
Other people forego this conclusion but still insist on the first point: There is no difference between diffuse or spec in the real world; there is only reflection of light. The only thing that matters is the shape of the object’s surface.
The reality of diffuse..
Take a piece of chocolate and roll it under the light. The chocolate does not have any moisture, there is no layer of oil covering the chocolate, and the chocolate has no clearcoat on it (I hope).
Despite this, the chocolate reflects light in both a "rough" way (the matte brown) and a "smooth" way (the sharp white highlights). One way we can explain this is that the chocolate surface is covered in a mixture of smooth and rough areas, so microscopically small that to our eyes it evens out to both a "diffuse" and "specular" response in one place. But how does this explain the highlights being white, and the diffuse color being brown? Is it possible the chocolate is always, somehow, covered in a thin film of oil?
As a thought experiment, imagine a cube made of solid red plastic. The plastic will, as we know, have a red diffuse and a "white" spec. Now, imagine a laser slicing this plastic cube down the middle, leaving a perfectly smooth surface exposed. How will this surface look? Since it is perfectly smooth, shouldn't it reflect all light back as "red spec"? In that case, it will be a red mirror, almost like a metallic Christmas ornament.
This doesn't seem right for plastic, does it?
Here is another example, a polished granite counter top with no clear-coat or varnish, simply a very polished stone.
Again, we can see a brown diffuse color (with a visible texture this time), and a "white" specular reflection (the reflection is not tinted towards any color).
Okay, one more example.. a piece of wood being lit by 3 dots.
I like this one because if you look closely you can see the separate diffuse reflection, crawling vertically up the side of the wood, and specular reflection mirrored in it horizontally.
Okay, so maybe you know that all surfaces exhibit some form of specular reflection, but how do we get a diffuse reflection from a very smooth surface? Is it possible that some mixture of rough areas and flat areas, on a microscopic scale, causes the illusion of separate diffuse/spec? If we had an ideal, perfectly smooth surface.. could we still get a diffuse reflection?
Diffuse = SSS
The answer is, yes! Actually, the “lambert” reflection model we all use is meant to be simulating diffuse reflection from a perfectly smooth surface! This is because diffuse “reflection” is actually the same phenomenon as subsurface scattering, albeit on a much smaller scale.
Light penetrates the surface of the material, bounces around inside, and comes back out in a random direction. The only difference is the distance the light is scattered – in a material like wax or milk it is scattered far and exits the surface a good distance away from the entry point, while in a piece of wood or a rock it exits much closer to the entry point. Take a big enough candle, look at it from far enough away, and it will look just as “diffuse” as any rock or tree!
This is the reason many shaders also have a “diffuse roughness” separate from the “spec roughness” - it slides between a smooth surface (which scatters diffuse light) and a rough surface (which also scatters diffuse light).
In V-Ray, for example, the slider blends between a Lambert shader (diffuse scatter from a smooth surface) and an Oren-Nayar shader (diffuse scatter from a rough surface). Parts of the surface may self-shadow, cause diffuse inter-reflection between multiple microfacets, and catch more light at glancing angles due to small protrusions from the surface.
Viewed from a macro scale, this rough surface flattens the overall lighting, and makes the edges of the object brighter.
I always hear talk of "simplified" shaders that limit parameters to better mimic the real world.. having "specular intensity" actually modify the fresnel response, having the refraction IOR linked to the reflection IOR,, etc..
Well, going by this, if we really wanted to simplify our shader we could get rid of all "diffuse" and "SSS" parameters altogether and simply have one slider to rule them all (or rather, two): Scatter Color and Scatter Distance. This is not very practical or intuitive to use compared to a "diffuse color" slot, how it does drive home the point I am trying to make pretty clearly.
Again, you don’t need to know any of this to create beautiful images.. however, this post is for those who share my interest in the nature of light and materials, and enjoy over-examining the tools that are provided to them.
Thanks for reading!