|
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 around. In this case our geometric primitive is |
| 2981 | +our box hittable, and we want to rotate it somehow. This is especially easy in ray tracing because |
| 2982 | +we don’t move anything; instead we move the rays in the opposite direction. For example, consider a |
| 2983 | +_translation_ (often called a _move_). We could take the pink box at the origin and add 2 to all its |
| 2984 | +x components, or (as we almost always do in ray tracing) leave the box where it is, but in its hit |
| 2985 | +routine subtract 2 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 | +If we have an intersection along the offset ray, then we know that the incident ray will hit the |
| 3000 | +translated object. However, our offset ray is not the original ray, so if we just take the |
| 3001 | +intersection we found along the offset ray then our intersection will actually be wrong by the |
| 3002 | +offset amount, so we have to shift the intersection point back onto the original ray by the offset |
| 3003 | +amount. Let's add the code to make this happen. |
2995 | 3004 |
|
2996 | 3005 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2997 | 3006 | class translate : public hittable {
|
2998 | 3007 | public:
|
| 3008 | + ... |
| 3009 | + |
| 3010 | + bool hit(const ray& r, interval ray_t, hit_record& rec) const override { |
| 3011 | + // Move the ray backwards by the offset |
| 3012 | + ray offset_r(r.origin() - offset, r.direction(), r.time()); |
| 3013 | + |
| 3014 | + // Determine where (if any) an intersection occurs along the offset ray |
| 3015 | + if (!object->hit(offset_r, ray_t, rec)) |
| 3016 | + return false; |
| 3017 | + |
| 3018 | + // Move the intersection point forwards by the offset |
| 3019 | + rec.p += offset; |
| 3020 | + rec.set_face_normal(offset_r, rec.normal); |
| 3021 | + |
| 3022 | + return true; |
| 3023 | + } |
| 3024 | + |
| 3025 | + ... |
| 3026 | + }; |
| 3027 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3028 | + [Listing [translate-hit]: <kbd>[hittable.h]</kbd> Hittable translation hit function] |
| 3029 | + |
| 3030 | +... and then flesh out the rest of the `translate` class: |
| 3031 | + |
| 3032 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
| 3033 | + class translate : public hittable { |
| 3034 | + public: |
| 3035 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
2999 | 3036 | translate(shared_ptr<hittable> p, const vec3& displacement)
|
3000 | 3037 | : object(p), offset(displacement)
|
3001 | 3038 | {
|
3002 | 3039 | bbox = object->bounding_box() + offset;
|
3003 | 3040 | }
|
| 3041 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3004 | 3042 |
|
3005 | 3043 | 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)) |
| 3044 | + // Move the ray backwards by the offset |
| 3045 | + ray offset_r(r.origin() - offset, r.direction(), r.time()); |
| 3046 | + |
| 3047 | + // Determine where (if any) an intersection occurs along the offset ray |
| 3048 | + if (!object->hit(offset_r, ray_t, rec)) |
3008 | 3049 | return false;
|
3009 | 3050 |
|
| 3051 | + // Move the intersection point forwards by the offset |
3010 | 3052 | rec.p += offset;
|
3011 |
| - rec.set_face_normal(moved_r, rec.normal); |
| 3053 | + rec.set_face_normal(offset_r, rec.normal); |
3012 | 3054 |
|
3013 | 3055 | return true;
|
3014 | 3056 | }
|
3015 | 3057 |
|
| 3058 | + |
| 3059 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
3016 | 3060 | aabb bounding_box() const override { return bbox; }
|
3017 | 3061 |
|
3018 | 3062 | public:
|
3019 | 3063 | shared_ptr<hittable> object;
|
3020 | 3064 | vec3 offset;
|
3021 | 3065 | aabb bbox;
|
| 3066 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3022 | 3067 | };
|
3023 | 3068 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
3024 | 3069 | [Listing [translate-class]: <kbd>[hittable.h]</kbd> Hittable translation class]
|
3025 | 3070 | </div>
|
3026 | 3071 |
|
3027 |
| -The expression `object->bounding_box() + offset` above requires some additional support. |
| 3072 | +We need to remember to offset the bounding box, otherwise the incident ray might be looking in the |
| 3073 | +wrong place and trivially reject the intersection. The expression `object->bounding_box() + offset` |
| 3074 | +above requires some additional support. |
3028 | 3075 |
|
3029 | 3076 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
3030 | 3077 | class aabb {
|
|
3101 | 3148 | $$ x' = \cos(\theta) \cdot x + \sin(\theta) \cdot z $$
|
3102 | 3149 | $$ z' = -\sin(\theta) \cdot x + \cos(\theta) \cdot z $$
|
3103 | 3150 |
|
3104 |
| -And about the x-axis: |
| 3151 | +And if we want to rotate about the x-axis: |
3105 | 3152 |
|
3106 | 3153 | $$ y' = \cos(\theta) \cdot y - \sin(\theta) \cdot z $$
|
3107 | 3154 | $$ z' = \sin(\theta) \cdot y + \cos(\theta) \cdot z $$
|
3108 | 3155 | </div>
|
3109 | 3156 |
|
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. |
| 3157 | +For a simple operation like a translation, thinking of the operation as a simple movement of the |
| 3158 | +initial ray is a fine way to reason about what's going on. But, for a more complex operation like a |
| 3159 | +rotation, it can be easy to accidentally get your terms crossed (or forget a negative sign), so it's |
| 3160 | +better to consider a rotation as a change of coordinates. The pseudocode for the `translate::hit` |
| 3161 | +function above describes the function in terms of _moving_: |
| 3162 | + |
| 3163 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
| 3164 | + bool hit(const ray& r, interval ray_t, hit_record& rec) const override { |
| 3165 | + // Move the ray backwards by the offset |
| 3166 | + |
| 3167 | + // Determine where (if any) an intersection occurs along the offset ray |
| 3168 | + |
| 3169 | + // Move the intersection point forwards by the offset |
| 3170 | + } |
| 3171 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3172 | + [Listing [translate-hit-move]: <kbd>[hittable.h]</kbd> The moving translate hit code] |
| 3173 | + |
| 3174 | +But this can also be thought of in terms of a _changing of coordinates_: |
| 3175 | + |
| 3176 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
| 3177 | + bool hit(const ray& r, interval ray_t, hit_record& rec) const override { |
| 3178 | + // Change the ray from world space to object space |
| 3179 | + |
| 3180 | + // Determine where (if any) an intersection occurs in object space |
| 3181 | + |
| 3182 | + // Change the intersection point from object space to world space |
| 3183 | + } |
| 3184 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3185 | + [Listing [translate-hit-coord]: <kbd>[hittable.h]</kbd> The change of coordinates translate hit code] |
3114 | 3186 |
|
3115 | 3187 | <div class='together'>
|
| 3188 | +Rotating an object will not only change the point of intersection, but will also change the surface |
| 3189 | +normal vector, which will change the direction of reflections and refractions. So we need to change the |
| 3190 | +normal as well. Fortuneatly, we can just treat the normal like a vector, and so use the same |
| 3191 | +formulas as above. |
| 3192 | + |
3116 | 3193 | For a y-rotation class we have:
|
3117 | 3194 |
|
3118 | 3195 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
3119 | 3196 | class rotate_y : public hittable {
|
3120 | 3197 | public:
|
3121 |
| - rotate_y(shared_ptr<hittable> p, double angle) : object(p) { |
| 3198 | + ... |
| 3199 | + |
| 3200 | + bool hit(const ray& r, interval ray_t, hit_record& rec) const override { |
| 3201 | + // Change the ray from world space to object space |
| 3202 | + auto origin = r.origin(); |
| 3203 | + auto direction = r.direction(); |
| 3204 | + |
| 3205 | + origin[0] = cos_theta*r.origin()[0] - sin_theta*r.origin()[2]; |
| 3206 | + origin[2] = sin_theta*r.origin()[0] + cos_theta*r.origin()[2]; |
| 3207 | + |
| 3208 | + direction[0] = cos_theta*r.direction()[0] - sin_theta*r.direction()[2]; |
| 3209 | + direction[2] = sin_theta*r.direction()[0] + cos_theta*r.direction()[2]; |
| 3210 | + |
| 3211 | + ray rotated_r(origin, direction, r.time()); |
| 3212 | + |
| 3213 | + // Determine where (if any) an intersection occurs in object space |
| 3214 | + if (!object->hit(rotated_r, ray_t, rec)) |
| 3215 | + return false; |
| 3216 | + |
| 3217 | + // Change the intersection point from object space to world space |
| 3218 | + auto p = rec.p; |
| 3219 | + p[0] = cos_theta*rec.p[0] + sin_theta*rec.p[2]; |
| 3220 | + p[2] = -sin_theta*rec.p[0] + cos_theta*rec.p[2]; |
| 3221 | + |
| 3222 | + // Change the normal from object space to world space |
| 3223 | + auto normal = rec.normal |
| 3224 | + normal[0] = cos_theta*rec.normal[0] + sin_theta*rec.normal[2]; |
| 3225 | + normal[2] = -sin_theta*rec.normal[0] + cos_theta*rec.normal[2]; |
| 3226 | + |
| 3227 | + rec.p = p; |
| 3228 | + rec.set_face_normal(rotated_r, normal); |
| 3229 | + |
| 3230 | + return true; |
| 3231 | + } |
| 3232 | + |
| 3233 | + ... |
| 3234 | + }; |
| 3235 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3236 | + [Listing [rot-y-hit]: <kbd>[hittable.h]</kbd> Hittable rotate-Y hit function] |
| 3237 | + |
| 3238 | +</div> |
| 3239 | +The code for solving from world space to object space might at first appear incorrect because we are |
| 3240 | +rotating the opposite way. The negative sign of the `sin_theta` terms are flipped. While we want to |
| 3241 | +rotate around the y-axis by $\theta$, the formula used has our world space to object space solving |
| 3242 | +for $-\theta$. What we have above is actually correct. It's the exact same thing that we did above |
| 3243 | +for translation. Changing from world space to object space required us to change by |
| 3244 | +$-\text{offset}$. Changing from world space to object space for rotation requires us to rotate by |
| 3245 | +$-\theta$. And, just as before with translation, where the change from object space to world space |
| 3246 | +required a change of $+\text{offset}$, the change in rotation from object space to world space |
| 3247 | +requires $+\theta$. |
| 3248 | + |
| 3249 | +With all of that said, we can now flesh out the rest of the class: |
| 3250 | + |
| 3251 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
| 3252 | + class rotate_y : public hittable { |
| 3253 | + public: |
| 3254 | + |
| 3255 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
| 3256 | + rotate_y(shared_ptr<hittable> p, double angle) : object(p) { |
3122 | 3257 | auto radians = degrees_to_radians(angle);
|
3123 | 3258 | sin_theta = sin(radians);
|
3124 | 3259 | cos_theta = cos(radians);
|
|
3149 | 3284 |
|
3150 | 3285 | bbox = aabb(min, max);
|
3151 | 3286 | }
|
| 3287 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3152 | 3288 |
|
3153 | 3289 | 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; |
| 3290 | + ... |
3181 | 3291 | }
|
3182 | 3292 |
|
| 3293 | + |
| 3294 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
3183 | 3295 | aabb bounding_box() const override { return bbox; }
|
3184 | 3296 |
|
3185 | 3297 | public:
|
3186 | 3298 | shared_ptr<hittable> object;
|
3187 | 3299 | double sin_theta;
|
3188 | 3300 | double cos_theta;
|
3189 | 3301 | aabb bbox;
|
| 3302 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
3190 | 3303 | };
|
3191 | 3304 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
3192 | 3305 | [Listing [rot-y]: <kbd>[hittable.h]</kbd> Hittable rotate-Y class]
|
3193 |
| -</div> |
3194 | 3306 |
|
3195 | 3307 | <div class='together'>
|
3196 | 3308 | And the changes to Cornell are:
|
|
0 commit comments