|
2975 | 2975 |
|
2976 | 2976 | <div class='together'>
|
2977 | 2977 | Now that we have boxes, we need to rotate them a bit to have them match the _real_ Cornell box. In
|
2978 |
| -ray tracing, this is usually done with an _instance_. An instance is a geometric primitive that has |
2979 |
| -been moved or rotated somehow. This is especially easy in ray tracing because we don’t move |
2980 |
| -anything; instead we move the rays in the opposite direction. For example, consider a _translation_ |
2981 |
| -(often called a _move_). We could take the pink box at the origin and add 2 to all its x components, |
2982 |
| -or (as we almost always do in ray tracing) leave the box where it is, but in its hit routine |
2983 |
| -subtract 2 off the x-component of the ray origin. |
| 2978 | +ray tracing, this is usually done with an _instance_. An instance is a copy of a geometric |
| 2979 | +primitive that has been placed into the scene. This instance is entirely independent of the other |
| 2980 | +copies of the primitive and can be moved or rotated. In this case, our geometric primitive is our |
| 2981 | +hittable `box` object, and we want to rotate it. This is especially easy in ray tracing because we |
| 2982 | +don’t actually need to move objects in the scene; instead we move the rays in the opposite |
| 2983 | +direction. For example, consider a _translation_ (often called a _move_). We could take the pink box |
| 2984 | +at the origin and add two to all its x components, or (as we almost always do in ray tracing) leave |
| 2985 | +the box where it is, but in its hit routine subtract two off the x-component of the ray origin. |
2984 | 2986 |
|
2985 | 2987 | ![Figure [ray-box]: Ray-box intersection with moved ray vs box](../images/fig-2.08-ray-box.jpg)
|
2986 | 2988 |
|
|
2990 | 2992 | Instance Translation
|
2991 | 2993 | ---------------------
|
2992 | 2994 | <div class='together'>
|
2993 |
| -Whether you think of this as a move or a change of coordinates is up to you. The code for this, to |
2994 |
| -move any underlying hittable is a _translate_ instance. |
| 2995 | +Whether you think of this as a move or a change of coordinates is up to you. The way to reason about |
| 2996 | +this is to think of moving the incident ray backwards the offset amount, determining if an |
| 2997 | +intersection occurs, and then moving that intersection point forward the offset amount. |
| 2998 | + |
| 2999 | +We need to move the intersection point forward the offset amount so that the intersection is |
| 3000 | +actually in the path of the incident ray. If we forgot to move the intersection point forward then |
| 3001 | +the intersection would be in the path of the offset ray, which isn't correct. Let's add the code to |
| 3002 | +make this happen. |
2995 | 3003 |
|
2996 | 3004 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2997 | 3005 | class translate : public hittable {
|
2998 | 3006 | public:
|
| 3007 | + ... |
| 3008 | + |
| 3009 | + bool hit(const ray& r, interval ray_t, hit_record& rec) const override { |
| 3010 | + // Move the ray backwards by the offset |
| 3011 | + ray offset_r(r.origin() - offset, r.direction(), r.time()); |
| 3012 | + |
| 3013 | + // Determine where (if any) an intersection occurs along the offset ray |
| 3014 | + if (!object->hit(offset_r, ray_t, rec)) |
| 3015 | + return false; |
| 3016 | + |
| 3017 | + // Move the intersection point forwards by the offset |
| 3018 | + rec.p += offset; |
| 3019 | + |
| 3020 | + return true; |
| 3021 | + } |
| 3022 | + |
| 3023 | + ... |
| 3024 | + }; |
| 3025 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3026 | + [Listing [translate-hit]: <kbd>[hittable.h]</kbd> Hittable translation hit function] |
| 3027 | + |
| 3028 | +... and then flesh out the rest of the `translate` class: |
| 3029 | + |
| 3030 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
| 3031 | + class translate : public hittable { |
| 3032 | + public: |
| 3033 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
2999 | 3034 | translate(shared_ptr<hittable> p, const vec3& displacement)
|
3000 | 3035 | : object(p), offset(displacement)
|
3001 | 3036 | {
|
3002 | 3037 | bbox = object->bounding_box() + offset;
|
3003 | 3038 | }
|
| 3039 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3004 | 3040 |
|
3005 | 3041 | bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
|
3006 |
| - ray moved_r(r.origin() - offset, r.direction(), r.time()); |
3007 |
| - if (!object->hit(moved_r, ray_t, rec)) |
| 3042 | + // Move the ray backwards by the offset |
| 3043 | + ray offset_r(r.origin() - offset, r.direction(), r.time()); |
| 3044 | + |
| 3045 | + // Determine where (if any) an intersection occurs along the offset ray |
| 3046 | + if (!object->hit(offset_r, ray_t, rec)) |
3008 | 3047 | return false;
|
3009 | 3048 |
|
| 3049 | + // Move the intersection point forwards by the offset |
3010 | 3050 | rec.p += offset;
|
3011 |
| - rec.set_face_normal(moved_r, rec.normal); |
3012 | 3051 |
|
3013 | 3052 | return true;
|
3014 | 3053 | }
|
3015 | 3054 |
|
| 3055 | + |
| 3056 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
3016 | 3057 | aabb bounding_box() const override { return bbox; }
|
3017 | 3058 |
|
3018 | 3059 | public:
|
3019 | 3060 | shared_ptr<hittable> object;
|
3020 | 3061 | vec3 offset;
|
3021 | 3062 | aabb bbox;
|
| 3063 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3022 | 3064 | };
|
3023 | 3065 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
3024 | 3066 | [Listing [translate-class]: <kbd>[hittable.h]</kbd> Hittable translation class]
|
3025 | 3067 | </div>
|
3026 | 3068 |
|
3027 |
| -The expression `object->bounding_box() + offset` above requires some additional support. |
| 3069 | +We also need to remember to offset the bounding box, otherwise the incident ray might be looking in |
| 3070 | +the wrong place and trivially reject the intersection. The expression |
| 3071 | +`object->bounding_box() + offset` above requires some additional support. |
3028 | 3072 |
|
3029 | 3073 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
3030 | 3074 | class aabb {
|
|
3101 | 3145 | $$ x' = \cos(\theta) \cdot x + \sin(\theta) \cdot z $$
|
3102 | 3146 | $$ z' = -\sin(\theta) \cdot x + \cos(\theta) \cdot z $$
|
3103 | 3147 |
|
3104 |
| -And about the x-axis: |
| 3148 | +And if we want to rotate about the x-axis: |
3105 | 3149 |
|
3106 | 3150 | $$ y' = \cos(\theta) \cdot y - \sin(\theta) \cdot z $$
|
3107 | 3151 | $$ z' = \sin(\theta) \cdot y + \cos(\theta) \cdot z $$
|
3108 | 3152 | </div>
|
3109 | 3153 |
|
3110 |
| -Unlike the situation with translations, the surface normal vector also changes, so we need to |
3111 |
| -transform directions too if we get a hit. Fortunately for rotations, the same formulas apply. If you |
3112 |
| -add scales, things get more complicated. See the web page https://in1weekend.blogspot.com/ for links |
3113 |
| -to that. |
| 3154 | +Thinking of translation as a simple movement of the initial ray is a fine way to reason about what's |
| 3155 | +going on. But, for a more complex operation like a rotation, it can be easy to accidentally get your |
| 3156 | +terms crossed (or forget a negative sign), so it's better to consider a rotation as a change of |
| 3157 | +coordinates. |
| 3158 | + |
| 3159 | +The pseudocode for the `translate::hit` function above describes the function in terms of _moving_: |
| 3160 | + |
| 3161 | +1. Move the ray backwards by the offset |
| 3162 | +2. Determine whether an intersection exists along the offset ray (and if so, where) |
| 3163 | +3. Move the intersection point forwards by the offset |
| 3164 | + |
| 3165 | +But this can also be thought of in terms of a _changing of coordinates_: |
| 3166 | + |
| 3167 | +1. Change the ray from world space to object space |
| 3168 | +2. Determine whether an intersection exists in object space (and if so, where) |
| 3169 | +3. Change the intersection point from object space to world space |
| 3170 | + |
3114 | 3171 |
|
3115 | 3172 | <div class='together'>
|
3116 |
| -For a y-rotation class we have: |
| 3173 | +Rotating an object will not only change the point of intersection, but will also change the surface |
| 3174 | +normal vector, which will change the direction of reflections and refractions. So we need to change |
| 3175 | +the normal as well. Fortunately, the normal will rotate similarly to a vector, so we can use the |
| 3176 | +same formulas as above. While normals and vectors may appear identical for an object undergoing |
| 3177 | +rotation and translation, an object undergoing scaling requires special attention to keep the |
| 3178 | +normals orthogonal to the surface. We won't cover that here, but you should research surface normal |
| 3179 | +transformations if you implement scaling. |
| 3180 | + |
| 3181 | +We need to start by changing the ray from world space to object space, which for rotation means |
| 3182 | +rotating by $-\theta$. |
| 3183 | + |
| 3184 | + $$ x' = \cos(\theta) \cdot x - \sin(\theta) \cdot z $$ |
| 3185 | + $$ z' = \sin(\theta) \cdot x + \cos(\theta) \cdot z $$ |
| 3186 | + |
| 3187 | +We can now create a class for y-rotation: |
3117 | 3188 |
|
3118 | 3189 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
3119 | 3190 | class rotate_y : public hittable {
|
3120 | 3191 | public:
|
3121 |
| - rotate_y(shared_ptr<hittable> p, double angle) : object(p) { |
| 3192 | + ... |
| 3193 | + |
| 3194 | + bool hit(const ray& r, interval ray_t, hit_record& rec) const override { |
| 3195 | + // Change the ray from world space to object space |
| 3196 | + auto origin = r.origin(); |
| 3197 | + auto direction = r.direction(); |
| 3198 | + |
| 3199 | + origin[0] = cos_theta*r.origin()[0] - sin_theta*r.origin()[2]; |
| 3200 | + origin[2] = sin_theta*r.origin()[0] + cos_theta*r.origin()[2]; |
| 3201 | + |
| 3202 | + direction[0] = cos_theta*r.direction()[0] - sin_theta*r.direction()[2]; |
| 3203 | + direction[2] = sin_theta*r.direction()[0] + cos_theta*r.direction()[2]; |
| 3204 | + |
| 3205 | + ray rotated_r(origin, direction, r.time()); |
| 3206 | + |
| 3207 | + // Determine where (if any) an intersection occurs in object space |
| 3208 | + if (!object->hit(rotated_r, ray_t, rec)) |
| 3209 | + return false; |
| 3210 | + |
| 3211 | + // Change the intersection point from object space to world space |
| 3212 | + auto p = rec.p; |
| 3213 | + p[0] = cos_theta*rec.p[0] + sin_theta*rec.p[2]; |
| 3214 | + p[2] = -sin_theta*rec.p[0] + cos_theta*rec.p[2]; |
| 3215 | + |
| 3216 | + // Change the normal from object space to world space |
| 3217 | + auto normal = rec.normal |
| 3218 | + normal[0] = cos_theta*rec.normal[0] + sin_theta*rec.normal[2]; |
| 3219 | + normal[2] = -sin_theta*rec.normal[0] + cos_theta*rec.normal[2]; |
| 3220 | + |
| 3221 | + rec.p = p; |
| 3222 | + rec.normal = normal; |
| 3223 | + |
| 3224 | + return true; |
| 3225 | + } |
| 3226 | + |
| 3227 | + ... |
| 3228 | + }; |
| 3229 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3230 | + [Listing [rot-y-hit]: <kbd>[hittable.h]</kbd> Hittable rotate-Y hit function] |
| 3231 | + |
| 3232 | +</div> |
| 3233 | +... and now for the rest of the class: |
| 3234 | + |
| 3235 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
| 3236 | + class rotate_y : public hittable { |
| 3237 | + public: |
| 3238 | + |
| 3239 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
| 3240 | + rotate_y(shared_ptr<hittable> p, double angle) : object(p) { |
3122 | 3241 | auto radians = degrees_to_radians(angle);
|
3123 | 3242 | sin_theta = sin(radians);
|
3124 | 3243 | cos_theta = cos(radians);
|
|
3149 | 3268 |
|
3150 | 3269 | bbox = aabb(min, max);
|
3151 | 3270 | }
|
| 3271 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3152 | 3272 |
|
3153 | 3273 | bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
|
3154 |
| - auto origin = r.origin(); |
3155 |
| - auto direction = r.direction(); |
3156 |
| - |
3157 |
| - origin[0] = cos_theta*r.origin()[0] - sin_theta*r.origin()[2]; |
3158 |
| - origin[2] = sin_theta*r.origin()[0] + cos_theta*r.origin()[2]; |
3159 |
| - |
3160 |
| - direction[0] = cos_theta*r.direction()[0] - sin_theta*r.direction()[2]; |
3161 |
| - direction[2] = sin_theta*r.direction()[0] + cos_theta*r.direction()[2]; |
3162 |
| - |
3163 |
| - ray rotated_r(origin, direction, r.time()); |
3164 |
| - |
3165 |
| - if (!object->hit(rotated_r, ray_t, rec)) |
3166 |
| - return false; |
3167 |
| - |
3168 |
| - auto p = rec.p; |
3169 |
| - auto normal = rec.normal; |
3170 |
| - |
3171 |
| - p[0] = cos_theta*rec.p[0] + sin_theta*rec.p[2]; |
3172 |
| - p[2] = -sin_theta*rec.p[0] + cos_theta*rec.p[2]; |
3173 |
| - |
3174 |
| - normal[0] = cos_theta*rec.normal[0] + sin_theta*rec.normal[2]; |
3175 |
| - normal[2] = -sin_theta*rec.normal[0] + cos_theta*rec.normal[2]; |
3176 |
| - |
3177 |
| - rec.p = p; |
3178 |
| - rec.set_face_normal(rotated_r, normal); |
3179 |
| - |
3180 |
| - return true; |
| 3274 | + ... |
3181 | 3275 | }
|
3182 | 3276 |
|
| 3277 | + |
| 3278 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
3183 | 3279 | aabb bounding_box() const override { return bbox; }
|
3184 | 3280 |
|
3185 | 3281 | public:
|
3186 | 3282 | shared_ptr<hittable> object;
|
3187 | 3283 | double sin_theta;
|
3188 | 3284 | double cos_theta;
|
3189 | 3285 | aabb bbox;
|
| 3286 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3190 | 3287 | };
|
3191 | 3288 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
3192 | 3289 | [Listing [rot-y]: <kbd>[hittable.h]</kbd> Hittable rotate-Y class]
|
3193 |
| -</div> |
3194 | 3290 |
|
3195 | 3291 | <div class='together'>
|
3196 | 3292 | And the changes to Cornell are:
|
|
0 commit comments