Intersecting a Ray with a Cylinder

Either the ray misses the cylinder or it hits the cylinder. Right? This dichotomy neatly describes the tests you’ll write first. You’ll start by confirming that a ray misses a cylinder. Such tests can be made to pass trivially, but rather than passing them by making your local_intersect method do nothing, this provides a good opportunity to start actually implementing the intersection routines.

Test #1: A Ray Misses a Cylinder

Show that the local_intersect function correctly identifies when a ray misses a cylinder.

This test creates a cylinder and casts three different rays at it. The first ray is positioned on the surface and points along the +y axis, parallel to the cylinder. The second is inside the cylinder and also points along the +y axis. The third ray is positioned outside the cylinder and oriented askew from all axes. All three should miss the cylinder.

 Scenario Outline​: A ray misses a cylinder
 Given​ cyl ← cylinder()
 And​ direction ← normalize(<direction>)
 And​ r ← ray(<origin>, direction)
 When​ xs ← local_intersect(cyl, r)
 Then​ xs.count = 0
 Examples​:
  | origin | direction |
  | point(1, 0, 0) | vector(0, 1, 0) |
  | point(0, 0, 0) | vector(0, 1, 0) |
  | point(0, 0, -5) | vector(1, 1, 1) |

The algorithm that implements this shares some features with the ray-sphere intersection algorithm. As with the sphere algorithm, you’ll compute a discriminant value, which will be negative if the ray does not intersect. Here’s some pseudocode:

 function​ local_intersect(cylinder, ray)
  a ← ray.direction.x² + ray.direction.z²
 
 # ray is parallel to the y axis
 return​ () ​if​ a is approximately zero
 
  b ← 2 * ray.origin.x * ray.direction.x +
  2 * ray.origin.z * ray.direction.z
  c ← ray.origin.x² + ray.origin.z² - 1
 
  disc ← b² - 4 * a * c
 
 # ray does not intersect the cylinder
 return​ () ​if​ disc < 0
 
 # this is just a placeholder, to ensure the tests
 # pass that expect the ray to miss.
 return​ ( intersection(1, cylinder) )
 end​ ​function

Note that the last line of the function, returning a single intersection at t=1, ensures that the tests pass because the ray truly misses the cylinder and not simply because the function wasn’t doing anything else. You’ll flesh that bit out next, in test #2.

Test #2: A Ray Hits a Cylinder

Show that the local_intersect function correctly identifies when a ray hits a cylinder.

Once again, the scenario outline creates three different rays, each of which is expected to intersect the cylinder. The first is configured to strike the cylinder on a tangent, but even though the actual intersection is at a single point, you’ll still make your code return two intersections, both at t=5. (This mimics how you handled tangent intersections in Chapter 5, Ray-Sphere Intersections, and will help with determining object overlaps in Chapter 16, Constructive Solid Geometry (CSG).) The second ray intersects the cylinder perpendicularly through the middle and results in two intersections at 4 and 6. The last ray is skewed so that it strikes the cylinder at an angle.

 Scenario Outline​: A ray strikes a cylinder
 Given​ cyl ← cylinder()
 And​ direction ← normalize(<direction>)
 And​ r ← ray(<origin>, direction)
 When​ xs ← local_intersect(cyl, r)
 Then​ xs.count = 2
 And​ xs[0].t = <t0>
 And​ xs[1].t = <t1>
 
 Examples​:
  | origin | direction | t0 | t1 |
  | point(1, 0, -5) | vector(0, 0, 1) | 5 | 5 |
  | point(0, 0, -5) | vector(0, 0, 1) | 4 | 6 |
  | point(0.5, 0, -5) | vector(0.1, 1, 1) | 6.80798 | 7.08872 |

Make this pass by using the discriminant to find the t values for the points of intersection. The highlighted lines in the following pseudocode demonstrate the calculation you need:

 function​ local_intersect(cylinder, ray)
  a ← ray.direction.x² + ray.direction.z²
 
 # ray is parallel to the y axis
 return​ () ​if​ a is approximately zero
 
  b ← 2 * ray.origin.x * ray.direction.x +
  2 * ray.origin.z * ray.direction.z
  c ← ray.origin.x² + ray.origin.z² - 1
 
  disc ← b² - 4 * a * c
 
 # ray does not intersect the cylinder
 return​ () ​if​ disc < 0
 
» t0 ← (-b - √(disc)) / (2 * a)
» t1 ← (-b + √(disc)) / (2 * a)
»
»return​ ( intersection(t0, cylinder), intersection(t1, cylinder) )
 end​ ​function

All that’s left before you can actually render this cylinder is to compute the normal vector.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.0.25