Open 0xCache opened 1 year ago
Tempted to close this as "by design". From the book, with my emphasis added:
The bigger the fuzz sphere, the fuzzier the reflections will be. This suggests adding a fuzziness parameter that is just the radius of the sphere (so zero is no perturbation). The catch is that for big spheres or grazing rays, we may scatter below the surface. We can just have the surface absorb those.
I agree that this is not an ideal solution, but I inclined to think that formally covering a possible fix for this isn't worth it.
Hmmm, perhaps we should just put more guardrails around the fuzz factor. Current code:
metal(const color& albedo, double fuzz) : albedo(albedo), fuzz(fuzz < 1 ? fuzz : 1) {}
Suggested update:
metal(const color& albedo, double fuzz) : albedo(albedo), fuzz(fmin(fabs(fuzz), 0.99)) {}
Nope, the above won't work, since the "fuzz sphere" will more easily intersect with the surface for grazing rays. This is the purpose of the return (dot(scattered.direction(), rec.normal) > 0)
statement at the end of metal::scatter()
.
The problem is that although only returning when not pointing into the sphere works, some energy carried by the rays is lost when rays are absorbed, resulting in a darker colour. So I choose to make the absorbed rays point out. The code first uses a dot product, and returns the scattered ray if the product is greater than 0. If not, return the tangent.
double scale = dot(hit.normal, reflected);
Vec direction = dot(reflected + fuzz, hit.normal) > 0 ? reflected + fuzz : reflected + (scale- 1e-5) * fuzz;
A draft:
Hi, I'm following the first book's guide and noticed some parts around the fuzzy metal hittable sphere are darker than the center. I suspected it's because when the reflected vector is closer to the surface, where dot(hit.normal, reflected) is closer to 0, a random unit vector multiplied by a big fuzz will make the final scattered ray point inside the sphere. I added a dot check, multiplied the fuzz vector by the dot product, and solved the problem. Before:
After: