|
391 | 391 | --------------
|
392 | 392 | <div class='together'>
|
393 | 393 | The one thing that all ray tracers have is a ray class, and a computation of what color is seen
|
394 |
| -along a ray. Let’s think of a ray as a function $\mathbf{p}(t) = \mathbf{a} + t \vec{\mathbf{b}}$. |
395 |
| -Here $\mathbf{p}$ is a 3D position along a line in 3D. $\mathbf{a}$ is the ray origin and |
396 |
| -$\vec{\mathbf{b}}$ is the ray direction. The ray parameter $t$ is a real number (`double` in the |
397 |
| -code). Plug in a different $t$ and $p(t)$ moves the point along the ray. Add in negative $t$ and you |
398 |
| -can go anywhere on the 3D line. For positive $t$, you get only the parts in front of $\mathbf{a}$, |
399 |
| -and this is what is often called a half-line or ray. |
| 394 | +along a ray. Let’s think of a ray as a function $\mathbf{P}(t) = \mathbf{A} + t \mathbf{b}$. Here |
| 395 | +$\mathbf{P}$ is a 3D position along a line in 3D. $\mathbf{A}$ is the ray origin and $\mathbf{b}$ is |
| 396 | +the ray direction. The ray parameter $t$ is a real number (`double` in the code). Plug in a |
| 397 | +different $t$ and $\mathbf{P}(t)$ moves the point along the ray. Add in negative $t$ and you can go |
| 398 | +anywhere on the 3D line. For positive $t$, you get only the parts in front of $\mathbf{A}$, and this |
| 399 | +is what is often called a half-line or ray. |
400 | 400 |
|
401 | 401 | ![Figure [lerp]: Linear interpolation](../images/fig.lerp.jpg)
|
402 | 402 |
|
403 | 403 | </div>
|
404 | 404 |
|
405 | 405 | <div class='together'>
|
406 |
| -The function $p(t)$ in more verbose code form I call `ray::at(t)`: |
| 406 | +The function $\mathbf{P}(t)$ in more verbose code form I call `ray::at(t)`: |
407 | 407 |
|
408 | 408 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
409 | 409 | #ifndef RAY_H
|
|
529 | 529 | Adding a Sphere
|
530 | 530 | ====================================================================================================
|
531 | 531 |
|
532 |
| -<div class='together'> |
533 | 532 | Let’s add a single object to our ray tracer. People often use spheres in ray tracers because
|
534 | 533 | calculating whether a ray hits a sphere is pretty straightforward.
|
535 | 534 |
|
536 | 535 |
|
537 | 536 | Ray-Sphere Intersection
|
538 | 537 | ------------------------
|
| 538 | +<div class='together'> |
539 | 539 | Recall that the equation for a sphere centered at the origin of radius $R$ is $x^2 + y^2 + z^2 =
|
540 | 540 | R^2$. Put another way, if a given point $(x,y,z)$ is on the sphere, then $x^2 + y^2 + z^2 = R^2$. If
|
541 | 541 | the given point $(x,y,z)$ is _inside_ the sphere, then $x^2 + y^2 + z^2 < R^2$, and if a given point
|
542 | 542 | $(x,y,z)$ is _outside_ the sphere, then $x^2 + y^2 + z^2 > R^2$.
|
543 | 543 |
|
544 |
| -It gets uglier if the sphere center is at $(\mathbf{c}_x, \mathbf{c}_y, \mathbf{c}_z)$: |
| 544 | +It gets uglier if the sphere center is at $(C_x, C_y, C_z)$: |
545 | 545 |
|
546 |
| - $$ (x-\mathbf{c}_x)^2 + (y-\mathbf{c}_y)^2 + (z-\mathbf{c}_z)^2 = R^2 $$ |
| 546 | + $$ (x - C_x)^2 + (y - C_y)^2 + (z - C_z)^2 = r^2 $$ |
547 | 547 | </div>
|
548 | 548 |
|
549 | 549 | <div class='together'>
|
550 | 550 | In graphics, you almost always want your formulas to be in terms of vectors so all the x/y/z stuff
|
551 | 551 | is under the hood in the `vec3` class. You might note that the vector from center
|
552 |
| -$\mathbf{c} = (\mathbf{c}_x,\mathbf{c}_y,\mathbf{c}_z)$ to point $\mathbf{P} = (x,y,z)$ is |
553 |
| -$(\mathbf{p} - \mathbf{c})$, and therefore |
| 552 | +$\mathbf{C} = (C_x,C_y,C_z)$ to point $\mathbf{P} = (x,y,z)$ is $(\mathbf{P} - \mathbf{C})$, and |
| 553 | +therefore |
554 | 554 |
|
555 |
| - $$ (\mathbf{p} - \mathbf{c}) \cdot (\mathbf{p} - \mathbf{c}) |
556 |
| - = (x-\mathbf{c}_x)^2 + (y-\mathbf{c}_y)^2 + (z-\mathbf{c}_z)^2 |
| 555 | + $$ (\mathbf{P} - \mathbf{C}) \cdot (\mathbf{P} - \mathbf{C}) |
| 556 | + = (x - C_x)^2 + (y - C_y)^2 + (z - C_z)^2 |
557 | 557 | $$
|
558 | 558 | </div>
|
559 | 559 |
|
560 | 560 | <div class='together'>
|
561 | 561 | So the equation of the sphere in vector form is:
|
562 | 562 |
|
563 |
| - $$ (\mathbf{p} - \mathbf{c}) \cdot (\mathbf{p} - \mathbf{c}) = R^2 $$ |
| 563 | + $$ (\mathbf{P} - \mathbf{C}) \cdot (\mathbf{P} - \mathbf{C}) = r^2 $$ |
564 | 564 | </div>
|
565 | 565 |
|
566 | 566 | <div class='together'>
|
567 |
| -We can read this as “any point $\mathbf{p}$ that satisfies this equation is on the sphere”. We want |
568 |
| -to know if our ray $p(t) = \mathbf{a} + t\vec{\mathbf{b}}$ ever hits the sphere anywhere. If it does |
569 |
| -hit the sphere, there is some $t$ for which $p(t)$ satisfies the sphere equation. So we are looking |
570 |
| -for any $t$ where this is true: |
| 567 | +We can read this as “any point $\mathbf{P}$ that satisfies this equation is on the sphere”. We want |
| 568 | +to know if our ray $\mathbf{P}(t) = \mathbf{A} + t\mathbf{b}$ ever hits the sphere anywhere. If it |
| 569 | +does hit the sphere, there is some $t$ for which $\mathbf{P}(t)$ satisfies the sphere equation. So |
| 570 | +we are looking for any $t$ where this is true: |
571 | 571 |
|
572 |
| - $$ (p(t) - \mathbf{c})\cdot(p(t) - \mathbf{c}) = R^2 $$ |
| 572 | + $$ (\mathbf{P}(t) - \mathbf{C}) \cdot (\mathbf{P}(t) - \mathbf{C}) = r^2 $$ |
573 | 573 |
|
574 |
| -or expanding the full form of the ray $p(t)$: |
| 574 | +or expanding the full form of the ray $\mathbf{P}(t)$: |
575 | 575 |
|
576 |
| - $$ (\mathbf{a} + t \vec{\mathbf{b}} - \mathbf{c}) |
577 |
| - \cdot (\mathbf{a} + t \vec{\mathbf{b}} - \mathbf{c}) = R^2 $$ |
| 576 | + $$ (\mathbf{A} + t \mathbf{b} - \mathbf{C}) |
| 577 | + \cdot (\mathbf{A} + t \mathbf{b} - \mathbf{C}) = r^2 $$ |
578 | 578 | </div>
|
579 | 579 |
|
580 | 580 | <div class='together'>
|
581 | 581 | The rules of vector algebra are all that we would want here, and if we expand that equation and
|
582 | 582 | move all the terms to the left hand side we get:
|
583 | 583 |
|
584 |
| - $$ t^2 \vec{\mathbf{b}}\cdot\vec{\mathbf{b}} |
585 |
| - + 2t \vec{\mathbf{b}} \cdot \vec{(\mathbf{a}-\mathbf{c})} |
586 |
| - + \vec{(\mathbf{a}-\mathbf{c})} \cdot \vec{(\mathbf{a}-\mathbf{c})} - R^2 = 0 |
| 584 | + $$ t^2 \mathbf{b} \cdot \mathbf{b} |
| 585 | + + 2t \mathbf{b} \cdot (\mathbf{A}-\mathbf{C}) |
| 586 | + + (\mathbf{A}-\mathbf{C}) \cdot (\mathbf{A}-\mathbf{C}) - r^2 = 0 |
587 | 587 | $$
|
588 | 588 | </div>
|
589 | 589 |
|
590 | 590 | <div class='together'>
|
591 |
| -The vectors and $R$ in that equation are all constant and known. The unknown is $t$, and the |
| 591 | +The vectors and $r$ in that equation are all constant and known. The unknown is $t$, and the |
592 | 592 | equation is a quadratic, like you probably saw in your high school math class. You can solve for $t$
|
593 | 593 | and there is a square root part that is either positive (meaning two real solutions), negative
|
594 | 594 | (meaning no real solutions), or zero (meaning one real solution). In graphics, the algebra almost
|
|
669 | 669 | On the earth, this implies that the vector from the earth’s center to you points straight up. Let’s
|
670 | 670 | throw that into the code now, and shade it. We don’t have any lights or anything yet, so let’s just
|
671 | 671 | visualize the normals with a color map. A common trick used for visualizing normals (because it’s
|
672 |
| -easy and somewhat intuitive to assume $\vec{\mathbf{N}}$ is a unit length vector -- so each |
| 672 | +easy and somewhat intuitive to assume $\mathbf{n}$ is a unit length vector -- so each |
673 | 673 | component is between -1 and 1) is to map each component to the interval from 0 to 1, and then map
|
674 | 674 | x/y/z to r/g/b. For the normal, we need the hit point, not just whether we hit or not. Let’s assume
|
675 | 675 | the closest hit point (smallest $t$). These changes in the code let us compute and visualize
|
676 |
| -$\vec{\mathbf{N}}$: |
| 676 | +$\mathbf{n}$: |
677 | 677 |
|
678 | 678 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
679 | 679 | double hit_sphere(const point3& center, double radius, const ray& r) {
|
|
1466 | 1466 |
|
1467 | 1467 | <div class='together'>
|
1468 | 1468 | There are two unit radius spheres tangent to the hit point $p$ of a surface. These two spheres have
|
1469 |
| -a center of $(p + \vec{N})$ and $(p - \vec{N})$, where $\vec{N}$ is the normal of the surface. The |
1470 |
| -sphere with a center at $(p - \vec{N})$ is considered _inside_ the surface, whereas the sphere with |
1471 |
| -center $(p + \vec{N})$ is considered _outside_ the surface. Select the tangent unit radius sphere |
1472 |
| -that is on the same side of the surface as the ray origin. Pick a random point $s$ inside this unit |
1473 |
| -radius sphere and send a ray from the hit point $p$ to the random point $s$ (this is the vector |
1474 |
| -$(s-p)$): |
| 1469 | +a center of $(\mathbf{P} + \mathbf{n})$ and $(\mathbf{P} - \mathbf{n})$, where $\mathbf{n}$ is the |
| 1470 | +normal of the surface. The sphere with a center at $(\mathbf{P} - \mathbf{n})$ is considered |
| 1471 | +_inside_ the surface, whereas the sphere with center $(\mathbf{P} + \mathbf{n})$ is considered |
| 1472 | +_outside_ the surface. Select the tangent unit radius sphere that is on the same side of the surface |
| 1473 | +as the ray origin. Pick a random point $\mathbf{S}$ inside this unit radius sphere and send a ray |
| 1474 | +from the hit point $\mathbf{P}$ to the random point $\mathbf{S}$ (this is the vector |
| 1475 | +$(\mathbf{S}-\mathbf{P})$): |
1475 | 1476 |
|
1476 | 1477 | ![Figure [rand-vector]: Generating a random diffuse bounce ray](../images/fig.rand-vector.jpg)
|
1477 | 1478 |
|
|
2020 | 2021 | </div>
|
2021 | 2022 |
|
2022 | 2023 | <div class='together'>
|
2023 |
| -The reflected ray direction in red is just $\vec{\mathbf{V}} + 2\vec{\mathbf{B}}$. In our design, |
2024 |
| -$\vec{\mathbf{N}}$ is a unit vector, but $\vec{\mathbf{V}}$ may not be. The length of |
2025 |
| -$\vec{\mathbf{B}}$ should be $\vec{\mathbf{V}} \cdot \vec{\mathbf{N}}$. Because $\vec{\mathbf{V}}$ |
2026 |
| -points in, we will need a minus sign, yielding: |
| 2024 | +The reflected ray direction in red is just $\mathbf{v} + 2\mathbf{b}$. In our design, $\mathbf{n}$ |
| 2025 | +is a unit vector, but $\mathbf{v}$ may not be. The length of $\mathbf{b}$ should be $\mathbf{v} |
| 2026 | +\cdot \mathbf{n}$. Because $\mathbf{v}$ points in, we will need a minus sign, yielding: |
2027 | 2027 |
|
2028 | 2028 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2029 | 2029 | vec3 reflect(const vec3& v, const vec3& n) {
|
|
2256 | 2256 | $$ \sin\theta' = \frac{\eta}{\eta'} \cdot \sin\theta $$
|
2257 | 2257 |
|
2258 | 2258 | On the refracted side of the surface there is a refracted ray $\mathbf{R'}$ and a normal
|
2259 |
| -$\mathbf{N'}$, and there exists an angle, $\theta'$, between them. We can split $\mathbf{R'}$ into |
2260 |
| -the parts of the ray that are parallel to $\mathbf{N'}$ and perpendicular to $\mathbf{N'}$: |
| 2259 | +$\mathbf{n'}$, and there exists an angle, $\theta'$, between them. We can split $\mathbf{R'}$ into |
| 2260 | +the parts of the ray that are parallel to $\mathbf{n'}$ and perpendicular to $\mathbf{n'}$: |
2261 | 2261 |
|
2262 | 2262 | $$ \mathbf{R'} = \mathbf{R'}_{\parallel} + \mathbf{R'}_{\bot} $$
|
2263 | 2263 |
|
2264 | 2264 | If we solve for $\mathbf{R'}_{\parallel}$ and $\mathbf{R'}_{\bot}$ we get:
|
2265 | 2265 |
|
2266 |
| - $$ \mathbf{R'}_{\parallel} = \frac{\eta}{\eta'} (\mathbf{R} + \cos\theta \mathbf{N}) $$ |
2267 |
| - $$ \mathbf{R'}_{\bot} = -\sqrt{1 - |\mathbf{R'}_{\parallel}|^2} \mathbf{N} $$ |
| 2266 | + $$ \mathbf{R'}_{\parallel} = \frac{\eta}{\eta'} (\mathbf{R} + \cos\theta \mathbf{n}) $$ |
| 2267 | + $$ \mathbf{R'}_{\bot} = -\sqrt{1 - |\mathbf{R'}_{\parallel}|^2} \mathbf{n} $$ |
2268 | 2268 |
|
2269 | 2269 | You can go ahead and prove this for yourself if you want, but we will treat it as fact and move on.
|
2270 | 2270 | The rest of the book will not require you to understand the proof.
|
2271 | 2271 |
|
2272 | 2272 | We still need to solve for $\cos\theta$. It is well known that the dot product of two vectors can
|
2273 | 2273 | be explained in terms of the cosine of the angle between them:
|
2274 | 2274 |
|
2275 |
| - $$ \mathbf{A} \cdot \mathbf{B} = |\mathbf{A}| |\mathbf{B}| \cos\theta $$ |
| 2275 | + $$ \mathbf{a} \cdot \mathbf{b} = |\mathbf{a}| |\mathbf{b}| \cos\theta $$ |
2276 | 2276 |
|
2277 |
| -If we restrict $\mathbf{A}$ and $\mathbf{B}$ to be unit vectors: |
| 2277 | +If we restrict $\mathbf{a}$ and $\mathbf{b}$ to be unit vectors: |
2278 | 2278 |
|
2279 |
| - $$ \mathbf{A} \cdot \mathbf{B} = \cos\theta $$ |
| 2279 | + $$ \mathbf{a} \cdot \mathbf{b} = \cos\theta $$ |
2280 | 2280 |
|
2281 | 2281 | We can now rewrite $\mathbf{R'}_{\parallel}$ in terms of known quantities:
|
2282 | 2282 |
|
2283 | 2283 | $$ \mathbf{R'}_{\parallel} =
|
2284 |
| - \frac{\eta}{\eta'} (\mathbf{R} + (\mathbf{-R} \cdot \mathbf{N}) \mathbf{N}) $$ |
| 2284 | + \frac{\eta}{\eta'} (\mathbf{R} + (\mathbf{-R} \cdot \mathbf{n}) \mathbf{n}) $$ |
2285 | 2285 |
|
2286 | 2286 | When we combine them back together, we can write a function to calculate $\mathbf{R'}$:
|
2287 | 2287 |
|
|
2382 | 2382 |
|
2383 | 2383 | and
|
2384 | 2384 |
|
2385 |
| - $$ \cos\theta = \mathbf{R} \cdot \mathbf{N} $$ |
| 2385 | + $$ \cos\theta = \mathbf{R} \cdot \mathbf{n} $$ |
2386 | 2386 |
|
2387 | 2387 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2388 | 2388 | double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
|
|
0 commit comments