
Post by guambler on Mar 30, 2020 1:52:51 GMT
Hello, I've gotten through The Ray Tracer Challenge, with only minor bumps along the way...until Transparency and Refraction. I can't pass Test #8 Handling Refraction in shade_hit. I've passed the other Tests #1  #7
The test seems to fail because the Refraction Ray from my Plane misses the Red Ball. (the discriminant is ~11.16). Can anyone help rubber duck this problem?
Here is my #8 Test: // Handling Refraction in ShadeHit World* world = World::DefaultWorld();
BlinnPhong* planeMaterial = new BlinnPhong(); planeMaterial>SetTransparency(0.5f); planeMaterial>SetRefractiveIndex(1.5f); Matrix4x4 planeTransform = Matrix4x4::Translation(0, 1, 0); Plane* plane = new Plane(planeMaterial, &planeTransform);
BlinnPhong* ballMaterial = new BlinnPhong(Canvas::Red); ballMaterial>SetAmbient(0.5f); Sphere* ball = new Sphere(ballMaterial, &Matrix4x4::Translation(0, 3.5f, 0.5f));
world>AddShape(plane); world>AddShape(ball);
Ray ray = Ray(Tuple::Point(0, 0, 3), Tuple::Vector(0, TWO_SQRT_OVER, TWO_SQRT_OVER));
Intersection* intersect = new Intersection(TWO_SQRT, plane, &ray); IntersectionList list = IntersectionList(); list.AddIntersection(intersect); list.Sort(); list.PrepareRefractionComputations();
Tuple refract_Test = intersect>ShadeHit(world, world>GetLight(0), 5); Tuple refreact_Answer = Tuple::Vector(0.93642f, 0.68642f, 0.68642f); bool refract_Passed = Math::IsEqualColor(&refract_Test, &refreact_Answer);
Here is a snippet of GetRefractedColor():
double n_ratio = intersect>GetN1() / intersect>GetN2(); double cos_i = Math::DotProduct(&intersect>GetViewDir(), &intersect>GetHitNormal()); double sin2_t = (n_ratio*n_ratio) * ( 1  (cos_i*cos_i) ); bool isTotalInternalReflection = sin2_t > 1;
if (isTotalInternalReflection) { return Canvas::Black; } else { recursionsLeft; double cos_t = sqrt(1  sin2_t); Tuple direction = intersect>GetHitNormal() * (n_ratio * cos_i  cos_t)  (intersect>GetViewDir() * n_ratio); Ray refractRay = Ray(intersect>GetUnderPos(), direction); IntersectionList list = IntersectionList(); Tuple refractColor = world>ColorAt(&list, &refractRay, recursionsLeft) * transparency; return refractColor; }
Thank you so much


fremag
Junior Member
Posts: 73

Post by fremag on Mar 30, 2020 7:59:33 GMT
Hi, I don't see any errors in your code or test setup. When computing the refracted color, the refracted ray hits my red sphere (the discriminant is 3.9166) Here is my refracted ray in world coordinate: Origin: X: 0 Y: 1 Z: 2 W: 1 Direction: X: 0 Y: 0.881917103688197 Z: 0.4714045207910317 W: 0 And the ray when checking intersection with the red sphere: Origin: X: 0 Y: 2.5 Z: 1.5 W: 1 Direction: X: 0 Y: 0.8819171190261841 Z: 0.4714045226573944 W: 0 Then a=1, b = 5.823, c = 7.49999 and b²4ac = 3.91 Hope this helps
More values: nRatio: 0.666666 cosI: 0.70710678118654757 sin2t: 0.22222222222222215 cost: 0.88191710368819698 eyeVector / viewDir: X: 0 Y: 0.4714045207910317 Z: 0.4714045207910317 W: 0
nomal: X: 0 Y: 0.4105125828971653 Z: 0 W: 0
direction: X: 0 Y: 0.881917103688197 Z: 0.4714045207910317 W: 0 under position: X: 0 Y: 1.0000100000000003 Z: 1.9999999999999998 W: 1



Post by guambler on Mar 30, 2020 16:16:55 GMT
Oh thank you SO much. Those output values are SUPER helpful! My values, more or less, align with yours (barring Epsilon differences). It falls apart once I reach the Red Sphere's LocalIntersect() test.
// For Refracted Color
n_ratio = 0.6666 cos_i = 0.7071067 cos_t = 0.881917
// For Refracted Ray
direction = {x = 0.00000000000000000, y = 0.88191709937580598, z = 0.47140451272328693, w = 0} underPos = {x = 0.00000000000000000, y =1.0000999657689320, z =2.0000000342285418, w = 1}
// For Ray in Red Sphere's Object Space origin = {x=0.00000000000000000 y=2.4999000342310680 z=1.5000000342285418, w = 1}
direction = {x=0.00000000000000000, y=0.88191709937580598, z=0.47140451272328693, w = 0}
// From Red Sphere Local Intersect (where things diverge...) sphereToRay = {x = 0.00000000000000000, y = 5.9999000342310680, z = 1.0000000342285418, w = 0}
A = 0.99999998478731489 B = 11.525637927185278
C = 35.998800489223058 discriminant = 11.154870137766835
I'd be shocked if my Ray Tracer was able to get this far with a major bug in my Sphere Intersection calculation. Here is code for it anyway
// Calculate Discriminant Tuple sphereToRay = objectRay>origin  GetTransform()>GetPosition(); const double A = Math::DotProduct(&objectRay>direction, &objectRay>direction); const double B = 2 * Math::DotProduct(&objectRay>direction, &sphereToRay); const double C = Math::DotProduct(&sphereToRay, &sphereToRay)  1; const double discriminant = (B*B)  (4 * A * C);
Once again, I really appreciate the help


fremag
Junior Member
Posts: 73

Post by fremag on Mar 30, 2020 20:15:25 GMT
I think the problem is that you apply the transform matrix twice: You have:
// For Ray in Red Sphere's Object Space origin = {x=0.00000000000000000 y=2.4999000342310680 z=1.5000000342285418, w = 1}
and then: // From Red Sphere Local Intersect (where things diverge...) sphereToRay = {x = 0.00000000000000000, y = 5.9999000342310680, z = 1.0000000342285418, w = 0}
I compute the discreminant with the first "origin" point, I don't know why you have another one. See the matrix is applied twice: y = 2.5 is transformed into 6 (+3.5) and z = 1.5 becomes 1 (+0.5) This is the translation applied in the test: Sphere* ball = new Sphere(ballMaterial, &Matrix4x4::Translation(0, 3.5f, 0.5f));



Post by guambler on Mar 31, 2020 0:08:13 GMT
sphereToRay is the Vector from the sphere's center, to the ray origin. I just realize now that I've been subtracting the sphere's world position this entire time Despite that...my Spheres have been working this entire time. That's crazy that I was able to go on this long without running into a problem sooner. My reflections have looked just as good as the book (attached image), for example.
Sure enough, if I simply convert objectRay.origin into a vector (setting w to 0) Test #8 passes. In fact, all my previous tests pass as well with that change. I'm going to perform some more renders ton ensure everything else is working.
You helped solve my issue freeMag, I really appreciate it!!
Attachments:



Post by guambler on Mar 31, 2020 5:51:43 GMT
Oh darn. The tests are passing. Even the Fresnsl Effect ones at the end of the chapter. Even though the tests pass though, Refraction does NOT visually work for me.
Stepping through my code, my Sphere is Intersecting with itself EVERYTIME. Obviously, I need Recursive GetRefractionColor to intersect the other shapes in the room.
I'm wondering if this has something to do with the Determining n1 and n2 portion. Even though that Refracition Test passes (all six indices, output proper n1, n2), I was quite confused if I missed something in that portion.
My code logic
1: calls ColorAt() 2: Iterates though each Shape in the scene with Intersecting the current Ray.
3: puts all intersections into List during iteration, and Sorts them 4: Run PrepareComputations on ALL the intersections to determine their n1, n2 values 5: "The Actual Hit" is then determined by the Intersection with the lowest positive hit distance 6: The selected Intersection's computation values are used for ShadeHit() 7: As ShadeHit() calculate color value, ColorAt() has chance to be recursivly called, starting process over
Other than keeping tracking of how many recursions occurred, I do not pass any data from previous ColorAt() to current function call (for example new IntersectionList is created everytime).
I'm wondering, if there's an explicit way we're supposed to make sure that my Sphere does NOT fire a ray and hit itself again (I thought thats what under_point, over_point are for)
I'm sorry if that's not a lot of information to go off of. Hopefully, something I'm doing is painfully wrong. Otherwise, I'm going to reexamine n1, n2 prepare_computations portion. Can someone verify what is "the hit" on page 153 referring to?
Thank you!
Attachments:


fremag
Junior Member
Posts: 73

Post by fremag on Mar 31, 2020 5:59:00 GMT
I agree: if one unit test fails, you're sure you have a bug but if none fails, you may still have a nasty bug. That's why it's necessary to test at another scale with some basic / complex scenes where you know the result. The chapter 11 about refraction was hard for mee too, I think things are easier after this one because you don't modify things but you only add features. Or, while you are in this part of the code, you can try the bonus chapter about soft shadows, it gives some nice results so it's worth it. www.raytracerchallenge.com/bonus/arealight.html


fremag
Junior Member
Posts: 73

Post by fremag on Mar 31, 2020 11:49:21 GMT



Post by guambler on Mar 31, 2020 14:19:52 GMT
Hey FreeMag, I dont have a GitHub repo setup, but I'll definitive take a look at the other forum thread. Thank you SO much for sharing your version with me, I'll for sure take a look at that too! My plan this morning, was to try and create the simplest test case, and focus on direct transparency (no bending light rays). That'll help me narrow down



Post by guambler on Mar 31, 2020 19:25:52 GMT
Alright, after hours of debugging, I FINALLY solved it! My RayTracer had TWO issues.
One: as I mentioned before, refraction kept recursively intersecting the SAME object. Turns out, when I was computing the Intersection, I was setting the under_point/over_point BEFORE flipping the Normal (causing the refraction ray to keep bouncing back and forth)
Two: I MISPLACED the brackets when calculating the refraction direction. (n_ratio * (cos_i  cos_t)) instead of ((n_ratio * cos_i)  cos_t)
Yet my tests were passing....I guess my problem is that when checking if my tests pass, I was rounding the values so that (0.04725, 0.05) would pass. However, those thousandth decimal places mattered at this point, and gave me a false positive.
Either way, I'm VERY glad to have this behind me now!
Attachments:


fremag
Junior Member
Posts: 73

Post by fremag on Mar 31, 2020 20:32:39 GMT
Congratulations !



Post by chrisd on Apr 4, 2020 4:58:13 GMT
@gaumbler, Thanks for sharing your solution.

