Post by mallocman on Dec 23, 2021 21:17:12 GMT
Hello,
First, I'd like to thank the author for this truly excellent book. I am teaching myself to code and it is difficult to find tutorials for substantive projects that don't give the entirety of the project away. This book is a model for what a coding tutorial should be.
Second, I'm having some trouble with the resolution of the black-band-inside-hollow-glass-sphere issue. My code currently outputs the image in the attachment.
My code is here and I will extract some of the potentially more relevant aspects:
fn schlick(&self) -> f64 {
let mut cos = self.eyev * self.normalv;
if self.n1 > self.n2 {
let n = self.n1 / self.n2;
let sin2_t = n.powi(2) * (1.0 - cos.powi(2));
if sin2_t > 1.0 {
return 1.0;
}
cos = (1.0 - sin2_t).sqrt();
}
let r0 = ((self.n1 - self.n2) / (self.n1 + self.n2)).powi(2);
r0 + (1.0 - r0) * (1.0 - cos).powi(5)
}
fn shade_hit(&self, comps: Computations, remaining: usize) -> Color {
let shadowed = self.is_shadowed(comps.over_point);
let clr = lighting(comps.object.get_material(), comps.object, self.lights[0], comps.over_point, comps.eyev, comps.normalv, shadowed);
let reflections = self.reflected_color(comps, remaining);
let refractions = self.refracted_color(comps, remaining);
let object = comps.object;
if object.get_reflective() > 0.0 && object.get_transparency() > 0.0 {
let reflectance = comps.schlick();
clr + (reflections * reflectance) + (refractions * (1.0 - reflectance))
} else {
clr + reflections + refractions
}
}
fn reflected_color(&self, comps: Computations, remaining: usize) -> Color {
if remaining < 1 {
return black();
}
let reflective = comps.object.get_reflective();
if reflective == 0.0 {
return black();
}
let ray = Ray::new(comps.over_point, comps.reflectv);
let clr = self.color_at(ray, remaining - 1);
clr * reflective
}
fn refracted_color(&self, comps: Computations, remaining: usize) -> Color {
if comps.object.get_transparency() == 0.0 || remaining == 0 {
return black();
}
let n_ratio = comps.n1 / comps.n2;
let cos_i = comps.eyev * comps.normalv;
let sin2_t = n_ratio.powi(2) * (1.0-(cos_i.powi(2)));
if sin2_t > 1.0 {
return black();
}
let cos_t = f64::sqrt(1.0 - sin2_t);
let direction = comps.normalv * ((n_ratio * cos_i) - cos_t) - comps.eyev * n_ratio;
let refract_ray = Ray::new(comps.under_point, direction);
self.color_at(refract_ray, remaining - 1) * comps.object.get_transparency()
}
}
It seems like the issue may be that my Schlick function is returning a value that is too low for the pixels that make up that black band. When I set reflectance to 1.0 instead of comps.schlick(), the band goes away, although that of course messes up the rest of the render in various ways. So it seems like my Schlick function is not performing correctly, but I've compared mine to a working Rust implementation here and am somewhat at a loss as to what could be the difference between my Schlick function and that one.