Post by sv97 on Sept 6, 2019 13:41:06 GMT
Hey guys, I've started implementing the ray tracer last week and am at the point where refraction is added (Chapter 11) and can't get the test "Finding n1 and n2 at various intersections" to pass (and total internal reflection which probably is a result of that).
This is my test failure output - precompute_n1_n2 first has what n1 and n2 should be according to the table and then the actual values. So it fails at the point where n1=n2=2.5 because it produces n2=2.0. Starting this chapter I also had to raise my epsilon a bit to get Chapter 11, Test #4 to pass (Epsilon now is 0.1e-2 for 32-bit floats). Don't know if that's relevant just thought I'd mention it.
failures:
---- scenes::tests::total_internal_reflection stdout ----
thread 'scenes::tests::total_internal_reflection' panicked at 'assertion failed: `(left ≈ right)`
left: `Pixel { r: 1.0, g: 1.0, b: 1.0 }`,
right: `Pixel { r: 0.0, g: 0.0, b: 0.0 }`', src/scenes/tests.rs:413:5
---- shapes::tests::precompute_n1_n2 stdout ----
[src/shapes/tests.rs:338] (n1, n2) = (
1.0,
1.5,
)
[src/shapes/tests.rs:339] (comps.n1, comps.n2) = (
1.0,
1.5,
)
[src/shapes/tests.rs:338] (n1, n2) = (
1.5,
2.0,
)
[src/shapes/tests.rs:339] (comps.n1, comps.n2) = (
1.5,
2.0,
)
[src/shapes/tests.rs:338] (n1, n2) = (
2.0,
2.5,
)
[src/shapes/tests.rs:339] (comps.n1, comps.n2) = (
2.0,
2.5,
)
[src/shapes/tests.rs:338] (n1, n2) = (
2.5,
2.5,
)
[src/shapes/tests.rs:339] (comps.n1, comps.n2) = (
2.5,
2.0,
)
thread 'shapes::tests::precompute_n1_n2' panicked at 'assertion failed: `(left ≈ right)`
left: `2.0`,
right: `2.5`', src/shapes/tests.rs:341:9
failures:
scenes::tests::total_internal_reflection
shapes::tests::precompute_n1_n2
test result: FAILED. 137 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
The code is here Source @ Github. prepare_computations is at the path src/shapes/intersection.rs starting at line 22.
To describe the way I implemented the algorithm in pseudocode:
self <- the intersection that prepare_computations is called on
xs <- A collection of intersections that should also contain self
containers <- empty vector
n1 <- optional value initialized with None // optional value to handle the case where xs is empty or doesn't contain self - that case shouldn't ever occur in a bugfree program
n2 <- optional value initialized with None
for intersection <- xs
i_eq_hit <- true if self is the current element in our iteration, false otherwise
if i_eq_hit
if containers has some last element
n1 <- that last elements materials refractive index
else
0
end_if
end_if
if there is some position in containers, such that the element at that position is the intersection of our current iteration
remove the element at that position in containers
else
append the current iteration's intersection's object to containers
end_if
if i_eq_hit
if containers has some last element
n2 <- that last elements materials refractive index
else
0
end_if
break
end_if
end_for
create a precomputation value with n1 and n2 if they are some values. If they are None crash the program.
And the same code again in rust (I'm guessing it's a bit foreign to most so I tried my best at annotating it):
let mut containers: Vec<&Arc<Shape>> = Vec::new(); // don't think about the Arc here - I've used atomic reference counting for the shapes. It's essentially a reference to some shape
let mut n1 = None;
let mut n2 = None;
for intersection in xs.iter() {
// equivalent to self as *const _ == intersection as *const _ - so comparing if they both point to the same point in memory
// I have to do this a bit weird because of my architecture - I've defined shapes as functions and function pointers require
// special handling for comparisons in rust.
let i_eq_hit = std::ptr::eq(self, intersection);
if i_eq_hit {
// checks if there's a last element in containers (so if it's non empty, if it is it selects the refractive index off that
// elements material - if there's no last element it assigns 1.0.
n1 = Some(
containers
.last()
.map(|o| o.material.refractive_index)
.unwrap_or(1.0),
);
}
// Find the position of the current object in containers
// this tries to pattern match the right value (the first position for that the predicate in the position() call returns true, or none if there is none) to the left side
if let Some(position) = containers
.iter()
.position(|x| std::ptr::eq(x, &&intersection.object))
{
// remove it if it's in there
containers.remove(position);
} else {
// add it if it isn't
containers.push(&intersection.object);
}
// exactly the same code as n1 with an added break
if i_eq_hit {
n2 = Some(
containers
.last()
.map(|o| o.material.refractive_index)
.unwrap_or(1.0),
);
break;
}
}
I'm absolutely not sure if my comparison
i_eq_hit <- true if self is the current element in our iteration, false otherwise
let i_eq_hit = std::ptr::eq(self, intersection);
is what the book actually means to express with if i = hit
. I also tried some variations with checking if it's the result of calling hit on xs but that failed even sooner in the table.
Would appreciate some help here as I've been stuck for two days, thanks