
Post by accolon on Oct 13, 2019 19:18:12 GMT
For the past few weeks, I've been working through this (totally awesome!) book with no major problems. But now that I arrived at chapter 13, there is something about the cone implementation that I can't solve: I seem to get two end caps, one with correct size but wrong lighting, one with correct lighting but constant size (radius 1 according to the stripe pattern), see attached images. At first I thought that the end cap was too small, but with the stripe pattern applied you can see that there is a small disc (which never changes its size, even when scaling the cone) on top of another "cap". In these images, both objects are unscaled, truncated at y=0 and y=2 and only translated along the x axis. The stripe pattern is scaled to 0.5. In the first image, both objects are open, in the second one both are closed. I based the cone implementation on my (correctly working) cylinder implementation and checked all formulas at least a couple of times. All tests for both implementations are passing. Any ideas what might be the reason for this effect? Attachments:



Post by gbordelon on Oct 13, 2019 20:45:43 GMT
I experienced this and the cause was twofold: my tracer was not correctly initializing vectors and points (remember the 4th value of a point is 1.0 and the 4th value of a vector is 0.0), and my tracer was not correctly sorting intersections (the comparator used the round() function to get integers from doubles instead of just using the <, > operators directly on the doubles).
I hope that helps.



Post by accolon on Oct 14, 2019 10:29:24 GMT
Thanks! I'm sure that my vector/point implementation is fine, but I'll check the intersections again.
It's odd that EVERYTHING else works perfectly (all the tests and even quite complex scenes I built with spheres, planes, cubes, cylinders and lots of patterns, reflections and transparency), but the cone caps don't.
Guess I will step through a simple cone scene with a debugger.



Post by accolon on Oct 14, 2019 18:29:47 GMT
After checking things with a debugger, I believe I found the problem in my local_normal_at implementation for cones.
The book says, "Lastly, for the normal vector, compute the end cap normals just as you did for the cylinder, but change the rest to the following, given in pseudocode: y < [...]", so I simply took the cylinder implementation and added the calculation for y.
However, this leads to something like:
if dist < 1 and local_point.y >= self.maximum  EPSILON: # top cap return Vector(0, 1, 0) Since I only check if dist is less than 1, I get the strange additional cap with constant radius 1.
If I replace this with
if dist < self.maximum ** 2 [...] everything looks fine (likewise for the minimum cap).
Can anybody confirm that this is the right way to do it? If yes, I believe the book is a little bit unclear at this point, since "just as you did for the cylinder" didn't make it clear to me that this change is necessary.



Post by Jamis on Oct 14, 2019 18:33:05 GMT
accolon  just a guess, but I wonder if you're not sizing your cylinder endcap appropriately? You said you're basing it on the cylinder, but the cylinder cap is always a constant radius, regardless of how high the cylinder is, whereas with a cylinder, the cap's radius needs to be the same as the y value at which the cap is placed. That is to say, for a cylinder truncated at y=4, the cap radius will be 4 as well.



Post by accolon on Oct 14, 2019 19:33:03 GMT
My cone check_cap function looks like this: def check_cap(ray, t, y): x = ray.origin.x + t * ray.direction.x z = ray.origin.z + t * ray.direction.z return (x ** 2 + z ** 2) <= y ** 2
I added the y parameter and the comparison to y^2 instead of 1 according to the description in the book, intersect_caps is calling this with self.minimum and self.maximum for y. I've attached an image with the single change I described above (comparing dist < self.maximum ** 2 in local_normal_at) which looks right to me  at least it looks much better than the "double cap" image. :) Attachments:



Post by mrwetsnow on Oct 28, 2019 2:14:27 GMT
I believe you want:
return (x ** 2 + z ** 2) <= y
Where y is either the absolute value of c.Min or the absolute value of c.Max, depending on which one you are checking. Note, it's not squared.
This seems t work properly for me.


r366y
New Member
Posts: 3

Post by r366y on Oct 30, 2019 18:54:24 GMT
accolon you are right. For the cone (and only for the cone not the cylinder) the correct cap equation is
function check_cap(ray, t, radius) x = ray.origin.x + t * ray.direction.x z = ray.origin.z + t * ray.direction.z return (x^2 + z^2) <= radius^2 end
function local_normal_at(cone, point) # compute the normal vector on a cone's end cap dist = point.x^2 + point.z^2 if dist < cone.maximum^2 && point.y >= cone.maximum  ϵ return vector3D(0., 1., 0.) elseif dist < cone.minimum^2 && point.y <= cone.minimum + ϵ
return vector3D(0., 1., 0.)
....
The area of the cap changes based on the y parameter and consequently changes the radius .
The cone's tests pass since the tested cone is not scaled



Post by accolon on Nov 1, 2019 13:21:22 GMT
Thanks for the additional discussion!
Since my final solution (which is now also confirmed by r366y) works, I'm happy  although I find it strange that other people did not run into this issue.

