I've been working on my path tracer for a while, and I need some feedback on how to go forward and check if what I've done is correct. Basically what I have is the following (pretty standard):
- generate a camera ray
- for each light ray bounce
--- intersect the ray with the scene
--- calculate the BSDF at the intersection, updating the radiance and generating a new ray according to the BSDF's density function
I've tried to make the path tracer as modular as possible, heavily using classes for primitive types (I've got sphere, plane, triangle, and vertical bounded-open infinitely thin cylinder - the cylinder was mostly for tests and needs to be generalised) and materials (diffuse, specular, plastic, "metal-like", light source, dielectric glass with fresnel reflection, and a few other test materials). I've got a standard perspective camera with a tweakable FOV setting (hooray), I am a little bummed about my scene graph implementation, I seem to be hopeless at implementing octrees, kd-trees, etc... I don't know why but I keep failing miserably. But that's an issue for a later time (sort of).
Now I am mostly wondering, have I actually got the theory right? I've looked online and BSDF's apparently deal with angles and ratios but I fail to understand how one implements that, so this is my interpretation of it (with some help from trusty google):
BSDF (Bidirectional Scattering Distribution Function) -> Input: an incident ray, a normal to the intersected surface. -> Output: a new ray, and an RGB color/emittance pair for the surface (it should be a spectral result but I'm approximating here).
So for instance, a diffuse BSDF would return a random ray within the hemisphere centered on the normal, and return a color equal to the surface's color, and an emittance of zero (since it emits no light). A "light source BSDF" (which makes no sense but standardises the material types), would return a color of zero and an emittance equal to the intensity of the light times its color. Then you get semi-emissive materials, which return nonzero emittance and color (such as a glowy sphere). For glass, you get a BRDF+BTDF (reflection+refraction) combo, which are separated by a random trial controlled by the Fresnel coefficient. And so on... then the color/emittance pairs are combined into a final radiance value which becomes the pixel's color.
This is the results I get on a few scenes:
Standard Cornell Box
Random stuff (the dragon on the yellow background is made of glass)
What do you guys think? Is my BSDF implementation physically correct? Or is it blatanty wrong? I have nothing to compare it to (except the standard cornell box scene but that's the simplest case and only uses a couple materials). And I have no glass dragon or pocket cornell box to test it in real life
Thanks for the feedback and help, because I would like to know if what I've done is correct before moving on to the more complex stuff (more complex models and materials, and perhaps bidirectional path tracing). I can give some code if needed but be warned that it's written in Pascal (but I made sure it was readable so it should be ok).
Also - how should I go about simulating subsurface scattering (which is needed for realistic metal materials)? I was thinking of passing the primitive object to the BSDF so it can calculate limits on where the ray should exit, but that's not very efficient. It is good enough to just approximate it and have the ray exit from a random origin on the surface normal's plane in a small radius?