|
17 | 17 | ====================================================================================================
|
18 | 18 |
|
19 | 19 | I’ve taught many graphics classes over the years. Often I do them in ray tracing, because you are
|
20 |
| -forced to write all the code but you can still get cool images with no API. I decided to adapt my |
| 20 | +forced to write all the code, but you can still get cool images with no API. I decided to adapt my |
21 | 21 | course notes into a how-to, to get you to a cool program as quickly as possible. It will not be a
|
22 | 22 | full-featured ray tracer, but it does have the indirect lighting which has made ray tracing a staple
|
23 | 23 | in movies. Follow these steps, and the architecture of the ray tracer you produce will be good for
|
|
66 | 66 | The PPM Image Format
|
67 | 67 | ---------------------
|
68 | 68 | Whenever you start a renderer, you need a way to see an image. The most straightforward way is to
|
69 |
| -write it to a file. The catch is, there are so many formats and many of those are complex. I always |
| 69 | +write it to a file. The catch is, there are so many formats. Many of those are complex. I always |
70 | 70 | start with a plain text ppm file. Here’s a nice description from Wikipedia:
|
71 | 71 |
|
72 | 72 | 
|
|
390 | 390 | The ray Class
|
391 | 391 | --------------
|
392 | 392 | <div class='together'>
|
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 \mathbf{b}$. Here |
| 393 | +The one thing that all ray tracers have is a ray class and a computation of what color is seen along |
| 394 | +a ray. Let’s think of a ray as a function $\mathbf{P}(t) = \mathbf{A} + t \mathbf{b}$. Here |
395 | 395 | $\mathbf{P}$ is a 3D position along a line in 3D. $\mathbf{A}$ is the ray origin and $\mathbf{b}$ is
|
396 | 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. |
| 397 | +different $t$ and $\mathbf{P}(t)$ moves the point along the ray. Add in negative $t$ values 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. |
400 | 400 |
|
401 | 401 | ![Figure [lerp]: Linear interpolation](../images/fig.lerp.jpg)
|
402 | 402 |
|
|
449 | 449 | often, so I’ll stick with a 200×100 image. I’ll put the “eye” (or camera center if you think of a
|
450 | 450 | camera) at $(0,0,0)$. I will have the y-axis go up, and the x-axis to the right. In order to respect
|
451 | 451 | the convention of a right handed coordinate system, into the screen is the negative z-axis. I will
|
452 |
| -traverse the screen from the lower left hand corner and use two offset vectors along the screen |
| 452 | +traverse the screen from the lower left hand corner, and use two offset vectors along the screen |
453 | 453 | sides to move the ray endpoint across the screen. Note that I do not make the ray direction a unit
|
454 | 454 | length vector because I think not doing that makes for simpler and slightly faster code.
|
455 | 455 |
|
|
578 | 578 | </div>
|
579 | 579 |
|
580 | 580 | <div class='together'>
|
581 |
| -The rules of vector algebra are all that we would want here, and if we expand that equation and |
582 |
| -move all the terms to the left hand side we get: |
| 581 | +The rules of vector algebra are all that we would want here. If we expand that equation and move all |
| 582 | +the terms to the left hand side we get: |
583 | 583 |
|
584 | 584 | $$ t^2 \mathbf{b} \cdot \mathbf{b}
|
585 | 585 | + 2t \mathbf{b} \cdot (\mathbf{A}-\mathbf{C})
|
|
770 | 770 | An Abstraction for Hittable Objects
|
771 | 771 | ------------------------------------
|
772 | 772 | Now, how about several spheres? While it is tempting to have an array of spheres, a very clean
|
773 |
| -solution is to make an “abstract class” for anything a ray might hit and make both a sphere and a |
| 773 | +solution is the make an “abstract class” for anything a ray might hit, and make both a sphere and a |
774 | 774 | list of spheres just something you can hit. What that class should be called is something of a
|
775 | 775 | quandary -- calling it an “object” would be good if not for “object oriented” programming. “Surface”
|
776 | 776 | is often used, with the weakness being maybe we will want volumes. “hittable” emphasizes the member
|
777 |
| -function that unites them. I don’t love any of these but I will go with “hittable”. |
| 777 | +function that unites them. I don’t love any of these, but I will go with “hittable”. |
778 | 778 |
|
779 | 779 | <div class='together'>
|
780 | 780 | This `hittable` abstract class will have a hit function that takes in a ray. Most ray tracers have
|
|
924 | 924 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
925 | 925 | [Listing [normals-point-against]: <kbd>[sphere.h]</kbd> Remembering the side of the surface]
|
926 | 926 |
|
927 |
| -The decision whether to have normals always point out or always point against the ray is based on |
928 |
| -whether you want to determine the side of the surface at the time of geometry or at the time of |
929 |
| -coloring. In this book we have more material types than we have geometry types, so we'll go for |
930 |
| -less work and put the determination at geometry time. This is simply a matter of preference and |
931 |
| -you'll see both implementations in the literature. |
| 927 | +We can set things up so that normals always point “outward” from the surface, or always point |
| 928 | +against the incident ray. This decision is determined by whether you want to determine the side of |
| 929 | +the surface at the time of geometry intersection or at the time of coloring. In this book we have |
| 930 | +more material types than we have geometry types, so we'll go for less work and put the determination |
| 931 | +at geometry time. This is simply a matter of preference, and you'll see both implementations in the |
| 932 | +literature. |
932 | 933 |
|
933 | 934 | We add the `front_face` bool to the `hit_record` struct. We'll also add a function to solve this
|
934 | 935 | calculation for us.
|
|
1245 | 1246 |
|
1246 | 1247 | When a real camera takes a picture, there are usually no jaggies along edges because the edge pixels
|
1247 | 1248 | are a blend of some foreground and some background. We can get the same effect by averaging a bunch
|
1248 |
| -of samples inside each pixel. We will not bother with stratification, which is controversial but is |
1249 |
| -usual for my programs. For some ray tracers it is critical, but the general kind we are writing here |
1250 |
| -doesn’t benefit very much from it, and it makes the code uglier. We abstract the camera class a bit |
1251 |
| -so we can make a cooler camera later. |
1252 |
| - |
| 1249 | +of samples inside each pixel. We will not bother with stratification. This is controversial, but is |
| 1250 | +usual for my programs. For some ray tracers it is critical, but the kind of general one we are |
| 1251 | +writing doesn’t benefit very much from it and it makes the code uglier. We abstract the camera class |
| 1252 | +a bit so we can make a cooler camera later. |
1253 | 1253 |
|
1254 | 1254 | Some Random Number Utilities
|
1255 | 1255 | -----------------------------
|
|
1461 | 1461 | Lambertian.)
|
1462 | 1462 |
|
1463 | 1463 | (Reader Vassillen Chizhov proved that the lazy hack is indeed just a lazy hack and is inaccurate.
|
1464 |
| -The correct representation of ideal Lambertian isn't much more work and is presented at the end of |
| 1464 | +The correct representation of ideal Lambertian isn't much more work, and is presented at the end of |
1465 | 1465 | the chapter.)
|
1466 | 1466 |
|
1467 | 1467 | <div class='together'>
|
|
1982 | 1982 | ---------------------------------------
|
1983 | 1983 | <div class='together'>
|
1984 | 1984 | For the Lambertian (diffuse) case we already have, it can either scatter always and attenuate by its
|
1985 |
| -reflectance $R$, or it can scatter with no attenuation but absorb the fraction $1-R$ of the rays. Or |
| 1985 | +reflectance $R$, or it can scatter with no attenuation but absorb the fraction $1-R$ of the rays, or |
1986 | 1986 | it could be a mixture of those strategies. For Lambertian materials we get this simple class:
|
1987 | 1987 |
|
1988 | 1988 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
|
2212 | 2212 |
|
2213 | 2213 | Clear materials such as water, glass, and diamonds are dielectrics. When a light ray hits them, it
|
2214 | 2214 | splits into a reflected ray and a refracted (transmitted) ray. We’ll handle that by randomly
|
2215 |
| -choosing between reflection or refraction and only generating one scattered ray per interaction. |
| 2215 | +choosing between reflection or refraction, and only generating one scattered ray per interaction. |
2216 | 2216 |
|
2217 | 2217 |
|
2218 | 2218 | Refraction
|
|
2340 | 2340 | Total Internal Reflection
|
2341 | 2341 | --------------------------
|
2342 | 2342 | That definitely doesn't look right. One troublesome practical issue is that when the ray is in the
|
2343 |
| -material with the higher refractive index, there is no real solution to Snell’s law and thus there |
| 2343 | +material with the higher refractive index, there is no real solution to Snell’s law, and thus there |
2344 | 2344 | is no refraction possible. If we refer back to Snell's law and the derivation of $\sin\theta'$:
|
2345 | 2345 |
|
2346 | 2346 | $$ \sin\theta' = \frac{\eta}{\eta'} \cdot \sin\theta $$
|
|
2354 | 2354 | $$ \frac{1.5}{1.0} \cdot \sin\theta > 1.0 $$,
|
2355 | 2355 |
|
2356 | 2356 | <div class="together">
|
2357 |
| -the equality between the two sides of the equation is broken and a solution cannot exist. If a |
2358 |
| -solution does not exist the glass cannot refract and must reflect the ray: |
| 2357 | +The equality between the two sides of the equation is broken, and a solution cannot exist. If a |
| 2358 | +solution does not exist, the glass cannot refract, and therefore must reflect the ray: |
2359 | 2359 |
|
2360 | 2360 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2361 | 2361 | if(etai_over_etat * sin_theta > 1.0) {
|
|
2528 | 2528 | -------------------------------
|
2529 | 2529 | <div class='together'>
|
2530 | 2530 | An interesting and easy trick with dielectric spheres is to note that if you use a negative radius,
|
2531 |
| -the geometry is unaffected but the surface normal points inward, so it can be used as a bubble |
2532 |
| -to make a hollow glass sphere: |
| 2531 | +the geometry is unaffected, but the surface normal points inward. This can be used as a bubble to |
| 2532 | +make a hollow glass sphere: |
2533 | 2533 |
|
2534 | 2534 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2535 | 2535 | world.add(make_shared<sphere>(point3(0,0,-1), 0.5, make_shared<lambertian>(color(.1, .2, .5))));
|
|
2763 | 2763 | --------------------------
|
2764 | 2764 | <div class="together">
|
2765 | 2765 | A real camera has a complicated compound lens. For our code we could simulate the order: sensor,
|
2766 |
| -then lens, then aperture, and figure out where to send the rays and flip the image once computed |
2767 |
| -(the image is projected upside down on the film). Graphics people usually use a thin lens |
2768 |
| -approximation: |
| 2766 | +then lens, then aperture. Then we could figure out where to send the rays, and flip the image after |
| 2767 | +it's computed (the image is projected upside down on the film). Graphics people, however, usually |
| 2768 | +use a thin lens approximation: |
2769 | 2769 |
|
2770 | 2770 | ![Figure [cam-lens]: Camera lens model](../images/fig.cam-lens.jpg)
|
2771 | 2771 |
|
|
2963 | 2963 | </div>
|
2964 | 2964 |
|
2965 | 2965 | An interesting thing you might note is the glass balls don’t really have shadows which makes them
|
2966 |
| -look like they are floating. This is not a bug (you don’t see glass balls much in real life, where |
2967 |
| -they also look a bit strange and indeed seem to float on cloudy days). A point on the big sphere |
| 2966 | +look like they are floating. This is not a bug -- you don’t see glass balls much in real life, where |
| 2967 | +they also look a bit strange, and indeed seem to float on cloudy days. A point on the big sphere |
2968 | 2968 | under a glass ball still has lots of light hitting it because the sky is re-ordered rather than
|
2969 | 2969 | blocked.
|
2970 | 2970 |
|
|
2973 | 2973 | -----------
|
2974 | 2974 | You now have a cool ray tracer! What next?
|
2975 | 2975 |
|
2976 |
| - 1. Lights. You can do this explicitly, by sending shadow rays to lights, or it can be done |
| 2976 | + 1. Lights -- You can do this explicitly, by sending shadow rays to lights, or it can be done |
2977 | 2977 | implicitly by making some objects emit light, biasing scattered rays toward them, and then
|
2978 | 2978 | downweighting those rays to cancel out the bias. Both work. I am in the minority in favoring
|
2979 | 2979 | the latter approach.
|
2980 | 2980 |
|
2981 |
| - 2. Triangles. Most cool models are in triangle form. The model I/O is the worst and almost |
| 2981 | + 2. Triangles -- Most cool models are in triangle form. The model I/O is the worst and almost |
2982 | 2982 | everybody tries to get somebody else’s code to do this.
|
2983 | 2983 |
|
2984 |
| - 3. Surface textures. This lets you paste images on like wall paper. Pretty easy and a good thing |
| 2984 | + 3. Surface Textures -- This lets you paste images on like wall paper. Pretty easy and a good thing |
2985 | 2985 | to do.
|
2986 | 2986 |
|
2987 |
| - 4. Solid textures. Ken Perlin has his code online. Andrew Kensler has some very cool info at his |
| 2987 | + 4. Solid textures -- Ken Perlin has his code online. Andrew Kensler has some very cool info at his |
2988 | 2988 | blog.
|
2989 | 2989 |
|
2990 |
| - 5. Volumes and media. Cool stuff and will challenge your software architecture. I favor making |
| 2990 | + 5. Volumes and Media -- Cool stuff and will challenge your software architecture. I favor making |
2991 | 2991 | volumes have the hittable interface and probabilistically have intersections based on density.
|
2992 | 2992 | Your rendering code doesn’t even have to know it has volumes with that method.
|
2993 | 2993 |
|
2994 |
| - 6. Parallelism. Run $N$ copies of your code on $N$ cores with different random seeds. Average the |
2995 |
| - $N$ runs. This averaging can also be done hierarchically where $N/2$ pairs can be averaged to |
2996 |
| - get $N/4$ images, and pairs of those can be averaged. That method of parallelism should extend |
2997 |
| - well into the thousands of cores with very little coding. |
| 2994 | + 6. Parallelism -- Run $N$ copies of your code on $N$ cores with different random seeds. Average |
| 2995 | + the $N$ runs. This averaging can also be done hierarchically where $N/2$ pairs can be averaged |
| 2996 | + to get $N/4$ images, and pairs of those can be averaged. That method of parallelism should |
| 2997 | + extend well into the thousands of cores with very little coding. |
2998 | 2998 |
|
2999 | 2999 | Have fun, and please send me your cool images!
|
3000 | 3000 |
|
|
0 commit comments