|
Post by ajbdev on Jun 2, 2022 7:28:23 GMT
I've been stumped on this for almost a week and I'm not sure what else to try. Currently, I'm through Chapter 11 all the way to refraction, but I have two failing tests. After double checking my code and the book and even looking at other Ray Tracer Challenge codebases online, I can't figure out for the life of me what my error is. (Code is in crystal lang, but anyone familiar with ruby should be comfortable reading it) Failures:
1) World #shade_hit with a reflective material
Failure/Error: w.shade_hit(comps).should eq Color.new(0.87677, 0.92436, 0.82918)
Expected: #<Color:0x7f27510c7cf0 @tuple={0.87677, 0.92436, 0.82918, 1.0}>
got: #<Color:0x7f27510c7d20 @tuple={0.17, 0.17500000000000002, 0.165, 1.0}>
# spec/world_spec.cr:143
2) World #reflected_color the reflected color for a reflective material
Failure/Error: w.reflected_color(comps).should eq Color.new(0.19032, 0.2379, 0.14274)
Expected: #<Color:0x7f27510c7b70 @tuple={0.19032, 0.2379, 0.14274, 1.0}>
got: #<Color:0x7f27510c7ba0 @tuple={0.07, 0.07500000000000001, 0.065, 1.0}>
# spec/world_spec.cr:173
Finished in 254.29 milliseconds
171 examples, 2 failures, 0 errors, 0 pending Here's the source: github.com/ajbdev/crystalraytracerI can't figure out why these tests result in darker colors than expected for reflected colors. When I render a basic scene without any patterns applied I don't see anything too obvious that stands out as wrong (see attached). Attachments:
|
|
|
Post by Jamis on Jun 17, 2022 19:56:10 GMT
This kind of bug can be maddening! I don't see anything obviously wrong with your code, either, so let's try this. I ran that same test with my ray tracer, and had it dump some information in the "color_at" method. Here's what it says about the state of the ray tracer at that point (where the ray intersected the sphere):
color: v(0.3807,0.4759,0.2855) t: 1.4142135694448705 point: p(0.0000,0.0001,-1.0000) inside: false eyev: v(-0.0000,-0.7071,-0.7071) normalv: v(0.0000,0.0001,-1.0000) reflectv: v(0.0000,0.7072,-0.7070) over point: p(0.0000,0.0001,-1.0001) under point: p(0.0000,0.0001,-0.9999)
I also had the "shade_hit" method dump some state. It gets called twice during that test, and the output of each call is:
--- FIRST (the spawned reflected ray) --- base color: v(0.3807,0.4759,0.2855) reflected: v(0.0000,0.0000,0.0000) refracted: v(0.0000,0.0000,0.0000) final: v(0.3807,0.4759,0.2855)
--- SECOND (the shade_hit call from the test) --- base color: v(0.6864,0.6864,0.6864) reflected: v(0.1903,0.2379,0.1428) refracted: v(0.0000,0.0000,0.0000) final: v(0.8768,0.9244,0.8292)
The FIRST shade_hit call is the recursive call, when the spawned ray strikes the sphere ("s1") in the default world. SECOND is the outermost shade_hit call, the one invoked from the test itself, which intersects the plane.
Do you get numbers similar to those when you run the test? Maybe any differences will help point at where the problem lies.
|
|
|
Post by ajbdev on Jun 18, 2022 20:10:27 GMT
Here's what I get for the sphere intersection:
comps.serialize # => {"@t" => "1.4142418471307924e-5", "@object" => "#<Sphere:0x100760a20>", "@point" => "p(0.0,-2.00002000033287e-5,-0.999999999799996)", "@eye_v" => "v(0.0,0.7071067811865476,-0.7071067811865476)", "@normal_v" => "v(0.0,-2.00002000033287e-5,-0.999999999799996)", "@inside" => "false", "@over_point" => "p(0.0,-2.0000400005328734e-5,-1.000009999799994)", "@under_point" => "p(0.0,-2.0000000001328664e-5,-0.999989999799998)", "@reflect_v" => "v(0.0,-0.7071350651749401,-0.7070784960667617)", "@n1" => "", "@n2" => ""} And the state at the first shade_hit:
surface.to_s # => "rgb(0.08000000000000002,0.1,0.06)" reflected.to_s # => "rgb(0.0,0.0,0.0)" refracted.to_s # => "rgb(0.0,0.0,0.0)" final.to_s # => "rgb(0.08000000000000002,0.1,0.06)" Second shade_hit:
surface.to_s # => "rgb(0.1,0.1,0.1)" reflected.to_s # => "rgb(0.04000000000000001,0.05,0.03)" refracted.to_s # => "rgb(0.0,0.0,0.0)" final.to_s # => "rgb(0.14,0.15000000000000002,0.13)" I had a third shade_hit, too, I'm not sure if this is an error, but these are the final values that get evaluated in the test:
surface.to_s # => "rgb(0.1,0.1,0.1)" reflected.to_s # => "rgb(0.07,0.07500000000000001,0.065)" refracted.to_s # => "rgb(0.0,0.0,0.0)" final.to_s # => "rgb(0.17,0.17500000000000002,0.165)" Ignoring the shade hit results for a moment - the eyev, normalv, underpoint, and overpoint all seem pretty wildly off, despite those tests independently passing. This gives me a lead to start tracking down though, so thank you for that - if there's anything you see that sticks out to you I'd love to know, but otherwise I will dig into this further. Thank you! Once I get the culprit figured out I'll share it here.
|
|
|
Post by ajbdev on Jun 19, 2022 1:33:31 GMT
Hmmmm, it looks like the intersection with the sphere that is being returned as the hit is the other side of the sphere. t = 1.41E-05 qualifies as the closest intersection of the sphere in my implementation, but it looks like yours returns the other side.
The T value of the other sphere intersection is 1.4141994199546228, which is off from yours by a really small amount (-0.000014149490248). If I added that difference to my T value of the other side of the sphere, the value would be under the Epsilon value I'm using and would no longer evaluate as a visible intersection. It seems like the origin of the sphere is slightly off.
|
|