mmp / pbrt-v4

Source code to pbrt, the ray tracer described in the forthcoming 4th edition of the "Physically Based Rendering: From Theory to Implementation" book.
https://pbrt.org
Apache License 2.0
2.83k stars 440 forks source link

Trouble replicating Radiance results with diffusive materials #431

Closed germolinal closed 3 months ago

germolinal commented 3 months ago

Hello, first of all, thanks very much for this project. I am developing a ray-tracer in Rust, as a component of a larger whole-building simulation project and it would have been impossible without this.

I have been trying to compare some results of my tool with those of Radiance (i.e., the highly validated lighting simulation tool, considered the gold standard for building simulation). I have not been able to do it, and I wonder why. (I am also asking this same question on the Radiance side of things... I might be able to post the solution when I have one)

I am following the PBRT book guidelines. For now, I am using the Simple Path Tracer. Also, in order to debug the integration algorithm, for now, I am working only with diffuse materials. I am also not implementing Russian roulette or any other smarter algorithms to accelerate the calculations.

You can see the core of my integration algorithm HERE.

The results are a perfect match for direct lighting, but not for global illumination. (see the results in the two scenes below).

/* THIS CODE SAMPLES A BSDF OF A LAMBERTIAN */

// Spawn a ray in a new direction
let u = rng.gen(); // get random value
let new_dir = sample_uniform_hemisphere(u); // create a new upward-looking direction
let cos_theta = new_dir.z; // cosine of the angle between UP and new_dir
let pdf = 0.5 / PI; // uniformly distributed, so 1/2*PI
let spectrum = material.colour() / PI; // rho over P for lambertian materials

/****************************/
// THIS 1.07 IS THE HACK... explained below, in the text
beta *= spectrum * cos_theta / (pdf * 1.07);
/****************************/
specular_bounce = false;
let (_, normal, e1, e2) = interaction.get_triad();
ray = Ray3D {
    direction: material.to_world(normal, e1, e2, new_dir), 
    origin: interaction.point + normal * 0.001,
}
Screenshot 2024-06-27 at 1 27 37 PM Screenshot 2024-06-27 at 1 27 50 PM

In global illumination, the throughput of my rays decays much more slowly than Radiance's (beta is 1/rho^n, where n is the bounce, starting at 0).

However, when I hack the algorithm, dividing the sampled BSDF value by 1.07, the results seem to match again. I feel like I have seen the 1.07 value before... it is unrelated to the reflectance of the materials, as there are two scenes, both using Plastic and Metal with no specularity or roughness; but with different colours (0.9 grey, and 0.5 grey).

Screenshot 2024-06-27 at 1 29 38 PM Screenshot 2024-06-27 at 1 29 50 PM

Is there anything you can spot that might be creating this bias? Considering that the direct calculation is correct. I would really appreciate your help.

germolinal commented 3 months ago

PD: I asked Greg Ward—developer of Radiance—and he told me to check my sampling and pdf values for checking the Montecarlo integration. I integrated a hemisphere of value 1, and found the expected answer of Pi; and by integrating a unit overcast sky (i.e., L=(1. + 2. * cos(theta))/3) gives me the expected Pi * 7/9. I guess the error must come from elsewhere.

I'll keep digging!

germolinal commented 3 months ago

All right. I have solved this issue. As it happens, my implementation was correct apparently, but some of the Radiance parameters I was using prevented it from accounting for the last bunch of—low-energy—bounces. This was particularly noticeable in one of my scenes, which had a 90% reflective surface (to take into account these things)... I guess there is a lot of value in my new implementation, based on your work: it has less and more predictable parameters.