|
466 | 466 | that returns the color of the background (a simple gradient).
|
467 | 467 |
|
468 | 468 | I’ve often gotten into trouble using square images for debugging because I transpose $x$ and $y$ too
|
469 |
| -often, so I’ll stick with a 200×100 image. I’ll put the “eye” (or camera center if you think of a |
470 |
| -camera) at $(0,0,0)$. I will have the y-axis go up, and the x-axis to the right. In order to respect |
471 |
| -the convention of a right handed coordinate system, into the screen is the negative z-axis. I will |
472 |
| -traverse the screen from the lower left hand corner, and use two offset vectors along the screen |
473 |
| -sides to move the ray endpoint across the screen. Note that I do not make the ray direction a unit |
474 |
| -length vector because I think not doing that makes for simpler and slightly faster code. |
| 469 | +often, so I’ll use a non-square image. For now we'll use a 16:9 aspect ratio, since that's so |
| 470 | +common. |
| 471 | + |
| 472 | +In addition to setting up the pixel dimensions for the rendered image, we also need to set up a |
| 473 | +virtual viewport through which to pass our scene rays. For the standard square pixel spacing, the |
| 474 | +viewport's aspect ratio should be the same as our rendered image. We'll just pick a viewport two |
| 475 | +units in height. Ultimately, changing the scale of the viewport (while holding the focal distance |
| 476 | +constant) is equivalent to changing the viewing angle, or “zoom” of the image. |
| 477 | + |
| 478 | +I’ll put the “eye” (or camera center if you think of a camera) at $(0,0,0)$. I will have the y-axis |
| 479 | +go up, and the x-axis to the right. In order to respect the convention of a right handed coordinate |
| 480 | +system, into the screen is the negative z-axis. I will traverse the screen from the lower left hand |
| 481 | +corner, and use two offset vectors along the screen sides to move the ray endpoint across the |
| 482 | +screen. Note that I do not make the ray direction a unit length vector because I think not doing |
| 483 | +that makes for simpler and slightly faster code. |
475 | 484 |
|
476 | 485 | ![Figure [cam-geom]: Camera geometry](../images/fig.cam-geom.jpg)
|
477 | 486 |
|
|
497 | 506 |
|
498 | 507 | std::cout << "P3\n" << image_width << " " << image_height << "\n255\n";
|
499 | 508 |
|
| 509 | + |
500 | 510 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
501 |
| - point3 origin(0.0, 0.0, 0.0); |
502 |
| - vec3 horizontal(4.0, 0.0, 0.0); |
503 |
| - vec3 vertical(0.0, 2.25, 0.0); |
504 |
| - point3 lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0,0,1); |
| 511 | + auto viewport_height = 2.0; |
| 512 | + auto viewport_width = aspect_ratio * viewport_height; |
| 513 | + auto focal_length = 1.0; |
| 514 | + |
| 515 | + auto origin = point3(0, 0, 0); |
| 516 | + auto horizontal = vec3(viewport_width, 0, 0); |
| 517 | + auto vertical = vec3(0, viewport_height, 0); |
| 518 | + auto lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length); |
505 | 519 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
| 520 | + |
506 | 521 | for (int j = image_height-1; j >= 0; --j) {
|
507 | 522 | std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
|
508 | 523 | for (int i = 0; i < image_width; ++i) {
|
509 | 524 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
510 | 525 | auto u = double(i) / (image_width-1);
|
511 | 526 | auto v = double(j) / (image_height-1);
|
512 |
| - ray r(origin, lower_left_corner + u*horizontal + v*vertical); |
| 527 | + ray r(origin, lower_left_corner + u*horizontal + v*vertical - origin); |
513 | 528 | color pixel_color = ray_color(r);
|
514 | 529 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
515 | 530 | write_color(std::cout, pixel_color);
|
|
1201 | 1216 |
|
1202 | 1217 | std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
|
1203 | 1218 |
|
1204 |
| - point3 lower_left_corner(-2.0, -1.0, -1.0); |
1205 |
| - vec3 horizontal(4.0, 0.0, 0.0); |
1206 |
| - vec3 vertical(0.0, 2.0, 0.0); |
1207 |
| - point3 origin(0.0, 0.0, 0.0); |
| 1219 | + auto viewport_height = 2.0; |
| 1220 | + auto viewport_width = aspect_ratio * viewport_height; |
| 1221 | + auto focal_length = 1.0; |
| 1222 | + |
| 1223 | + auto origin = point3(0, 0, 0); |
| 1224 | + auto horizontal = vec3(viewport_width, 0, 0); |
| 1225 | + auto vertical = vec3(0, viewport_height, 0); |
| 1226 | + auto lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length); |
1208 | 1227 |
|
1209 | 1228 |
|
1210 | 1229 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
|
1313 | 1332 | </div>
|
1314 | 1333 |
|
1315 | 1334 | <div class='together'>
|
1316 |
| -Putting that all together yields a camera class encapsulating our simple axis-aligned camera from |
| 1335 | +Now's a good time to create a `camera` class to manage our virtual camera and the related tasks of |
| 1336 | +scene scampling. The following class implements a simple camera using the axis-aligned camera from |
1317 | 1337 | before:
|
1318 | 1338 |
|
1319 | 1339 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
|
1325 | 1345 | class camera {
|
1326 | 1346 | public:
|
1327 | 1347 | camera() {
|
1328 |
| - lower_left_corner = point3(-2.0, -1.0, -1.0); |
1329 |
| - horizontal = vec3(4.0, 0.0, 0.0); |
1330 |
| - vertical = vec3(0.0, 2.0, 0.0); |
1331 |
| - origin = point3(0.0, 0.0, 0.0); |
| 1348 | + auto aspect_ratio = 16.0 / 9.0; |
| 1349 | + auto viewport_height = 2.0; |
| 1350 | + auto viewport_width = aspect_ratio * viewport_height; |
| 1351 | + auto focal_length = 1.0; |
| 1352 | + |
| 1353 | + origin = point3(0, 0, 0); |
| 1354 | + horizontal = vec3(viewport_width, 0.0, 0.0); |
| 1355 | + vertical = vec3(0.0, viewport_height, 0.0); |
| 1356 | + lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length); |
1332 | 1357 | }
|
1333 | 1358 |
|
1334 | 1359 | ray get_ray(double u, double v) const {
|
|
1399 | 1424 | world.add(make_shared<sphere>(point3(0,0,-1), 0.5));
|
1400 | 1425 | world.add(make_shared<sphere>(point3(0,-100.5,-1), 100));
|
1401 | 1426 |
|
| 1427 | + |
1402 | 1428 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
1403 | 1429 | camera cam;
|
1404 | 1430 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
| 1431 | + |
1405 | 1432 | for (int j = image_height-1; j >= 0; --j) {
|
1406 | 1433 | std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
|
1407 | 1434 | for (int i = 0; i < image_width; ++i) {
|
|
1882 | 1909 | double t;
|
1883 | 1910 | bool front_face;
|
1884 | 1911 |
|
1885 |
| - |
1886 | 1912 | inline void set_face_normal(const ray& r, const vec3& outward_normal) {
|
1887 | 1913 | front_face = dot(r.direction(), outward_normal) < 0;
|
1888 | 1914 | normal = front_face ? outward_normal :-outward_normal;
|
|
2107 | 2133 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2108 | 2134 |
|
2109 | 2135 | camera cam;
|
| 2136 | + |
2110 | 2137 | for (int j = image_height-1; j >= 0; --j) {
|
2111 | 2138 | std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
|
2112 | 2139 | for (int i = 0; i < image_width; ++i) {
|
|
2551 | 2578 | double vfov, // vertical field-of-view in degrees
|
2552 | 2579 | double aspect_ratio
|
2553 | 2580 | ) {
|
2554 |
| - origin = point3(0.0, 0.0, 0.0); |
2555 |
| - |
2556 | 2581 | auto theta = degrees_to_radians(vfov);
|
2557 |
| - auto half_height = tan(theta/2); |
2558 |
| - auto half_width = aspect_ratio * half_height; |
| 2582 | + auto h = tan(theta/2); |
| 2583 | + auto viewport_height = 2.0 * h; |
| 2584 | + auto viewport_width = aspect_ratio * viewport_height; |
2559 | 2585 |
|
2560 |
| - lower_left_corner = point3(-half_width, -half_height, -1.0); |
| 2586 | + auto focal_length = 1.0; |
2561 | 2587 |
|
2562 |
| - horizontal = vec3(2*half_width, 0.0, 0.0); |
2563 |
| - vertical = vec3(0.0, 2*half_height, 0.0); |
| 2588 | + origin = point3(0, 0, 0); |
| 2589 | + horizontal = vec3(viewport_width, 0.0, 0.0); |
| 2590 | + vertical = vec3(0.0, viewport_height, 0.0); |
| 2591 | + lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length); |
2564 | 2592 | }
|
2565 | 2593 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2566 | 2594 |
|
|
2626 | 2654 | public:
|
2627 | 2655 | camera(
|
2628 | 2656 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
2629 |
| - point3 lookfrom, point3 lookat, vec3 vup, |
| 2657 | + point3 lookfrom, |
| 2658 | + point3 lookat, |
| 2659 | + vec3 vup, |
2630 | 2660 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2631 | 2661 | double vfov, // vertical field-of-view in degrees
|
2632 | 2662 | double aspect_ratio
|
2633 | 2663 | ) {
|
2634 |
| - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
2635 |
| - origin = lookfrom; |
2636 |
| - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
2637 |
| - vec3 u, v, w; |
2638 |
| - |
2639 | 2664 | auto theta = degrees_to_radians(vfov);
|
2640 |
| - auto half_height = tan(theta/2); |
2641 |
| - auto half_width = aspect_ratio * half_height; |
| 2665 | + auto h = tan(theta/2); |
| 2666 | + auto viewport_height = 2.0 * h; |
| 2667 | + auto viewport_width = aspect_ratio * viewport_height; |
2642 | 2668 |
|
2643 |
| - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
2644 |
| - w = unit_vector(lookfrom - lookat); |
2645 |
| - u = unit_vector(cross(vup, w)); |
2646 |
| - v = cross(w, u); |
2647 | 2669 |
|
2648 |
| - lower_left_corner = origin - half_width*u - half_height*v - w; |
| 2670 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
| 2671 | + auto w = unit_vector(lookfrom - lookat); |
| 2672 | + auto u = unit_vector(cross(vup, w)); |
| 2673 | + auto v = cross(w, u); |
2649 | 2674 |
|
2650 |
| - horizontal = 2*half_width*u; |
2651 |
| - vertical = 2*half_height*v; |
| 2675 | + origin = lookfrom; |
| 2676 | + horizontal = viewport_width * u; |
| 2677 | + vertical = viewport_height * v; |
| 2678 | + lower_left_corner = origin - horizontal/2 - vertical/2 - w; |
2652 | 2679 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2653 | 2680 | }
|
2654 | 2681 |
|
|
2759 | 2786 | class camera {
|
2760 | 2787 | public:
|
2761 | 2788 | camera(
|
2762 |
| - point3 lookfrom, point3 lookat, vec3 vup, |
| 2789 | + point3 lookfrom, |
| 2790 | + point3 lookat, |
| 2791 | + vec3 vup, |
2763 | 2792 | double vfov, // vertical field-of-view in degrees
|
| 2793 | + double aspect_ratio, |
2764 | 2794 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
2765 |
| - double aspect_ratio, double aperture, double focus_dist |
2766 |
| - ) { |
2767 |
| - origin = lookfrom; |
2768 |
| - lens_radius = aperture / 2; |
| 2795 | + double aperture, |
| 2796 | + double focus_dist |
2769 | 2797 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2770 |
| - |
| 2798 | + ) { |
2771 | 2799 | auto theta = degrees_to_radians(vfov);
|
2772 |
| - auto half_height = tan(theta/2); |
2773 |
| - auto half_width = aspect_ratio * half_height; |
| 2800 | + auto h = tan(theta/2); |
| 2801 | + auto viewport_height = 2.0 * h; |
| 2802 | + auto viewport_width = aspect_ratio * viewport_height; |
2774 | 2803 |
|
| 2804 | + |
| 2805 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight |
2775 | 2806 | w = unit_vector(lookfrom - lookat);
|
2776 | 2807 | u = unit_vector(cross(vup, w));
|
2777 | 2808 | v = cross(w, u);
|
| 2809 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ |
2778 | 2810 |
|
| 2811 | + origin = lookfrom; |
2779 | 2812 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
|
2780 |
| - lower_left_corner = origin |
2781 |
| - - half_width * focus_dist * u |
2782 |
| - - half_height * focus_dist * v |
2783 |
| - - focus_dist * w; |
| 2813 | + horizontal = focus_dist * viewport_width * u; |
| 2814 | + vertical = focus_dist * viewport_height * v; |
| 2815 | + lower_left_corner = origin - horizontal/2 - vertical/2 - focus_dist*w; |
2784 | 2816 |
|
2785 |
| - horizontal = 2*half_width*focus_dist*u; |
2786 |
| - vertical = 2*half_height*focus_dist*v; |
| 2817 | + lens_radius = aperture / 2; |
2787 | 2818 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
|
2788 | 2819 | }
|
2789 | 2820 |
|
|
0 commit comments