|
Post by tirrho on Aug 9, 2019 6:15:00 GMT
Hello! I am enjoying your book a lot! Right now I'm stuck at test number 4 in reflection testing.
My tests are showing this: The picture I am getting now looks like this: (I think the acne comes from the failing tests, since it started just now.) I am not sure where to even start debugging this. I've tried stepping through the debugger showing values as they are created, but I cannot seem to understand why the test isn't passing. The only thing I can think of is floating point rounding errors, which seem to trip up others on the forum as well.
This is what I am getting for comps:
:t = 1.4142135623730951 :object = { :id 3, :transform [ [ 1 0 0 0 ] [ 0 1 0 -1 ] [ 0 0 1 0 ] [ 0 0 0 1 ] ], :material { :color [ 1 1 1 ], ... }, :Shape :Plane } :point = [ 0.0 -1.0000000000000002 -1.9999999999999998 1.0 ] :eyev = [ 0 0.7071067811865476 -0.7071067811865476 -0.0 ] :normalv = [ 0 1 0 0.0 ] :inside? = false :reflectv = [ 0.0 0.7071067811865476 0.7071067811865476 0.0 ] :over-point = [ 0.0 -0.9999000000000002 -1.9999999999999998 1.0 ]
And this is the intersection I get from running the test:
:t = 1.4142135623730951 :object = { :id 3, :transform [ [ 1 0 0 0 ] [ 0 1 0 -1 ] [ 0 0 1 0 ] [ 0 0 0 1 ] ], :material { :color [ 1 1 1 ], ... }, :Shape :Plane }
My Plane looks like:
:id = 3 :transform = [ [ 1 0 0 0 ] [ 0 1 0 -1 ] [ 0 0 1 0 ] [ 0 0 0 1 ] ] :material = { :color = [ 1 1 1 ] :ambient = 0.1 :diffuse = 0.9 :specular = 0.9 :shininess = 200 :reflective = 0.5} :Shape = :Plane
And my world looks like this:
:world-objects = [
{ :id 1, :transform [ [ 1 0 0 0 ] [ 0 1 0 0 ] [ 0 0 1 0 ] [ 0 0 0 1 ] ], :material { :color [ 0.8 1.0 0.6 ], ... }, :Shape :Sphere }
{ :id 2, :transform [ [ 0.5 0 0 0 ] [ 0 0.5 0 0 ] [ 0 0 0.5 0 ] [ 0 0 0 1 ] ], :material { :color [ 1 1 1 ], ... }, :Shape :Sphere }
{ :id 3, :transform [ [ 1 0 0 0 ] [ 0 1 0 -1 ] [ 0 0 1 0 ] [ 0 0 0 1 ] ], :material { :color [ 1 1 1 ], ... }, :Shape :Plane }
] :world-light = { :position [ -10 10 -10 1.0 ], :intensity [ 1 1 1 ] } Lastly, my reflected-color:
(defn reflected-color "Color for a reflective material. Takes world and computations, returns color" [w comps] (if-let [reflect-value (zero? (get-in comps [:object :material :reflective]))] (color 0 0 0) (let [reflect-ray (ray (:over-point comps) (:reflectv comps)) col (color-at w reflect-ray)] (multiply-color col (get-in comps [:object :material :reflective])))))
Should I round to 4 or 5 digits for all tuples/colors/matrices on creation? Right now I use epsilon value of 0.0001 for comparison in the function checking equality. I'd be happy to provide you with more information if needed. Thank you in advance!
|
|
|
Post by Jamis on Aug 9, 2019 14:52:07 GMT
Thank you for the detailed report! I am able to confirm that, for the test titled "The reflected color for a reflective material", I get the same comps values as you do, so that's reassuring.
I've added some more numbers from my own ray tracer. When I run that test, my "reflected_color" function computes the following values (renamed to match the variable names in your own implementation):
reflect-ray origin = p(0.0000,-0.9999,-2.0000) reflect-ray direction = v(0.0000,0.7071,0.7071) col = v(0.3807,0.4759,0.2855) result of multiply-color = v(0.1903,0.2379,0.1428)
Some of these may seem to duplicate numbers you previously reported from comps, but one of the things I'm trying to confirm with this is that none of the values have "mysteriously" drifted since they were computed. What numbers do you get there?
|
|
|
Post by tirrho on Aug 10, 2019 13:10:02 GMT
Hello!
Thanks for your reply!
I've tried to do some digging, stepping through with the debugger. I've also made sure my code now completely adheres to the first test, not the one where we change the shade-hit function. The return value at the end is now:
[0.05 0.05 0.05] During stepping through, the values I get from your suggested variables are:
reflect-ray origin = p(0.0000,-0.9999,-1.9999) reflect-ray direction = v(0.0000,0.7071,0.7071) col = v(0.1 0.1 0.1) result of multiply-color = v(0.05 0.05 0.05)
:inside? false
My guess is that the lighting function is at fault. The values returned to col seem to look like the original color divided by ambient, then divided by reflective property. When I try to change these two properties the resulting values are:
:ambient = 0.66
:reflective = 0.3
col = v(0.66 0.66 0.66) result of multiply-color = v(0.198 0.198 0.198) Probably something is wrong with the logic in my lighting function when computing the resulting values. I added the inside? property because I wrote the logic for the first clause like this:
...
(if (or (< light-dot-normal 0) in-shadow) (reduce add-colors [(color 0 0 0) (color 0 0 0) ambient])
...
I'll add my lighting function for context. It is still quite dirty, but I didn't figure out a way to do all these computations in the let blocks while keeping things clean
(defn lighting [mat object light point eyev normalv in-shadow] (let [col (if (contains? mat :pattern) (pattern-at-shape (:pattern mat) object point) (:color mat)) effective-color (hadamard-product col (:intensity light)) lightv (normalize (sub-tuples (:position light) point)) ambient (multiply-color effective-color (:ambient mat)) light-dot-normal (dot lightv normalv)] (if (or (< light-dot-normal 0) in-shadow) ambient (let [diffuse (multiply-color effective-color (* (:diffuse mat) light-dot-normal)) reflectv (reflect (negate-tuple lightv) normalv) reflect-dot-eye (dot reflectv eyev)] (if (<= reflect-dot-eye 0) (add-colors ambient diffuse) (let [factor (Math/pow reflect-dot-eye (:shininess mat)) specular (multiply-color (:intensity light) (* (:specular mat) factor))] (reduce add-colors [diffuse specular ambient])))))))
I'll also add in color-at, intersect-world and shade-hit
(defn color-at [world ray] (let [xs (intersect-world world ray) h (hit xs)] (if (empty? h) (color 0 0 0) (let [comps (prepare-computations h ray)] (shade-hit world comps)))))
(defn intersect-world [world ray] (->> (:world-objects world) (map #(intersect % ray)) (apply concat) (sort-by #(:t %)) (vec)))
(defn shade-hit [world comps] (let [shadowed (shadowed? world (:over-point comps)) surface (lighting (:material (:object comps)) (:object comps) (:world-light world) (:over-point comps) (:eyev comps) (:normalv comps) shadowed) ] surface))
I hope this provides some more information. Thanks so far!
|
|
|
Post by tirrho on Aug 10, 2019 19:20:50 GMT
I was able to get the correct color, finally! However, I am only able to get the correct value when I use :point for the reflect-ray, not the :over-point, and hard-code false in for the shadowed? passed to lighting.
This makes other tests from earlier fail, but gets me one step closer to succeeding.
How important is it to use :over-point rather than :point, as you mention as important in the book?
Thanks
|
|
|
Post by Jamis on Aug 27, 2019 15:20:14 GMT
Sorry! I missed your post, tirrho. Regarding your question, I'd say it is pretty fundamentally important to use over-point, rather than point, as mentioned in the book. If you're getting the correct value with point and not over-point, it suggests you might be miscomputing over-point, or at the very least, using it incorrectly in your code. Given that I confirmed the value of the over-point you gave in your initial post, it suggests maybe your use of the value may be off. What is the value of your computed lightv vector in the lighting function?
|
|
|
Post by citizen428 on Feb 14, 2020 14:41:32 GMT
Sorry for resurrecting such an old thread, but I seem to be having similar problems in my F# implementation, a lot of the values here seem familiar.
My `reflectRay` function seems correct, it produces the `reflectRay` you describe above Jamis. Things seem to go wrong at `colorAt`, where I get back a `None` from `Intersection.hits`, which means black is returned.
I'm quite confused by this, as everything worked perfectly so far and all other unit tests are passing.
|
|
|
Post by Jamis on Feb 20, 2020 4:51:33 GMT
citizen428 --- my go-to when debugging issues like this is to restrict the render to a single pixel of the image, specifically one where (for example) I would expect the reflected ray to strike something. Then I step through (either with a debugger, or with print statements) checking all of my assumptions at each step and making sure each value is what I expect. In extreme cases, I might even work through the math for a single pixel by hand, and then compare the results with what I'm getting in the ray tracer. I'm not sure what else to recommend--I know it's not exactly glamorous work, but the bugs in a ray tracer can be extremely subtle, sometimes! Please let me know if you have any specific questions and I'll see what I can do.
|
|
|
Post by citizen428 on Feb 20, 2020 9:14:08 GMT
Thanks Jamis, that's probably what I'll end up doing. I just thought it's interesting that I seem to have pretty much the same problem as described in this thread.
|
|
|
Post by poster on Apr 20, 2020 16:56:48 GMT
Sorry to bump this thread, but I think I am having the exact same problem as the OP, including the issue where using point instead of over_point "fixes" the issue. I have been trying to debug this issue over the past few days, but I am not getting anywhere. Is there something obvious I am doing wrong that I just can't see? What am I missing here?
Here are some of the values I am seeing in my program: Point: Tuple { x: 0.0, y: -1.0000000000000002, z: -1.9999999999999998, w: 1.0 } Over point: Tuple { x: 0.0, y: -0.9999900000000003, z: -1.9999999999999998, w: 1.0 }
expected color: `Color { red: 0.19032, green: 0.2379, blue: 0.14274 }` actual color: `Color { red: 0.05, green: 0.05, blue: 0.05 }`
|
|
|
Post by poster on Apr 21, 2020 17:31:13 GMT
Hey, I was able to figure out my issue, so please disregard the above. Thanks!
|
|
|
Post by metaljoe on May 26, 2020 7:54:39 GMT
Looks like I'm not alone! How did others fix this?
I can get the expected result if I use point, not over_point, but I know this isn't the correct approach. I've checked and re-checked the test and the code multiple times over the last couple of days.
Fails with over_point:
over_point: Tuple { x: 0.0, y: -0.9999000000000002, z: -1.9999999999999998, w: 1.0 } reflect_ray.origin: Tuple { x: 0.0, y: -0.9999000000000002, z: -1.9999999999999998, w: 1.0 } reflect_ray.direction: Tuple { x: 0.0, y: 0.7071067811865476, z: 0.7071067811865476, w: 0.0 } => Colour { red: 0.05, green: 0.05, blue: 0.05 }
Passes with point:
point: Tuple { x: 0.0, y: -1.0000000000000002, z: -1.9999999999999998, w: 1.0 } reflect_ray.origin: Tuple { x: 0.0, y: -1.0000000000000002, z: -1.9999999999999998, w: 1.0 } reflect_ray.direction: Tuple { x: 0.0, y: 0.7071067811865476, z: 0.7071067811865476, w: 0.0 } => Colour { red: 0.19032, green: 0.2379, blue: 0.14274 }
EPSILON is 0.0001 as this worked fine for previous tests and test renderings. Changing to 0.00001, which is sometimes also used in the book, results in both point and over_point failing the test.
|
|
fremag
Junior Member
Posts: 73
|
Post by fremag on Jun 3, 2020 11:29:27 GMT
Hi,
If it's not too late, here are some values I get in this test. Before the first call to ReflectedColor: the hit object is the plane and overpoint / reflected ray have the same values as you have.
reflectRay:
Origin: X: 0 Y: -0.9999900000000003 Z: -1.9999999999999998 W: 1 Direction: X: 0 Y: 0.7071067811865476 Z: 0.7071067811865476 W: 0
Then I'm looking the color given by the reflected ray (using over point) A new intersection is computed to get the color: the ray hit the sphere
So we must get the color of sphere at the new intersection point. There is no new reflection or refraction on the sphere so the color is the classic color given by Phong model. Color: R: 0.38066440299018867 G: 0.47583050373773583 B: 0.2854983022426415
Then this color is multiplied by the plane's reflective value (0.5)
so the final color is : R: 0.19033220149509433 G: 0.23791525186886792 B: 0.14274915112132075
|
|
|
Post by metaljoe on Jun 9, 2020 10:04:07 GMT
Thanks. I've narrowed it down to my is_shadowed() returning false for point and true for over_point, which then affects the final colour. For point, the ray calculated in the function generates an intersection hit, whereas over_point does not.
The ray for point: Ray { origin: Tuple { x: 0.0, y: 0.0, z: -1.0000999999999995, w: 1.0 }, direction: Tuple { x: -0.5965518969287772, y: 0.5965518969287772, z: -0.5368907417169302, w: 0.0 } }
For over_point: { origin: Tuple { x: 0.0, y: -0.0001, z: -1.0000999999999995, w: 1.0 }, direction: Tuple { x: -0.5965497739554088, y: 0.5965557394531482, z: -0.5368888310621283, w: 0.0 } }
I'll keep looking.
|
|
dr
New Member
Posts: 2
|
Post by dr on Jul 26, 2021 23:11:55 GMT
I'm running into an issue with this test as well, but I think my code may be working correctly. so the final color is : R: 0.19033220149509433 G: 0.23791525186886792 B: 0.14274915112132075 These are the exact values I'm getting, but the issue is the values specified in the book for the test are different enough to fail the EPSILON check. Will it be ok if I change R from 0.19032 to 0.19033 and G from 0.2379 to 0.23791, or is there an underlying issue I need to track down?
|
|
|
Post by Jamis on Jul 27, 2021 1:53:33 GMT
Hey dr, if it's that close, I'd say that's still within some epsilon. Unless you're seeing other failures elsewhere by a larger margin, I don't think you need to attribute this to a bug in your code. You may just need to adjust the expected values in your tests, in this case.
|
|