|
38 | 38 |
|
39 | 39 |
|
40 | 40 | Acknowledgments
|
41 |
| ----------------- |
| 41 | +--------------- |
42 | 42 | Thanks to Becker for his many helpful comments on the draft and to Matthew Heimlich for spotting a
|
43 | 43 | critical motion blur error. Thanks to Andrew Kensler, Thiago Ize, and Ingo Wald for advice on
|
44 | 44 | ray-AABB tests. Thanks to David Hart and Grue Debry for help with a bunch of the details. Thanks to
|
|
229 | 229 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
230 | 230 |
|
231 | 231 | If we take the example diffuse spheres from scene at the end of the last book and make them move
|
232 |
| -from their centers at `time==0`, to `center + vec3(0, 0.5*random_double(), 0)` at `time==1`, with the |
233 |
| -camera aperture open over that frame. |
| 232 | +from their centers at `time==0`, to `center + vec3(0, 0.5*random_double(), 0)` at `time==1`, with |
| 233 | +the camera aperture open over that frame. |
234 | 234 |
|
235 | 235 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
236 | 236 | hitable *random_scene() {
|
|
376 | 376 | does the ray hit that plane? Recall that the ray can be thought of as just a function that given a
|
377 | 377 | $t$ returns a location $p(t)$:
|
378 | 378 |
|
379 |
| - $$ p(t) = A + t \cdot B $$ |
| 379 | + $$ p(t) = A + t \cdot B $$ |
380 | 380 |
|
381 | 381 | That equation applies to all three of the x/y/z coordinates. For example $x(t) = A_x + t \cdot B_x$.
|
382 | 382 | This ray hits the plane $x = x_0$ at the $t$ that satisfies this equation:
|
383 | 383 |
|
384 |
| - $$ x_0 = A_x + t_0 \cdot B_x $$ |
| 384 | + $$ x_0 = A_x + t_0 \cdot B_x $$ |
385 | 385 |
|
386 | 386 | Thus $t$ at that hitpoint is:
|
387 | 387 |
|
388 |
| - $$ t_0 = \frac{x_0 - A_x}{B_x} $$ |
| 388 | + $$ t_0 = \frac{x_0 - A_x}{B_x} $$ |
389 | 389 |
|
390 | 390 | We get the similar expression for $x_1$:
|
391 | 391 |
|
392 |
| - $$ t_1 = \frac{x_1 - A_x}{B_x} $$ |
| 392 | + $$ t_1 = \frac{x_1 - A_x}{B_x} $$ |
393 | 393 |
|
394 | 394 | The key observation to turn that 1D math into a hit test is that for a hit, the $t$-intervals need
|
395 | 395 | to overlap. For example, in 2D the green and blue overlapping only happens if there is a hit:
|
|
424 | 424 | as long as we make it reasonably fast, so let’s go for simplest, which is often fastest anyway!
|
425 | 425 | First let’s look at computing the intervals:
|
426 | 426 |
|
427 |
| - $$ t_{x0} = \frac{x_0 - A_x}{B_x} $$ |
428 |
| - $$ t_{x1} = \frac{x_1 - A_x}{B_x} $$ |
| 427 | + $$ t_{x0} = \frac{x_0 - A_x}{B_x} $$ |
| 428 | + $$ t_{x1} = \frac{x_1 - A_x}{B_x} $$ |
429 | 429 |
|
430 | 430 | One troublesome thing is that perfectly valid rays will have $B_x = 0$, causing division by zero.
|
431 | 431 | Some of those rays are inside the slab, and some are not. Also, the zero will have a ± sign under
|
432 | 432 | IEEE floating point. The good news for $B_x = 0$ is that $t_{x0}$ and $t_{x1}$ will both be +∞ or
|
433 | 433 | both be -∞ if not between $x_0$ and $x_1$. So, using min and max should get us the right answers:
|
434 | 434 |
|
435 |
| - $$ t_{x0} = min(\frac{x_0 - A_x}{B_x}, \frac{x_1 - A_x}{B_x}) $$ |
436 |
| - $$ t_{x1} = max(\frac{x_0 - A_x}{B_x}, \frac{x_1 - A_x}{B_x}) $$ |
| 435 | + $$ t_{x0} = min(\frac{x_0 - A_x}{B_x}, \frac{x_1 - A_x}{B_x}) $$ |
| 436 | + $$ t_{x1} = max(\frac{x_0 - A_x}{B_x}, \frac{x_1 - A_x}{B_x}) $$ |
437 | 437 |
|
438 | 438 | The remaining troublesome case if we do that is if $B_x = 0$ and either $x_0 - A_x = 0$ or $x_1-A_x
|
439 | 439 | = 0$ so we get a `NaN`. In that case we can probably accept either hit or no hit answer, but we’ll
|
|
768 | 768 | };
|
769 | 769 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
770 | 770 |
|
771 |
| -where you used to have new lambertian(vec3(0.5, 0.5, 0.5))) now you should replace the vec3(...) |
772 |
| -with new constant_texture(vec3(...)) new lambertian(new constant_texture(vec3(0.5, 0.5, 0.5)))) |
| 771 | +where you used to have |
| 772 | + |
| 773 | +`new lambertian(vec3(0.5, 0.5, 0.5)))` |
| 774 | + |
| 775 | +now you should replace the vec3(...) with `new constant_texture(vec3(...))` |
| 776 | + |
| 777 | +`new lambertian(new constant_texture(vec3(0.5, 0.5, 0.5))))` |
773 | 778 |
|
774 | 779 | We can create a checker texture by noting that the sign of sine and cosine just alternates in a
|
775 | 780 | regular way and if we multiply trig functions in all three dimensions, the sign of that product
|
|
846 | 851 | Perlin Noise
|
847 | 852 | ====================================================================================================
|
848 | 853 |
|
849 |
| -To get cool looking solid textures most people use some form of Perlin noise. These are named after |
| 854 | +To get cool looking solid textures most people use some form of Perlin noise. These are named after |
850 | 855 | their inventor Ken Perlin. Perlin texture doesn’t return white noise like this:
|
851 | 856 |
|
852 | 857 | 
|
|
1003 | 1008 |
|
1004 | 1009 | 
|
1005 | 1010 |
|
1006 |
| -Better, but there are obvious grid features in there. Some of it is Mach bands, a known perceptual |
1007 |
| -artifact of linear interpolation of color. A standard trick is to use a hermite cubic to round off |
| 1011 | +Better, but there are obvious grid features in there. Some of it is Mach bands, a known perceptual |
| 1012 | +artifact of linear interpolation of color. A standard trick is to use a hermite cubic to round off |
1008 | 1013 | the interpolation:
|
1009 | 1014 |
|
1010 | 1015 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
|
1067 | 1072 | static vec3* perlin_generate() {
|
1068 | 1073 | vec3 * p = new vec3[256];
|
1069 | 1074 | for ( int i = 0; i < 256; ++i )
|
1070 |
| - p[i] = unit_vector(vec3(-1 + 2*random_double(), -1 + 2*random_double(), -1 + 2*random_double())); |
| 1075 | + p[i] = unit_vector(vec3( |
| 1076 | + -1 + 2*random_double(), |
| 1077 | + -1 + 2*random_double(), |
| 1078 | + -1 + 2*random_double() |
| 1079 | + )); |
1071 | 1080 | return p;
|
1072 | 1081 | }
|
1073 | 1082 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
1151 | 1160 |
|
1152 | 1161 | However, usually turbulence is used indirectly. For example, the “hello world” of procedural solid
|
1153 | 1162 | textures is a simple marble-like texture. The basic idea is to make color proportional to something
|
1154 |
| -like a sine function, and use turbulence to adjust the phase (so it shifts x in sin( x )) which |
| 1163 | +like a sine function, and use turbulence to adjust the phase (so it shifts $x$ in $sin( x )$) which |
1155 | 1164 | makes the stripes undulate. Commenting out straight noise and turbulence, and giving a marble-like
|
1156 | 1165 | effect is:
|
1157 | 1166 |
|
|
1189 | 1198 | position in the image. For example, for pixel $(i,j)$ in an nx by ny image, the image texture
|
1190 | 1199 | position is:
|
1191 | 1200 |
|
1192 |
| - $$ u = i/(nx-1) $$ |
1193 |
| - $$ v = j/(nx-1) $$ |
| 1201 | + $$ u = i/(nx-1) $$ |
| 1202 | + $$ v = j/(nx-1) $$ |
1194 | 1203 |
|
1195 | 1204 | This is just a fractional position. For a hitable, we need to also return a u and v in the hit
|
1196 | 1205 | record. For spheres, this is usually based on some form of longitude and latitude, _i.e._, spherical
|
1197 | 1206 | coordinates. So if we have a $(\theta,\phi)$ in spherical coordinates we just need to scale $\theta$
|
1198 | 1207 | and $\phi$ to fractions. If $\theta$ is the angle down from the pole, and $\phi$ is the angle around
|
1199 | 1208 | the axis through the poles, the normalization to $[0,1]$ would be:
|
1200 | 1209 |
|
1201 |
| - $$ u = \phi / (2\pi) $$ |
1202 |
| - $$ v = \theta / \pi $$ |
| 1210 | + $$ u = \phi / (2\pi) $$ |
| 1211 | + $$ v = \theta / \pi $$ |
1203 | 1212 |
|
1204 | 1213 | To compute $\theta$ and $\phi$, for a given hitpoint, the formula for spherical coordinates of a
|
1205 | 1214 | unit radius sphere on the origin is:
|
1206 | 1215 |
|
1207 |
| - $$ x = cos(\phi) cos(\theta) $$ |
1208 |
| - $$ y = sin(\phi) cos(\theta) $$ |
1209 |
| - $$ z = sin(\theta) $$ |
| 1216 | + $$ x = cos(\phi) cos(\theta) $$ |
| 1217 | + $$ y = sin(\phi) cos(\theta) $$ |
| 1218 | + $$ z = sin(\theta) $$ |
1210 | 1219 |
|
1211 |
| -We need to invert that. Because of the lovely math.h function atan2() which takes any number |
1212 |
| -proportional to sine and cosine and returns the angle, we can pass in x and y (the cos(theta) |
| 1220 | +We need to invert that. Because of the lovely `math.h` function `atan2()` which takes any number |
| 1221 | +proportional to sine and cosine and returns the angle, we can pass in $x$ and $y$ (the $cos(\theta)$ |
1213 | 1222 | cancel):
|
1214 | 1223 |
|
1215 |
| - $$ \phi = atan2(y, x) $$ |
| 1224 | + $$ \phi = atan2(y, x) $$ |
1216 | 1225 |
|
1217 |
| -The atan2 returns in the range $-\pi$ to $\pi$ so we need to take a little care there. The $\theta$ |
1218 |
| -is more straightforward: |
| 1226 | +The $atan2$ returns in the range $-\pi$ to $\pi$ so we need to take a little care there. |
| 1227 | +The $\theta$ is more straightforward: |
1219 | 1228 |
|
1220 |
| - $$ \theta = asin(z) $$ |
| 1229 | + $$ \theta = asin(z) $$ |
1221 | 1230 |
|
1222 | 1231 | which returns numbers in the range $-\pi/2$ to $\pi/2$.
|
1223 | 1232 |
|
|
1226 | 1235 |
|
1227 | 1236 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
1228 | 1237 | get_sphere_uv((rec.p-center)/radius, rec.u, rec.v);
|
1229 |
| - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 1238 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
1230 | 1239 |
|
1231 | 1240 | The utility function is:
|
1232 | 1241 |
|
|
1269 | 1278 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
1270 | 1279 |
|
1271 | 1280 | The representation of a packed array in that order is pretty standard. Thankfully, the `stb_image`
|
1272 |
| -package makes that super simple -- just include the header in main.h with a #define: |
| 1281 | +package makes that super simple -- just include the header in `main.h` with a `#define`: |
1273 | 1282 |
|
1274 | 1283 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
1275 | 1284 | #define STB_IMAGE_IMPLEMENTATION
|
|
1279 | 1288 | 
|
1280 | 1289 |
|
1281 | 1290 |
|
1282 |
| -To read an image from a file eathmap.jpg (I just grabbed a random earth map from the web -- any |
| 1291 | +To read an image from a file earthmap.jpg (I just grabbed a random earth map from the web -- any |
1283 | 1292 | standard projection will do for our purposes), and then assign it to a diffuse material, the code
|
1284 | 1293 | is:
|
1285 | 1294 |
|
|
1319 | 1328 | };
|
1320 | 1329 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
1321 | 1330 |
|
1322 |
| -So that I don’t have to make all the non-emitting materials implement emitted(), I have the base |
| 1331 | +So that I don’t have to make all the non-emitting materials implement `emitted()`, I have the base |
1323 | 1332 | class return black:
|
1324 | 1333 |
|
1325 | 1334 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
|
1365 | 1374 | Recall that a ray $p(t) = a + t \cdot b$ has its z component defined by $z(t) = a_z + t \cdot b_z$.
|
1366 | 1375 | Rearranging those terms we can solve for what the t is where $z=k$.
|
1367 | 1376 |
|
1368 |
| - $$ t = (k-a_z) / b_z $$ |
| 1377 | + $$ t = (k-a_z) / b_z $$ |
1369 | 1378 |
|
1370 | 1379 | Once we have t, we can plug that into the equations for x and y:
|
1371 | 1380 |
|
1372 |
| - $$ x = a_x + t*b_x $$ |
1373 |
| - $$ y = a_y + t*b_y $$ |
| 1381 | + $$ x = a_x + t*b_x $$ |
| 1382 | + $$ y = a_y + t*b_y $$ |
1374 | 1383 |
|
1375 | 1384 | It is a hit if $x_0 < x < x_1$ and $y_0 < y < y_1$.
|
1376 | 1385 |
|
1377 |
| -The actual xy_rect class is thus: |
| 1386 | +The actual `xy_rect` class is thus: |
1378 | 1387 |
|
1379 | 1388 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
1380 | 1389 | class xy_rect: public hitable {
|
|
1713 | 1722 | the correct impression it’s a little involved, but it is straightforward, and you can find it in any
|
1714 | 1723 | graphics text and in many lecture notes. The result for rotating counter-clockwise about z is:
|
1715 | 1724 |
|
1716 |
| - $$ x\prime = cos(\theta) \cdot x - sin(\theta) \cdot y $$ |
1717 |
| - $$ y\prime = sin(\theta) \cdot x + cos(\theta) \cdot y $$ |
| 1725 | + $$ x\prime = cos(\theta) \cdot x - sin(\theta) \cdot y $$ |
| 1726 | + $$ y\prime = sin(\theta) \cdot x + cos(\theta) \cdot y $$ |
1718 | 1727 |
|
1719 |
| -The great thing is that it works for any theta and doesn’t need any cases for quadrants or anything |
1720 |
| -like that. The inverse transform is the opposite geometric operation: rotate by -theta. Here, recall |
1721 |
| -that cos(theta) = cos(-theta) and sin(-theta) = -sin(theta), so the formulas are very simple. |
| 1728 | +The great thing is that it works for any $\theta$ and doesn’t need any cases for quadrants or |
| 1729 | +anything like that. The inverse transform is the opposite geometric operation: rotate by $-\theta$. |
| 1730 | +Here, recall that $cos(\theta) = cos(-\theta)$ and $sin(-\theta) = -sin(\theta)$, so the formulas |
| 1731 | +are very simple. |
1722 | 1732 |
|
1723 | 1733 | Similarly, for rotating about y (as we want to do for the blocks in the box) the formulas are:
|
1724 | 1734 |
|
1725 |
| - $$ x\prime = cos(\theta) \cdot x + sin(\theta) \cdot z $$ |
1726 |
| - $$ z\prime = -sin(\theta) \cdot x + cos(\theta) \cdot z $$ |
| 1735 | + $$ x\prime = cos(\theta) \cdot x + sin(\theta) \cdot z $$ |
| 1736 | + $$ z\prime = -sin(\theta) \cdot x + cos(\theta) \cdot z $$ |
1727 | 1737 |
|
1728 | 1738 | And about the x-axis:
|
1729 | 1739 |
|
1730 |
| - $$ y\prime = cos(\theta) \cdot y - sin(\theta) \cdot z $$ |
1731 |
| - $$ z\prime = sin(\theta) \cdot y + cos(\theta) \cdot z $$ |
| 1740 | + $$ y\prime = cos(\theta) \cdot y - sin(\theta) \cdot z $$ |
| 1741 | + $$ z\prime = sin(\theta) \cdot y + cos(\theta) \cdot z $$ |
1732 | 1742 |
|
1733 | 1743 | Unlike the situation with translations, the surface normal vector also changes, so we need to
|
1734 | 1744 | transform directions too if we get a hit. Fortunately for rotations, the same formulas apply. If you
|
|
1854 | 1864 | As the ray passes through the volume, it may scatter at any point. The denser the volume, the more
|
1855 | 1865 | likely that is. The probability that the ray scatters in any small distance $\delta L$ is:
|
1856 | 1866 |
|
1857 |
| - $$ probability = C \cdot \delta L $$ |
| 1867 | + $$ probability = C \cdot \delta L $$ |
1858 | 1868 |
|
1859 |
| -where $C$ is proportional to the optical density of the volume. If you go through all the differential |
1860 |
| -equations, for a random number you get a distance where the scattering occurs. If that distance is |
1861 |
| -outside the volume, then there is no “hit”. For a constant volume we just need the density $C$ and the |
1862 |
| -boundary. I’ll use another hitable for the boundary. The resulting class is: |
| 1869 | +where $C$ is proportional to the optical density of the volume. If you go through all the |
| 1870 | +differential equations, for a random number you get a distance where the scattering occurs. If |
| 1871 | +that distance is outside the volume, then there is no “hit”. For a constant volume we just need |
| 1872 | +the density $C$ and the boundary. I’ll use another hitable for the boundary. The resulting class is: |
1863 | 1873 |
|
1864 | 1874 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
1865 | 1875 | class constant_medium : public hitable {
|
|
2049 | 2059 | int ns = 1000;
|
2050 | 2060 | for (int j = 0; j < ns; j++) {
|
2051 | 2061 | boxlist2[j] = new sphere(
|
2052 |
| - vec3(165*random_double(), 165*random_double(), 165*random_double()), 10, white); |
| 2062 | + vec3(165*random_double(), 165*random_double(), 165*random_double()), |
| 2063 | + 10, white); |
2053 | 2064 | }
|
2054 | 2065 | list[l++] = new translate(new rotate_y(
|
2055 | 2066 | new bvh_node(boxlist2,ns, 0.0, 1.0), 15), vec3(-100,270,395));
|
|
0 commit comments