Right now, your stripes implementation has one small problem. If you’ve played around with it at all, you may have seen it: the stripes are completely fixed, frozen in place. It’s as if you were to shine a flashlight on your scene, with a stripe filter over the bulb. You’d find that every object that has a stripe pattern is covered with stripes of exactly the same size and orientation, regardless of how the objects themselves are arranged, as in this scene:
Because the point being passed to the stripe_at function is in world space, the patterns completely ignore the transformations of the objects to which they are applied.
This is unfortunate, because we expect a pattern to move when its object moves. If you make an object bigger or smaller, the pattern on it should get bigger or smaller. Rotating an object ought to rotate the pattern, too.
Further, it makes sense to be able to transform the patterns themselves, independently of the object. Want your stripes closer together or farther apart? Scale them. Want to change how they are oriented on the object? Rotate them. What to change their phase? Translate them to shift them to one side or the other.
Write the following three tests to sketch out how this behavior should look, and introduce a new method called stripe_at_object(pattern, object, point). It should return the color for the given pattern, on the given object, at the given world-space point, and it should respect the transformations on both the pattern and the object while doing so.
| Scenario: Stripes with an object transformation |
| Given object ← sphere() |
| And set_transform(object, scaling(2, 2, 2)) |
| And pattern ← stripe_pattern(white, black) |
| When c ← stripe_at_object(pattern, object, point(1.5, 0, 0)) |
| Then c = white |
| |
| Scenario: Stripes with a pattern transformation |
| Given object ← sphere() |
| And pattern ← stripe_pattern(white, black) |
| And set_pattern_transform(pattern, scaling(2, 2, 2)) |
| When c ← stripe_at_object(pattern, object, point(1.5, 0, 0)) |
| Then c = white |
| |
| Scenario: Stripes with both an object and a pattern transformation |
| Given object ← sphere() |
| And set_transform(object, scaling(2, 2, 2)) |
| And pattern ← stripe_pattern(white, black) |
| And set_pattern_transform(pattern, translation(0.5, 0, 0)) |
| When c ← stripe_at_object(pattern, object, point(2.5, 0, 0)) |
| Then c = white |
Make these tests pass by implementing the stripe_at_object function. It should do the following:
It’ll look like this in pseudocode:
| function stripe_at_object(pattern, object, world_point) |
| object_point ← inverse(object.transform) * world_point |
| pattern_point ← inverse(pattern.transform) * object_point |
| |
| return stripe_at(pattern, pattern_point) |
| end function |
Almost there! Now make your program actually use this new function by changing your lighting and shade_hit functions as follows:
All of your tests should be passing now. Celebrate by giving the stripes pattern another try! See what happens if you rotate the stripes, or scale them, or transform the object they’re attached to.
When you’re ready, let’s talk about how to generalize all of this, in preparation for adding more patterns.
3.129.22.135