|
Post by strace3184 on Nov 22, 2020 17:44:56 GMT
I'm at the end of chapter 10, and I'm experiencing noise when I apply a checkerboard pattern to the planes.
I'm pretty sure this is what in a previous chapter is described as acne, due to floating point errors causing values to inconsistency fall behind and above a threshold.
For example, if translate the floor plane by a small amount, say 0.01 world points, the noise disappears.
The formula I'm using is:
(x.floor() + y.floor() + z.floor()) as i32 % 2 == 0 In order to get a simple test case, it's enough to use only y:
point.y.floor() as i32 % 2 == 0
The noise can be observed from the computations; see this small sample: y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y:-0.0000000000000004440892098501, floor:-1.0000000000000000000000000000, i32:0, mod_is_0:false y:-0.0000000000000004440892098501, floor:-1.0000000000000000000000000000, i32:0, mod_is_0:false y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y: 0.0000000000000000000000000000, floor: 0.0000000000000000000000000000, i32:0, mod_is_0:true y:-0.0000000000000004440892098501, floor:-1.0000000000000000000000000000, i32:0, mod_is_0:false y:-0.0000000000000008881784197001, floor:-1.0000000000000000000000000000, i32:0, mod_is_0:false
note the noise, where `zero:false`.
I can work this around by considering individual values smaller than epsilon to be zero:
let x = if .x.abs() < EPSILON { 0.0 } else { x }; let y = if .y.abs() < EPSILON { 0.0 } else { y }; let z = if .z.abs() < EPSILON { 0.0 } else { z };
(I'm inspecting and optionally resetting the values individually, because otherwise the error would sum)
and then computing as before:
let use_color_a = (x.floor() + y.floor() + z.floor()) as i32 % 2 == 0;
Is this an appropriate way, or there is a simpler/better/more appropriate one?
|
|
|
Post by matthew on Dec 2, 2020 16:11:18 GMT
I had the same problem and my solution was doing the offset of the plane by about 0.01. My walls went from ugly to nice and I stopped investigating because I didn't want to mess with numbers so much. I just now finished chapter 11, came across your post, and went back to undo my plane modifications to see if I could help. You know what? The "acne" problem is now gone. I'm stunned that it went away without me doing anything about it.
|
|
|
Post by twistdroach on Dec 16, 2020 23:54:23 GMT
I don't think I experienced this, but...what language are you working with? Also - do you have a yaml file that reproduces the issue, I'd be happy to give it a try and post the results.
|
|
|
Post by strace3184 on Dec 19, 2020 10:07:55 GMT
I don't think I experienced this, but...what language are you working with? Also - do you have a yaml file that reproduces the issue, I'd be happy to give it a try and post the results. Thanks! that'd be helpeful & interesting :-)
I'll post the data in a couple of days (I need to go back in history and verify it). I did't use YAML, but I think it should be easy to compose one manually (the scene is trivial, I think I may even reproduce it with a single polygon).
|
|
|
Post by strace3184 on Dec 23, 2020 15:11:44 GMT
I don't think I experienced this, but...what language are you working with? Also - do you have a yaml file that reproduces the issue, I'd be happy to give it a try and post the results. Hello again! I don't have a YAML file, but the configuration is trivial (plane + light + camera), so that it can be very easily reproduced.
This is an extraction of the minimal test case, from whom you can extract the properties; the plane will display acne (assuming the problem will reproduce on your codebase as well):
const SCREEN_WIDTH: u16 = 300; // height is half
fn prepare_world() -> World { let light_source = PointLight::new((-10, 10, -10), (1, 1, 1));
let pattern = Box::new(CheckersPattern { color_a: COLOR_WHITE, color_b: COLOR_BLACK, ..CheckersPattern::default() });
let material = Material { pattern, ..Material::default() };
let floor = Arc::new(Plane { material, ..Plane::default() });
World { objects: vec![floor], light_source, } }
fn prepare_camera() -> Camera { let mut camera = Camera::new(SCREEN_WIDTH, SCREEN_WIDTH / 2, PI / 3.0);
camera.transform = Matrix::view_transform( &Tuple::point(0, 1.5, -5), &Tuple::point(0, 1, 0), &Tuple::vector(0, 1, 0), );
camera }
For reference, this is the pattern main code:
// point: In pattern space. // fn current_color_at(&self, point: &crate::math::Tuple) -> Color { let approximated_floors_sum = point.x.floor() + point.y.floor() + point.z.floor();
if approximated_floors_sum as i32 % 2 == 0 { self.color_a } else { self.color_b } }
|
|
|
Post by jhigginbotham64 on Jan 21, 2021 23:34:44 GMT
Hey strace3184 , thanks for asking this question. I have a similar issue, but I happen to really like your "workaround" and have started using it.
|
|
|
Post by jhigginbotham64 on Jan 21, 2021 23:52:03 GMT
Found this answer after a bit more digging around: forum.raytracerchallenge.com/post/237/threadIn your shade_hit function, try passing comps.over_point to the lighting function instead of comps.point. I made that change and removed your workaround, and the acne is still gone.
|
|