├── CHANGELOG.md
├── COPYING.txt
├── README.md
├── assets
├── RTOneWeekend.jpg
├── book.css
├── fig03-1.jpg
├── fig03-2.jpg
├── fig04-1.jpg
├── fig05-1.jpg
├── fig06-1.jpg
├── fig07-1.jpg
├── fig07-2.jpg
├── fig08-1.jpg
├── fig08-2.jpg
├── fig09-1.jpg
├── fig10-1.jpg
├── fig10-2.jpg
├── fig10-3.jpg
├── fig11-1.jpg
├── fig11-2.jpg
├── img01-1.jpg
├── img01-2.jpg
├── img03-1.jpg
├── img04-1.jpg
├── img05-1.jpg
├── img05-2.jpg
├── img06-1.jpg
├── img07-1.jpg
├── img07-2.jpg
├── img08-1.jpg
├── img08-2.jpg
├── img09-1.jpg
├── img09-2.jpg
├── img09-3.jpg
├── img10-1.jpg
├── img10-2.jpg
├── img10-3.jpg
├── img11-1.jpg
├── img12-1.jpg
└── markdeep.min.js
├── book
├── ack.md.html
├── ch00.md.html
├── ch01.md.html
├── ch02.md.html
├── ch03.md.html
├── ch04.md.html
├── ch05.md.html
├── ch06.md.html
├── ch07.md.html
├── ch08.md.html
├── ch09.md.html
├── ch10.md.html
├── ch11.md.html
└── ch12.md.html
├── index.html
└── src
├── camera.h
├── hitable.h
├── hitable_list.h
├── main.cc
├── material.h
├── random.h
├── ray.h
├── sphere.h
└── vec3.h
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Change Log — _Ray Tracing in One Weekend_
2 | ================================================================================
3 |
4 | v1.54.0 (2018-08-26)
5 | ----------------------
6 | - The _Ray Tracing in One Weekend_ book series is now free!
7 | - First GitHub release of the book, bundled with source code.
8 |
9 |
--------------------------------------------------------------------------------
/COPYING.txt:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | *WE'VE MOVED*
2 | ====================================================================================================
3 |
4 | We have consolidated our books into a single repository: [`raytracing.github.io`][].
5 |
6 | Please update your watches, stars, forks and links.
7 |
8 |
9 |
10 | [`raytracing.github.io`]: https://github.com/RayTracing/raytracing.github.io
11 |
--------------------------------------------------------------------------------
/assets/RTOneWeekend.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/RTOneWeekend.jpg
--------------------------------------------------------------------------------
/assets/book.css:
--------------------------------------------------------------------------------
1 | body {
2 | }
3 |
4 | /* Under-Development Warning Box */
5 |
6 | div.warning {
7 | background: #fee;
8 | margin: 1em 4em 4em 4em;
9 | border: solid 4px red;
10 | padding: 0em 4ex 1em 4ex;
11 | }
12 |
13 | div.warning a {
14 | font-family: sans-serif;
15 | }
16 |
17 | div.warning p {
18 | font-family: sans-serif;
19 | font-size: 100%;
20 | font-weight: normal;
21 | color: black;
22 | }
23 |
24 | div.warning p.title {
25 | font-family: sans-serif;
26 | font-size: 160%;
27 | font-weight: bold;
28 | color: red;
29 | }
30 |
--------------------------------------------------------------------------------
/assets/fig03-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig03-1.jpg
--------------------------------------------------------------------------------
/assets/fig03-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig03-2.jpg
--------------------------------------------------------------------------------
/assets/fig04-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig04-1.jpg
--------------------------------------------------------------------------------
/assets/fig05-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig05-1.jpg
--------------------------------------------------------------------------------
/assets/fig06-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig06-1.jpg
--------------------------------------------------------------------------------
/assets/fig07-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig07-1.jpg
--------------------------------------------------------------------------------
/assets/fig07-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig07-2.jpg
--------------------------------------------------------------------------------
/assets/fig08-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig08-1.jpg
--------------------------------------------------------------------------------
/assets/fig08-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig08-2.jpg
--------------------------------------------------------------------------------
/assets/fig09-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig09-1.jpg
--------------------------------------------------------------------------------
/assets/fig10-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig10-1.jpg
--------------------------------------------------------------------------------
/assets/fig10-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig10-2.jpg
--------------------------------------------------------------------------------
/assets/fig10-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig10-3.jpg
--------------------------------------------------------------------------------
/assets/fig11-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig11-1.jpg
--------------------------------------------------------------------------------
/assets/fig11-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/fig11-2.jpg
--------------------------------------------------------------------------------
/assets/img01-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img01-1.jpg
--------------------------------------------------------------------------------
/assets/img01-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img01-2.jpg
--------------------------------------------------------------------------------
/assets/img03-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img03-1.jpg
--------------------------------------------------------------------------------
/assets/img04-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img04-1.jpg
--------------------------------------------------------------------------------
/assets/img05-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img05-1.jpg
--------------------------------------------------------------------------------
/assets/img05-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img05-2.jpg
--------------------------------------------------------------------------------
/assets/img06-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img06-1.jpg
--------------------------------------------------------------------------------
/assets/img07-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img07-1.jpg
--------------------------------------------------------------------------------
/assets/img07-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img07-2.jpg
--------------------------------------------------------------------------------
/assets/img08-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img08-1.jpg
--------------------------------------------------------------------------------
/assets/img08-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img08-2.jpg
--------------------------------------------------------------------------------
/assets/img09-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img09-1.jpg
--------------------------------------------------------------------------------
/assets/img09-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img09-2.jpg
--------------------------------------------------------------------------------
/assets/img09-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img09-3.jpg
--------------------------------------------------------------------------------
/assets/img10-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img10-1.jpg
--------------------------------------------------------------------------------
/assets/img10-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img10-2.jpg
--------------------------------------------------------------------------------
/assets/img10-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img10-3.jpg
--------------------------------------------------------------------------------
/assets/img11-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img11-1.jpg
--------------------------------------------------------------------------------
/assets/img12-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RayTracing/InOneWeekend/e6eed60166360c18ef5d87b4938a207f78a36a2a/assets/img12-1.jpg
--------------------------------------------------------------------------------
/book/ack.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 | **Acknowledgements**
11 |
12 | Thanks to readers Apoorva Joshi, Jason Stone, Becker, Matthew Heimlich, Frank He, Benjamin
13 | Summerton, Marcus Ottosson and Lorenzo Mancini for finding bugs, and to the [limnu.com][] team with
14 | help on the figures. Thanks to Fabio San for pull requests.
15 |
16 |
17 | [limnu.com]: https://limnu.com/
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/book/ch00.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 | **Ray Tracing in One Weekend**
11 | Peter Shirley
12 | Version 1.54
13 | Copyright 2018. Peter Shirley. All rights reserved.
14 |
15 |
16 |
17 | **Chapter 0: Overview**
18 |
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
21 | course notes into a how-to, to get you to a cool program as quickly as possible. It will not be a
22 | full-featured ray tracer, but it does have the indirect lighting which has made ray tracing a staple
23 | in movies. Follow these steps, and the architecture of the ray tracer you produce will be good for
24 | extending to a more extensive ray tracer if you get excited and want to pursue that.
25 |
26 | When somebody says “ray tracing” it could mean many things. What I am going to describe is
27 | technically a path tracer, and a fairly general one. While the code will be pretty simple (let the
28 | computer do the work!) I think you’ll be very happy with the images you can make.
29 |
30 | I’ll take you through writing a ray tracer in the order I do it, along with some debugging tips. By
31 | the end, you will have a ray tracer that produces some great images. You should be able to do this
32 | in a weekend. If you take longer, don’t worry about it. I use C++ as the driving language, but you
33 | don’t need to. However, I suggest you do, because it’s fast, portable, and most production movie and
34 | video game renderers are written in C++. Note that I avoid most “modern features” of C++, but
35 | inheritance and operator overloading are too useful for ray tracers to pass on. I do not provide the
36 | code online, but the code is real and I show all of it except for a few straightforward operators in
37 | the vec3 class. I am a big believer in typing in code to learn it, but when code is available I use
38 | it, so I only practice what I preach when the code is not available. So don’t ask!
39 |
40 | I have left that last part in because it is funny what a 180 I have done. Several readers ended up
41 | with subtle errors that were helped when we compared code. So please do type in the code, but if you
42 | want to look at mine it is at:
43 |
44 | https://github.com/petershirley/raytracinginoneweekend
45 |
46 | I assume a little bit of familiarity with vectors (like dot product and vector addition). If you
47 | don’t know that, do a little review. If you need that review, or to learn it for the first time,
48 | check out Marschner’s and my graphics text, Foley, Van Dam, et al., or McGuire’s graphics codex.
49 |
50 | If you run into trouble, or do something cool you’d like to show somebody, send me some email at
51 | ptrshrl@gmail.com
52 |
53 | I’ll be maintaining a site related to the book including further reading and links to resources at a
54 | blog in1weekend related to this book.
55 |
56 | Let’s get on with it!
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/book/ch01.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 1: Output an image**
12 |
13 | Whenever you start a renderer, you need a way to see an image. The most straightforward way is to
14 | write it to a file. The catch is, there are so many formats and many of those are complex. I always
15 | start with a plain text ppm file. Here’s a nice description from Wikipedia:
16 |
17 | 
18 |
19 | Let’s make some C++ code to output such a thing:
20 |
21 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
22 | #include
23 |
24 | int main() {
25 | int nx = 200;
26 | int ny = 100;
27 | std::cout << "P3\n" << nx << " " << ny << "\n255\n";
28 | for (int j = ny-1; j >= 0; j--) {
29 | for (int i = 0; i < nx; i++) {
30 | float r = float(i) / float(nx);
31 | float g = float(j) / float(ny);
32 | float b = 0.2;
33 | int ir = int(255.99*r);
34 | int ig = int(255.99*g);
35 | int ib = int(255.99*b);
36 | std::cout << ir << " " << ig << " " << ib << "\n";
37 | }
38 | }
39 | }
40 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
41 |
42 | There are some things to note in that code:
43 |
44 | 1. The pixels are written out in rows with pixels left to right.
45 |
46 | 2. The rows are written out from top to bottom.
47 |
48 | 3. By convention, each of the red/green/blue components range from 0.0 to 1.0. We will relax that
49 | later when we internally use high dynamic range, but before output we will tone map to the zero
50 | to one range, so this code won’t change.
51 |
52 | 4. Red goes from black to fully on from left to right, and green goes from black at the bottom to
53 | fully on at the top. Red and green together make yellow so we should expect the upper right
54 | corner to be yellow.
55 |
56 | Opening the output file (in ToyViewer on my mac, but try it in your favorite viewer and google “ppm
57 | viewer” if your viewer doesn’t support it) shows:
58 |
59 | 
60 |
61 | Hooray! This is the graphics “hello world”. If your image doesn’t look like that, open the output
62 | file in a text editor and see what it looks like. It should start something like this:
63 |
64 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
65 | P3
66 | 200 100
67 | 255
68 | 0 253 51
69 | 1 253 51
70 | 2 253 51
71 | 3 253 51
72 | 4 253 51
73 | 5 253 51
74 | 6 253 51
75 | 7 253 51
76 | 8 253 51
77 | 9 253 51
78 | 10 253 51
79 | 11 253 51
80 | 12 253 51
81 | 13 253 51
82 | 14 253 51
83 | 15 253 51
84 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85 |
86 | If it doesn’t, then you probably just have some newlines or something similar that is confusing the
87 | image reader.
88 |
89 | If you want to produce more image types than PPM, I am a fan of `stb_image.h` available on github.
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/book/ch02.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 3: Rays, a simple camera, and background**
12 |
13 | The one thing that all ray tracers have is a ray class, and a computation of what color is seen
14 | along a ray. Let’s think of a ray as a function $p(t) = A + t*B$. Here $p$ is a 3D position along a
15 | line in 3D. $A$ is the ray origin and $B$ is the ray direction. The ray parameter $t$ is a real
16 | number (float in the code). Plug in a different $t$ and $p(t)$ moves the point along the ray. Add in
17 | negative $t$ and you can go anywhere on the 3D line. For positive $t$, you get only the parts in
18 | front of $A$, and this is what is often called a half-line or ray. The example $C = p(2)$ is shown
19 | here:
20 |
21 | 
22 |
23 | The function $p(t)$ in more verbose code form I call “point_at_parameter(t)”:
24 |
25 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
26 | #ifndef RAYH
27 | #define RAYH
28 | #include "vec3.h"
29 |
30 | class ray
31 | {
32 | public:
33 | ray() {}
34 | ray(const vec3& a, const vec3& b) { A = a; B = b; }
35 | vec3 origin() const { return A; }
36 | vec3 direction() const { return B; }
37 | vec3 point_at_parameter(float t) const { return A + t*B; }
38 |
39 | vec3 A;
40 | vec3 B;
41 | };
42 |
43 | #endif
44 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45 |
46 | Now we are ready to turn the corner and make a ray tracer. At the core of a ray tracer is to send
47 | rays through pixels and compute what color is seen in the direction of those rays. This is of the
48 | form calculate which ray goes from the eye to a pixel, compute what that ray intersects, and compute
49 | a color for that intersection point. When first developing a ray tracer, I always do a simple camera
50 | for getting the code up and running. I also make a simple `color(ray)` function that returns the
51 | color of the background (a simple gradient).
52 |
53 | I’ve often gotten into trouble using square images for debugging because I transpose $x$ and $y$ too
54 | often, so I’ll stick with a 200×100 image. I’ll put the “eye” (or camera center if you think of a
55 | camera) at $(0,0,0)$. I will have the y-axis go up, and the x-axis to the right. In order to respect
56 | the convention of a right handed coordinate system, into the screen is the negative z-axis. I will
57 | traverse the screen from the lower left hand corner and use two offset vectors along the screen
58 | sides to move the ray endpoint across the screen. Note that I do not make the ray direction a unit
59 | length vector because I think not doing that makes for simpler and slightly faster code.
60 |
61 | 
62 |
63 | Below in code, the ray $r$ goes to approximately the pixel centers (I won’t worry about exactness
64 | for now because we’ll add antialiasing later):
65 |
66 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
67 | #include
68 | #include "ray.h"
69 |
70 | vec3 color(const ray& r, hitable *world, int depth) {
71 | vec3 unit_direction = unit_vector(r.direction());
72 | float t = 0.5*(unit_direction.y() + 1.0);
73 | return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
74 | }
75 |
76 | int main() {
77 | int nx = 200;
78 | int ny = 100;
79 | std::cout << "P3\n" << nx << " " << ny << "\n255\n";
80 | vec3 lower_left_corner(-2.0, -1.0, -1.0);
81 | vec3 horizontal(4.0, 0.0, 0.0);
82 | vec3 vertical(0.0, 2.0, 0.0);
83 | vec3 origin(0.0, 0.0, 0.0);
84 | for (int j = ny-1; j >= 0; j--) {
85 | for (int i = 0; i < nx; i++) {
86 | float u = float(i) / float(nx);
87 | float v = float(j) / float(ny);
88 | ray r(origin, lower_left_corner + u*horizontal + v*vertical);
89 | vec3 col = color(r);
90 | int ir = int(255.99*col[0]);
91 | int ig = int(255.99*col[1]);
92 | int ib = int(255.99*col[2]);
93 |
94 | std::cout << ir << " " << ig << " " << ib << "\n";
95 | }
96 | }
97 | }
98 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
99 |
100 | The `color(ray)` function linearly blends white and blue depending on the up/downess of the $y$
101 | coordinate. I first made it a unit vector so $-1.0 < y < 1.0$. I then did a standard graphics trick
102 | of scaling that to $0.0 < t < 1.0$. When $t = 1.0$ I want blue. When $t = 0.0$ I want white. In
103 | between, I want a blend. This forms a “linear blend”, or “linear interpolation”, or “lerp” for
104 | short, between two things. A lerp is always of the form
105 |
106 | $$ blendedValue = (1-t)*startValue + t*endValue, $$
107 |
108 | with $t$ going from zero to one. In our case this produces:
109 |
110 | 
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/book/ch04.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 4: Adding a sphere**
12 |
13 | Let’s add a single object to our ray tracer. People often use spheres in ray tracers because
14 | calculating whether a ray hits a sphere is pretty straightforward. Recall that the equation for a
15 | sphere centered at the origin of radius $R$ is $x^2 + y^2 + z^2 = R^2$. The way you can read that
16 | equation is “for any $(x, y, z)$, if $x^2 + y^2 + z^2 = R^2$ then $(x,y,z)$ is on the sphere, and
17 | otherwise it is not”. It gets uglier if the sphere center is at $(C_x, C_y, C_z)$:
18 |
19 | $$ (x-C_x)^2 + (y-C_y)^2 + (z-C_z)^2 = R^2 $$
20 |
21 | In graphics, you almost always want your formulas to be in terms of vectors so all the x/y/z stuff
22 | is under the hood in the `vec3` class. You might note that the vector from center
23 | $ C = (C_x,C_y,C_z) $ to point $ P = (x,y,z) $ is $ (p - C) $, and therefore
24 |
25 | $$ dot((p - C),(p - C)) = (x-C_x)^2 + (y-C_y)^2 + (z-C_z)^2 $$
26 |
27 | So the equation of the sphere in vector form is:
28 |
29 | $$ dot((p - C),(p - C)) = R^2 $$
30 |
31 | We can read this as “any point p that satisfies this equation is on the sphere”. We want to know if
32 | our ray $ p(t) = A + t*B $ ever hits the sphere anywhere. If it does hit the sphere, there is some
33 | $t$ for which $p(t)$ satisfies the sphere equation. So we are looking for any t where this is true:
34 |
35 | $$ dot((p(t) - C),(p(t) - C)) = R^2 $$
36 |
37 | or expanding the full form of the ray $p(t)$:
38 |
39 | $$ dot((A + t*B - C), (A + t*B - C)) = R^2 $$
40 |
41 | The rules of vector algebra are all that we would want here, and if we expand that equation and
42 | move all the terms to the left hand side we get:
43 |
44 | $$ t^2 \cdot dot(B,B) + 2t \cdot dot(B,A-C) + dot(A-C,A-C) - R^2 = 0 $$
45 |
46 | The vectors and $R$ in that equation are all constant and known. The unknown is $t$, and the
47 | equation is a quadratic, like you probably saw in your high school math class. You can solve for $t$
48 | and there is a square root part that is either positive (meaning two real solutions), negative
49 | (meaning no real solutions), or zero (meaning one real solution). In graphics, the algebra almost
50 | always relates very directly to the geometry. What we have is:
51 |
52 | ![Figure 4-1][fig04-1]
53 |
54 | If we take that math and hard-code it into our program, we can test it by coloring red any pixel
55 | that hits a small sphere we place at -1 on the z-axis:
56 |
57 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
58 | bool hit_sphere(const vec3& center, float radius, const ray& r) {
59 | vec3 oc = r.origin() - center;
60 | float a = dot(r.direction(), r.direction());
61 | float b = 2.0 * dot(oc, r.direction());
62 | float c = dot(oc, oc) - radius*radius;
63 | float discriminant = b*b - 4*a*c;
64 | return (discriminant > 0);
65 | }
66 |
67 | vec3 color(const ray& r) {
68 | if (hit_sphere(vec3(0,0,-1), 0.5, r))
69 | return vec3(1, 0, 0);
70 | vec3 unit_direction = unit_vector(r.direction());
71 | float t = 0.5*(unit_direction.y() + 1.0);
72 | return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
73 | }
74 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75 |
76 | What we get is this:
77 |
78 | ![Image 4-1][img04-1]
79 |
80 | Now this lacks all sorts of things -- like shading and reflection rays and more than one object --
81 | but we are closer to halfway done than we are to our start! One thing to be aware of is that we
82 | tested whether the ray hits the sphere at all, but $t < 0$ solutions work fine. If you change your
83 | sphere center to $z = +1$ you will get exactly the same picture because you see the things behind
84 | you. This is not a feature! We’ll fix those issues next.
85 |
86 |
87 |
88 | [fig04-1]: ../assets/fig04-1.jpg
89 | [img04-1]: ../assets/img04-1.jpg
90 |
91 |
92 |
93 |
94 |
95 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/book/ch05.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 5: Surface normals and multiple objects**
12 |
13 | First, let’s get ourselves a surface normal so we can shade. This is a vector that is perpendicular
14 | to the surface, and by convention, points out. One design decision is whether these normals
15 | (again by convention) are unit length. That is convenient for shading so I will say yes, but I won’t
16 | enforce that in the code. This could allow subtle bugs, so be aware this is personal preference
17 | as are most design decisions like that. For a sphere, the normal is in the direction of the hitpoint
18 | minus the center:
19 |
20 | 
21 |
22 | On the earth, this implies that the vector from the earth’s center to you points straight up. Let’s
23 | throw that into the code now, and shade it. We don’t have any lights or anything yet, so let’s just
24 | visualize the normals with a color map. A common trick used for visualizing normals (because it’s
25 | easy and somewhat intuitive to assume $N$ is a unit length vector -- so each component is between -1
26 | and 1) is to map each component to the interval from 0 to 1, and then map x/y/z to r/g/b. For the
27 | normal, we need the hit point, not just whether we hit or not. Let’s assume the closest hit point
28 | (smallest $t$). These changes in the code let us compute and visualize $N$:
29 |
30 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
31 | bool hit_sphere(const vec3& center, float radius, const ray& r) {
32 | vec3 oc = r.origin() - center;
33 | float a = dot(r.direction(), r.direction());
34 | float b = 2.0 * dot(oc, r.direction());
35 | float c = dot(oc, oc) - radius*radius;
36 | float discriminant = b*b - 4*a*c;
37 | if (discriminant < 0) {
38 | return -1.0;
39 | }
40 | else {
41 | return (-b - sqrt(discriminant) ) / (2.0*a);
42 | }
43 | }
44 |
45 | vec3 color(const ray& r) {
46 | float t = hit_sphere(vec3(0,0,-1), 0.5, r);
47 | if (t > 0.0) {
48 | vec3 N = unit_vector(r.point_at_parameter(t) - vec3(0,0,-1));
49 | return 0.5*vec3(N.x()+1, N.y()+1, N.z()+1);
50 | }
51 | vec3 unit_direction = unit_vector(r.direction());
52 | t = 0.5*(unit_direction.y() + 1.0);
53 | return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
54 | }
55 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56 |
57 | And that yields this picture:
58 |
59 | 
60 |
61 | Now, how about several spheres? While it is tempting to have an array of spheres, a very clean
62 | solution is the make an “abstract class” for anything a ray might hit and make both a sphere and a
63 | list of spheres just something you can hit. What that class should be called is something of a
64 | quandary -- calling it an “object” would be good if not for “object oriented” programming. “Surface”
65 | is often used, with the weakness being maybe we will want volumes. “Hitable” emphasizes the member
66 | function that unites them. I don’t love any of these but I will go with “hitable”.
67 |
68 | This `hitable` abstract class will have a hit function that takes in a ray. Most ray tracers have
69 | found it convenient to add a valid interval for hits $t_{min}$ to $t_{max}$, so the hit only
70 | “counts” if $t_{min} < t < t_{max}$. For the initial rays this is positive $t$, but as we will see,
71 | it can help some details in the code to have an interval $t_{min}$ to $t_{max}$. One design question
72 | is whether to do things like compute the normal if we hit something. We might end up hitting
73 | something closer as we do our search, and we will only need the normal of the closest thing. I will
74 | go with the simple solution and compute a bundle of stuff I will store in some structure. I know
75 | we’ll want motion blur at some point, so I’ll add a time input variable. Here’s the abstract class:
76 |
77 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
78 | #ifndef HITABLEH
79 | #define HITABLEH
80 |
81 | #include "ray.h"
82 |
83 | struct hit_record
84 | {
85 | float t;
86 | vec3 p;
87 | vec3 normal;
88 | };
89 |
90 | class hitable {
91 | public:
92 | virtual bool hit(
93 | const ray& r, float t_min, float t_max, hit_record& rec) const = 0;
94 | };
95 |
96 | #endif
97 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
98 |
99 | And here’s the sphere (note that I eliminated a bunch of redundant 2’s that cancel each other out):
100 |
101 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
102 | #ifndef SPHEREH
103 | #define SPHEREH
104 |
105 | #include "hitable.h"
106 |
107 | class sphere: public hitable {
108 | public:
109 | sphere() {}
110 | sphere(vec3 cen, float r) : center(cen), radius(r) {};
111 | virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
112 | vec3 center;
113 | float radius;
114 | };
115 |
116 | bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
117 | vec3 oc = r.origin() - center;
118 | float a = dot(r.direction(), r.direction());
119 | float b = dot(oc, r.direction());
120 | float c = dot(oc, oc) - radius*radius;
121 | float discriminant = b*b - a*c;
122 | if (discriminant > 0) {
123 | float temp = (-b - sqrt(discriminant))/a;
124 | if (temp < t_max && temp > t_min) {
125 | rec.t = temp;
126 | rec.p = r.point_at_parameter(rec.t);
127 | rec.normal = (rec.p - center) / radius;
128 | return true;
129 | }
130 | temp = (-b + sqrt(discriminant)) / a;
131 | if (temp < t_max && temp > t_min) {
132 | rec.t = temp;
133 | rec.p = r.point_at_parameter(rec.t);
134 | rec.normal = (rec.p - center) / radius;
135 | return true;
136 | }
137 | }
138 | return false;
139 | }
140 |
141 |
142 | #endif
143 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
144 |
145 | And a list of objects:
146 |
147 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
148 | #ifndef HITABLELISTH
149 | #define HITABLELISTH
150 |
151 | #include "hitable.h"
152 |
153 | class hitable_list: public hitable {
154 | public:
155 | hitable_list() {}
156 | hitable_list(hitable **l, int n) {list = l; list_size = n; }
157 | virtual bool hit(
158 | const ray& r, float tmin, float tmax, hit_record& rec) const;
159 | hitable **list;
160 | int list_size;
161 | };
162 |
163 | bool hitable_list::hit(
164 | const ray& r, float t_min, float t_max, hit_record& rec) const {
165 |
166 | hit_record temp_rec;
167 | bool hit_anything = false;
168 | double closest_so_far = t_max;
169 | for (int i = 0; i < list_size; i++) {
170 | if (list[i]->hit(r, t_min, closest_so_far, temp_rec)) {
171 | hit_anything = true;
172 | closest_so_far = temp_rec.t;
173 | rec = temp_rec;
174 | }
175 | }
176 | return hit_anything;
177 | }
178 |
179 | #endif
180 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
181 |
182 | And the new main:
183 |
184 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
185 | #include
186 | #include "sphere.h"
187 | #include "hitable_list.h"
188 | #include "float.h"
189 |
190 | vec3 color(const ray& r, hitable *world) {
191 | hit_record rec;
192 | if (world->hit(r, 0.0, MAXFLOAT, rec)) {
193 | return 0.5*vec3(rec.normal.x()+1, rec.normal.y()+1, rec.normal.z()+1);
194 | }
195 | else {
196 | vec3 unit_direction = unit_vector(r.direction());
197 | float t = 0.5*(unit_direction.y() + 1.0);
198 | return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
199 | }
200 | }
201 |
202 | int main() {
203 | int nx = 200;
204 | int ny = 100;
205 | std::cout << "P3\n" << nx << " " << ny << "\n255\n";
206 | vec3 lower_left_corner(-2.0, -1.0, -1.0);
207 | vec3 horizontal(4.0, 0.0, 0.0);
208 | vec3 vertical(0.0, 2.0, 0.0);
209 | vec3 origin(0.0, 0.0, 0.0);
210 | hitable *list[2];
211 | list[0] = new sphere(vec3(0,0,-1), 0.5);
212 | list[1] = new sphere(vec3(0,-100.5,-1), 100);
213 | hitable *world = new hitable_list(list,2);
214 | for (int j = ny-1; j >= 0; j--) {
215 | for (int i = 0; i < nx; i++) {
216 | float u = float(i) / float(nx);
217 | float v = float(j) / float(ny);
218 | ray r(origin, lower_left_corner + u*horizontal + v*vertical);
219 |
220 | vec3 p = r.point_at_parameter(2.0);
221 | vec3 col = color(r, world);
222 | int ir = int(255.99*col[0]);
223 | int ig = int(255.99*col[1]);
224 | int ib = int(255.99*col[2]);
225 |
226 | std::cout << ir << " " << ig << " " << ib << "\n";
227 | }
228 | }
229 | }
230 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
231 |
232 | This yields a picture that is really just a visualization of where the spheres are along with their
233 | surface normal. This is often a great way to look at your model for flaws and characteristics.
234 |
235 | 
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
--------------------------------------------------------------------------------
/book/ch06.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 6: Antialiasing**
12 |
13 | When a real camera takes a picture, there are usually no jaggies along edges because the edge pixels
14 | are a blend of some foreground and some background. We can get the same effect by averaging a bunch
15 | of samples inside each pixel. We will not bother with stratification, which is controversial but is
16 | usual for my programs. For some ray tracers it is critical, but the kind of general one we are
17 | writing doesn’t benefit very much from it and it makes the code uglier. We abstract the camera class
18 | a bit so we can make a cooler camera later.
19 |
20 | One thing we need is a random number generator that returns real random numbers. We need a function
21 | that returns a canonical random number which by convention returns random real in the range
22 | $0 ≤ ran < 1$. The “less than” before the 1 is important as we will sometimes take advantage of that.
23 |
24 | A simple approach to this is to use the `rand()` function that can be found in ``. This
25 | function returns a random integer in the range 0 and RANDMAX. Hence we can get a real random number
26 | as desired with the following code snippet:
27 |
28 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
29 | #ifndef RANDOMH
30 | #define RANDOMH
31 |
32 | #include
33 |
34 | inline double random_double() {
35 | return rand() / (RAND_MAX + 1.0);
36 | }
37 | #endif
38 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 |
40 | C++ did not traditionally have a standard random number generator, but newer versions of C++ have
41 | addressed this issue with the `` header (if imperfectly according to some experts).
42 | If you want to use this, you can obtain a random number with the conditions we need as follows:
43 |
44 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
45 | #ifndef RANDOMH
46 | #define RANDOMH
47 |
48 | #include
49 | #include
50 |
51 | inline double random_double() {
52 | static std::uniform_real_distribution distribution(0.0, 1.0);
53 | static std::mt19937 generator;
54 | static std::function rand_generator =
55 | std::bind(distribution, generator);
56 | return rand_generator();
57 | }
58 | #endif
59 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60 |
61 | For a given pixel we have several samples within that pixel and send rays through each of the
62 | samples. The colors of these rays are then averaged:
63 |
64 | 
65 |
66 | Putting that all together yields a camera class encapsulating our simple axis-aligned camera from
67 | before:
68 |
69 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
70 | #ifndef CAMERAH
71 | #define CAMERAH
72 |
73 | #include "ray.h"
74 |
75 | class camera {
76 | public:
77 | camera() {
78 | lower_left_corner = vec3(-2.0, -1.0, -1.0);
79 | horizontal = vec3(4.0, 0.0, 0.0);
80 | vertical = vec3(0.0, 2.0, 0.0);
81 | origin = vec3(0.0, 0.0, 0.0);
82 | }
83 | ray get_ray(float u, float v) {
84 | return ray(origin,
85 | lower_left_corner + u*horizontal + v*vertical - origin);
86 | }
87 |
88 | vec3 origin;
89 | vec3 lower_left_corner;
90 | vec3 horizontal;
91 | vec3 vertical;
92 | };
93 | #endif
94 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
95 |
96 | Main is also changed:
97 |
98 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
99 | int main() {
100 | int nx = 200;
101 | int ny = 100;
102 | int ns = 100;
103 | std::cout << "P3\n" << nx << " " << ny << "\n255\n";
104 | hitable *list[2];
105 | list[0] = new sphere(vec3(0,0,-1), 0.5);
106 | list[1] = new sphere(vec3(0,-100.5,-1), 100);
107 | hitable *world = new hitable_list(list,2);
108 | camera cam;
109 | for (int j = ny-1; j >= 0; j--) {
110 | for (int i = 0; i < nx; i++) {
111 | vec3 col(0, 0, 0);
112 | for (int s=0; s < ns; s++) {
113 | float u = float(i + random_double()) / float(nx);
114 | float v = float(j + random_double()) / float(ny);
115 | ray r = cam.get_ray(u, v);
116 | col += color(r, world);
117 | }
118 | col /= float(ns);
119 | int ir = int(255.99*col[0]);
120 | int ig = int(255.99*col[1]);
121 | int ib = int(255.99*col[2]);
122 | std::cout << ir << " " << ig << " " << ib << "\n";
123 | }
124 | }
125 | }
126 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
127 |
128 | Zooming into the image that is produced, the big change is in edge pixels that are part background
129 | and part foreground:
130 |
131 | 
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/book/ch07.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 7: Diffuse Materials**
12 |
13 | Now that we have objects and multiple rays per pixel, we can make some realistic looking materials.
14 | We’ll start with diffuse (matte) materials. One question is whether we can mix and match shapes and
15 | materials (so we assign a sphere a material) or if it’s put together so the geometry and material
16 | are tightly bound (that could be useful for procedural objects where the geometry and material are
17 | linked). We’ll go with separate -- which is usual in most renderers -- but do be aware of the
18 | limitation.
19 |
20 | Diffuse objects that don’t emit light merely take on the color of their surroundings, but they
21 | modulate that with their own intrinsic color. Light that reflects off a diffuse surface has its
22 | direction randomized. So, if we send three rays into a crack between two diffuse surfaces they will
23 | each have different random behavior:
24 |
25 | 
26 |
27 | They also might be absorbed rather than reflected. The darker the surface, the more likely
28 | absorption is. (That’s why it is dark!) Really any algorithm that randomizes direction will produce
29 | surfaces that look matte. One of the simplest ways to do this turns out to be exactly correct for
30 | ideal diffuse surfaces. (I used to do it as a lazy hack that approximates mathematically ideal
31 | Lambertian.)
32 |
33 | Pick a random point s from the unit radius sphere that is tangent to the hitpoint, and send a ray
34 | from the hitpoint $p$ to the random point $s$. That sphere has center $(p + N)$:
35 |
36 | 
37 |
38 | We also need a way to pick a random point in a unit radius sphere centered at the origin. We’ll use
39 | what is usually the easiest algorithm: a rejection method. First, we pick a random point in the unit
40 | cube where x, y, and z all range from -1 to +1. We reject this point and try again if the point is
41 | outside the sphere. A do/while construct is perfect for that:
42 |
43 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
44 | vec3 random_in_unit_sphere() {
45 | vec3 p;
46 | do {
47 | p = 2.0*vec3(random_double(), random_double(), random_double()) - vec3(1,1,1);
48 | } while (p.squared_length() >= 1.0);
49 | return p;
50 | }
51 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52 |
53 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
54 | vec3 color(const ray& r, hitable *world, int depth) {
55 | hit_record rec;
56 | if (world->hit(r, 0.0, MAXFLOAT, rec)) {
57 | vec3 target = rec.p + rec.normal + random_in_unit_sphere();
58 | return 0.5 * color(ray(rec.p, target - rec.p), world);
59 | }
60 | else {
61 | vec3 unit_direction = unit_vector(r.direction());
62 | float t = 0.5*(unit_direction.y() + 1.0);
63 | return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
64 | }
65 | }
66 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67 |
68 | This gives us:
69 |
70 | 
71 |
72 | Note the shadowing under the sphere. This picture is very dark, but our spheres only absorb half the
73 | energy on each bounce, so they are 50% reflectors. If you can’t see the shadow, don’t worry, we will
74 | fix that now. These spheres should look pretty light (in real life, a light grey). The reason for
75 | this is that almost all image viewers assume that the image is “gamma corrected”, meaning the 0 to 1
76 | values have some transform before being stored as a byte. There are many good reasons for that, but
77 | for our purposes we just need to be aware of it. To a first approximation, we can use “gamma 2”
78 | which means raising the color to the power $1/gamma$, or in our simple case ½, which is just
79 | square-root:
80 |
81 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
82 | col /= float(ns);
83 | col = vec3( sqrt(col[0]), sqrt(col[1]), sqrt(col[2]) );
84 | int ir = int(255.99*col[0]);
85 | int ig = int(255.99*col[1]);
86 | int ib = int(255.99*col[2]);
87 | std::cout << ir << " " << ig << " " << ib << "\n";
88 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
89 |
90 | That yields light grey, as we desire:
91 |
92 | 
93 |
94 | There’s also a subtle bug in there. Some of the reflected rays hit the object they are reflecting
95 | off of not at exactly $t=0$, but instead at $t=-0.0000001$ or $t=0.00000001$ or whatever floating
96 | point approximation the sphere intersector gives us. So we need to ignore hits very near zero:
97 |
98 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
99 | if (world->hit(r, 0.001, MAXFLOAT, rec)) {
100 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
101 |
102 | This gets rid of the shadow acne problem. Yes it is really called that.
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/book/ch08.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 8: Metal**
12 |
13 | If we want different objects to have different materials, we have a design decision. We could have a
14 | universal material with lots of parameters and different material types just zero out some of those
15 | parameters. This is not a bad approach. Or we could have an abstract material class that
16 | encapsulates behavior. I am a fan of the latter approach. For our program the material needs to do
17 | two things:
18 |
19 | 1. Produce a scattered ray (or say it absorbed the incident ray).
20 | 2. If scattered, say how much the ray should be attenuated.
21 |
22 | This suggests the abstract class:
23 |
24 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
25 | class material {
26 | public:
27 | virtual bool scatter(
28 | const ray& r_in, const hit_record& rec, vec3& attenuation,
29 | ray& scattered) const = 0;
30 | };
31 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 |
33 | The `hit_record` is to avoid a bunch of arguments so we can stuff whatever info we want in there.
34 | You can use arguments instead; it’s a matter of taste. Hitables and materials need to know each
35 | other so there is some circularity of the references. In C++ you just need to alert the compiler
36 | that the pointer is to a class, which the “class material” in the hitable class below does:
37 |
38 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
39 | #ifndef HITABLEH
40 | #define HITABLEH
41 | #include "ray.h"
42 |
43 | class material;
44 |
45 | struct hit_record
46 | {
47 | float t;
48 | vec3 p;
49 | vec3 normal;
50 | material *mat_ptr;
51 | };
52 |
53 | class hitable {
54 | public:
55 | virtual bool hit(
56 | const ray& r, float t_min, float t_max, hit_record& rec) const = 0;
57 | };
58 |
59 | #endif
60 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
61 |
62 | What we have set up here is that material will tell us how rays interact with the surface.
63 | `hit_record` is just a way to stuff a bunch of arguments into a struct so we can send them as a
64 | group. When a ray hits a surface (a particular sphere for example), the material pointer in the
65 | `hit_record` will be set to point at the material pointer the sphere was given when it was set up in
66 | `main()` when we start. When the `color()` routine gets the `hit_record` it can call member
67 | functions of the material pointer to find out what ray, if any, is scattered.
68 |
69 | To achieve this, we must have a reference to the material for our sphere class to returned
70 | within `hit_record`. See the lines below marked with **`/* NEW */`**.
71 |
72 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
73 | class sphere: public hitable {
74 | public:
75 | sphere() {}
76 | sphere(vec3 cen, float r, material *m)
77 | : center(cen), radius(r), mat_ptr(m) {};
78 | virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
79 | vec3 center;
80 | float radius;
81 | material *mat_ptr; /* NEW */
82 | };
83 |
84 | bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
85 | vec3 oc = r.origin() - center;
86 | float a = dot(r.direction(), r.direction());
87 | float b = dot(oc, r.direction());
88 | float c = dot(oc, oc) - radius*radius;
89 | float discriminant = b*b - a*c;
90 | if (discriminant > 0) {
91 | float temp = (-b - sqrt(discriminant))/a;
92 | if (temp < t_max && temp > t_min) {
93 | rec.t = temp;
94 | rec.p = r.point_at_parameter(rec.t);
95 | rec.normal = (rec.p - center) / radius;
96 | rec.mat_ptr = mat_ptr; /* NEW */
97 | return true;
98 | }
99 | temp = (-b + sqrt(discriminant)) / a;
100 | if (temp < t_max && temp > t_min) {
101 | rec.t = temp;
102 | rec.p = r.point_at_parameter(rec.t);
103 | rec.normal = (rec.p - center) / radius;
104 | rec.mat_ptr = mat_ptr; /* NEW */
105 | return true;
106 | }
107 | }
108 | return false;
109 | }
110 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
111 |
112 | For the Lambertian (diffuse) case we already have, it can either scatter always and attenuate by its
113 | reflectance $R$, or it can scatter with no attenuation but absorb the fraction $1-R$ of the rays. Or
114 | it could be a mixture of those strategies. For Lambertian materials we get this simple class:
115 |
116 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
117 | class lambertian : public material {
118 | public:
119 | lambertian(const vec3& a) : albedo(a) {}
120 | virtual bool scatter(const ray& r_in, const hit_record& rec,
121 | vec3& attenuation, ray& scattered) const
122 | {
123 | vec3 target = rec.p + rec.normal + random_in_unit_sphere();
124 | scattered = ray(rec.p, target-rec.p);
125 | attenuation = albedo;
126 | return true;
127 | }
128 |
129 | vec3 albedo;
130 | };
131 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
132 |
133 | Note we could just as well only scatter with some probability $p$ and have attenuation be
134 | $albedo/p$. Your choice.
135 |
136 | For smooth metals the ray won’t be randomly scattered. The key math is: how does a ray get
137 | reflected from a metal mirror? Vector math is our friend here:
138 |
139 | 
140 |
141 | The reflected ray direction in red is just $(v + 2B)$. In our design, $N$ is a unit vector, but $v$
142 | may not be. The length of $B$ should be $dot(v,N)$. Because $v$ points in, we will need a minus
143 | sign, yielding:
144 |
145 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
146 | vec3 reflect(const vec3& v, const vec3& n) {
147 | return v - 2*dot(v,n)*n;
148 | }
149 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
150 |
151 | The metal material just reflects rays using that formula:
152 |
153 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
154 | class metal : public material {
155 | public:
156 | metal(const vec3& a) : albedo(a) {}
157 | virtual bool scatter(const ray& r_in, const hit_record& rec,
158 | vec3& attenuation, ray& scattered) const
159 | {
160 | vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
161 | scattered = ray(rec.p, reflected);
162 | attenuation = albedo;
163 | return (dot(scattered.direction(), rec.normal) > 0);
164 | }
165 | vec3 albedo;
166 | };
167 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
168 |
169 | We need to modify the color function to use this:
170 |
171 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
172 | vec3 color(const ray& r, hitable *world, int depth) {
173 | hit_record rec;
174 | if (world->hit(r, 0.001, MAXFLOAT, rec)) {
175 | ray scattered;
176 | vec3 attenuation;
177 | if (depth < 50 && rec.mat_ptr->scatter(r, rec, attenuation, scattered)) {
178 | return attenuation*color(scattered, world, depth+1);
179 | }
180 | else {
181 | return vec3(0,0,0);
182 | }
183 | }
184 | else {
185 | vec3 unit_direction = unit_vector(r.direction());
186 | float t = 0.5*(unit_direction.y() + 1.0);
187 | return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
188 | }
189 | }
190 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191 |
192 | You will also need to modify the sphere class to have a material pointer to it. And add some
193 | metal spheres:
194 |
195 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
196 | int main() {
197 | int nx = 200;
198 | int ny = 100;
199 | int ns = 100;
200 | std::cout << "P3\n" << nx << " " << ny << "\n255\n";
201 | hitable *list[4];
202 | list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));
203 | list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
204 | list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.0));
205 | list[3] = new sphere(vec3(-1,0,-1), 0.5, new metal(vec3(0.8, 0.8, 0.8)));
206 | hitable *world = new hitable_list(list,4);
207 | camera cam;
208 | for (int j = ny-1; j >= 0; j--) {
209 | for (int i = 0; i < nx; i++) {
210 | vec3 col(0, 0, 0);
211 | for (int s=0; s < ns; s++) {
212 | float u = float(i + random_double()) / float(nx);
213 | float v = float(j + random_double()) / float(ny);
214 | ray r = cam.get_ray(u, v);
215 | vec3 p = r.point_at_parameter(2.0);
216 | col += color(r, world,0);
217 | }
218 | col /= float(ns);
219 | col = vec3( sqrt(col[0]), sqrt(col[1]), sqrt(col[2]) );
220 | int ir = int(255.99*col[0]);
221 | int ig = int(255.99*col[1]);
222 | int ib = int(255.99*col[2]);
223 | std::cout << ir << " " << ig << " " << ib << "\n";
224 | }
225 | }
226 | }
227 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
228 |
229 | Which gives:
230 |
231 | 
232 |
233 | We can also randomize the reflected direction by using a small sphere and choosing a new endpoint
234 | for the ray:
235 |
236 | 
237 |
238 | The bigger the sphere, the fuzzier the reflections will be. This suggests adding a fuzziness
239 | parameter that is just the radius of the sphere (so zero is no perturbation). The catch is that for
240 | big spheres or grazing rays, we may scatter below the surface. We can just have the surface
241 | absorb those. We’ll put a maximum of 1 on the radius of the sphere which yields:
242 |
243 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
244 | class metal : public material {
245 | public:
246 | metal(const vec3& a, float f) : albedo(a) {
247 | if (f < 1) fuzz = f; else fuzz = 1;
248 | }
249 |
250 | virtual bool scatter(const ray& r_in, const hit_record& rec,
251 | vec3& attenuation, ray& scattered) const
252 | {
253 | vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
254 | scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere());
255 | attenuation = albedo;
256 | return (dot(scattered.direction(), rec.normal) > 0);
257 | }
258 | vec3 albedo;
259 | float fuzz;
260 | };
261 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
262 |
263 | We can try that out by adding fuzziness 0.3 and 1.0 to the metals:
264 |
265 | 
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
--------------------------------------------------------------------------------
/book/ch09.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 9: Dielectrics**
12 |
13 | Clear materials such as water, glass, and diamonds are dielectrics. When a light ray hits them, it
14 | splits into a reflected ray and a refracted (transmitted) ray. We’ll handle that by randomly
15 | choosing between reflection or refraction and only generating one scattered ray per interaction.
16 |
17 | The hardest part to debug is the refracted ray. I usually first just have all the light refract if
18 | there is a refraction ray at all. For this project, I tried to put two glass balls in our scene, and
19 | I got this (I have not told you how to do this right or wrong yet, but soon!):
20 |
21 | 
22 |
23 | Is that right? Glass balls look odd in real life. But no, it isn’t right. The world should be
24 | flipped upside down and no weird black stuff. I just printed out the ray straight through the middle
25 | of the image and it was clearly wrong. That often does the job.
26 |
27 | The refraction is described by Snell’s law:
28 |
29 | $$ n \cdot sin(theta) = n' \cdot sin(theta') $$
30 |
31 | Where $n$ and $n'$ are the refractive indices (typically air = 1, glass = 1.3–1.7, diamond = 2.4)
32 | and the geometry is:
33 |
34 | 
35 |
36 | One troublesome practical issue is that when the ray is in the material with the higher refractive
37 | index, there is no real solution to Snell’s law and thus there is no refraction possible. Here all
38 | the light is reflected, and because in practice that is usually inside solid objects, it is called
39 | “total internal reflection”. This is why sometimes the water-air boundary acts as a perfect mirror
40 | when you are submerged. The code for refraction is thus a bit more complicated than for reflection:
41 |
42 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
43 | bool refract(const vec3& v, const vec3& n, float ni_over_nt, vec3& refracted) {
44 | vec3 uv = unit_vector(v);
45 | float dt = dot(uv, n);
46 | float discriminant = 1.0 - ni_over_nt*ni_over_nt*(1-dt*dt);
47 | if (discriminant > 0) {
48 | refracted = ni_over_nt*(uv - n*dt) - n*sqrt(discriminant);
49 | return true;
50 | }
51 | else
52 | return false;
53 | }
54 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
55 |
56 | And the dielectric material that always refracts when possible is:
57 |
58 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
59 | class dielectric : public material {
60 | public:
61 | dielectric(float ri) : ref_idx(ri) {}
62 | virtual bool scatter(const ray& r_in, const hit_record& rec,
63 | vec3& attenuation, ray& scattered) const
64 | {
65 | vec3 outward_normal;
66 | vec3 reflected = reflect(r_in.direction(), rec.normal);
67 | float ni_over_nt;
68 | attenuation = vec3(1.0, 1.0, 0.0);
69 | vec3 refracted;
70 | if (dot(r_in.direction(), rec.normal) > 0) {
71 | outward_normal = -rec.normal;
72 | ni_over_nt = ref_idx;
73 | }
74 | else {
75 | outward_normal = rec.normal;
76 | ni_over_nt = 1.0 / ref_idx;
77 | }
78 | if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted))
79 | scattered = ray(rec.p, refracted);
80 | else {
81 | scattered = ray(rec.p, reflected);
82 | return false;
83 | }
84 | return true;
85 | }
86 |
87 | float ref_idx;
88 | };
89 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90 |
91 | Attenuation is always 1 -- the glass surface absorbs nothing. The `attenuation = vec3(1.0, 1.0,
92 | 0.0)` above will also kill the blue channel which is the type of color bug that can be hard to find
93 | -- it will give a color shift only. Try it the way it is and then change it to
94 | `attenuation = vec3(1.0, 1.0, 1.0)` to see the difference.
95 |
96 | If we try that out with these parameters:
97 |
98 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
99 | list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));
100 | list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
101 | list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2), 0.0));
102 | list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));
103 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104 |
105 | We get:
106 |
107 | 
108 |
109 | (The reader Becker has pointed out that when there is a reflection ray the function returns `false`
110 | so there are no reflections. He is right, and that is why there are none in the image above. I
111 | am leaving this in rather than correcting this because it is a very interesting example of a major
112 | bug that still leaves a reasonably plausible image. These sleeper bugs are the hardest bugs to
113 | find because we humans are not designed to find fault with what we see.)
114 |
115 | Now real glass has reflectivity that varies with angle -- look at a window at a steep angle and it
116 | becomes a mirror. There is a big ugly equation for that, but almost everybody uses a simple and
117 | surprisingly simple polynomial approximation by Christophe Schlick:
118 |
119 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
120 | float schlick(float cosine, float ref_idx) {
121 | float r0 = (1-ref_idx) / (1+ref_idx);
122 | r0 = r0*r0;
123 | return r0 + (1-r0)*pow((1 - cosine),5);
124 | }
125 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
126 |
127 | This yields our full glass material:
128 |
129 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
130 | class dielectric : public material {
131 | public:
132 | dielectric(float ri) : ref_idx(ri) {}
133 | virtual bool scatter(
134 | const ray& r_in, const hit_record& rec, vec3& attenuation,
135 | ray& scattered) const
136 | {
137 | vec3 outward_normal;
138 | vec3 reflected = reflect(r_in.direction(), rec.normal);
139 | float ni_over_nt;
140 | attenuation = vec3(1.0, 1.0, 1.0);
141 | vec3 refracted;
142 | float reflect_prob;
143 | float cosine;
144 | if (dot(r_in.direction(), rec.normal) > 0) {
145 | outward_normal = -rec.normal;
146 | ni_over_nt = ref_idx;
147 | cosine = ref_idx * dot(r_in.direction(), rec.normal)
148 | / r_in.direction().length();
149 | }
150 | else {
151 | outward_normal = rec.normal;
152 | ni_over_nt = 1.0 / ref_idx;
153 | cosine = -dot(r_in.direction(), rec.normal)
154 | / r_in.direction().length();
155 | }
156 | if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted)) {
157 | reflect_prob = schlick(cosine, ref_idx);
158 | }
159 | else {
160 | reflect_prob = 1.0;
161 | }
162 | if (random_double() < reflect_prob) {
163 | scattered = ray(rec.p, reflected);
164 | }
165 | else {
166 | scattered = ray(rec.p, refracted);
167 | }
168 | return true;
169 | }
170 |
171 | float ref_idx;
172 | };
173 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
174 |
175 | An interesting and easy trick with dielectric spheres is to note that if you use a negative radius,
176 | the geometry is unaffected but the surface normal points inward, so it can be used as a bubble
177 | to make a hollow glass sphere:
178 |
179 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
180 | list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.1, 0.2, 0.5)));
181 | list[1] = new sphere(vec3(0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
182 | list[2] = new sphere(vec3(1,0,-1), 0.5, new metal(vec3(0.8, 0.6, 0.2)));
183 | list[3] = new sphere(vec3(-1,0,-1), 0.5, new dielectric(1.5));
184 | list[4] = new sphere(vec3(-1,0,-1), -0.45, new dielectric(1.5));
185 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
186 |
187 | This gives:
188 |
189 | 
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/book/ch10.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 10: Positionable camera**
12 |
13 | Cameras, like dielectrics, are a pain to debug. So I always develop mine incrementally. First, let’s
14 | allow an adjustable field of view (_fov_). This is the angle you see through the portal. Since our
15 | image is not square, the fov is different horizontally and vertically. I always use vertical fov. I
16 | also usually specify it in degrees and change to radians inside a constructor -- a matter of
17 | personal taste.
18 |
19 | I first keep the rays coming from the origin and heading to the $z = -1$ plane. We could make it the
20 | $z = -2$ plane, or whatever, as long as we made $h$ a ratio to that distance. Here is our setup:
21 |
22 | 
23 |
24 | This implies $h = tan(\theta/2)$. Our camera now becomes:
25 |
26 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
27 | #ifndef CAMERAH
28 | #define CAMERAH
29 |
30 | #include "ray.h"
31 |
32 | class camera {
33 | public:
34 | camera(float vfov, float aspect) { // vfov is top to bottom in degrees
35 | float theta = vfov*M_PI/180;
36 | float half_height = tan(theta/2);
37 | float half_width = aspect * half_height;
38 | lower_left_corner = vec3(-half_width, -half_height, -1.0);
39 | horizontal = vec3(2*half_width, 0.0, 0.0);
40 | vertical = vec3(0.0, 2*half_height, 0.0);
41 | origin = vec3(0.0, 0.0, 0.0);
42 | }
43 | ray get_ray(float u, float v) {
44 | return ray(origin,
45 | lower_left_corner + u*horizontal + v*vertical - origin);
46 | }
47 |
48 | vec3 origin;
49 | vec3 lower_left_corner;
50 | vec3 horizontal;
51 | vec3 vertical;
52 | };
53 | #endif
54 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
55 |
56 | When calling it with camera `cam(90, float(nx)/float(ny))` and these spheres:
57 |
58 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
59 | float R = cos(M_PI/4);
60 | list[0] = new sphere(vec3(-R,0,-1), R, new lambertian(vec3(0, 0, 1)));
61 | list[1] = new sphere(vec3( R,0,-1), R, new lambertian(vec3(1, 0, 0)));
62 | hitable *world = new hitable_list(list,2);
63 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
64 |
65 | gives:
66 |
67 | 
68 |
69 | To get an arbitrary viewpoint, let’s first name the points we care about. We’ll call the position
70 | where we place the camera _lookfrom_, and the point we look at _lookat_. (Later, if you want, you
71 | could define a direction to look in instead of a point to look at.)
72 |
73 | We also need a way to specify the roll, or sideways tilt, of the camera; the rotation around the
74 | lookat-lookfrom axis. Another way to think about it is even if you keep `lookfrom` and `lookat`
75 | constant, you can still rotate your head around your nose. What we need is a way to specify an up
76 | vector for the camera. Notice we already we already have a plane that the up vector should be in,
77 | the plane orthogonal to the view direction.
78 |
79 | 
80 |
81 | We can actually use any up vector we want, and simply project it onto this plane to get an up vector
82 | for the camera. I use the common convention of naming a “view up” (_vup_) vector. A couple of cross
83 | products, and we now have a complete orthonormal basis (u,v,w) to describe our camera’s orientation.
84 |
85 | 
86 |
87 | Remember that `vup`, `v`, and `w` are all in the same plane. Note that, like before when our fixed
88 | camera faced -Z, our arbitrary view camera faces -w. And keep in mind that we can -- but we don’t
89 | have to -- use world up (0,1,0) to specify vup. This is convenient and will naturally keep your
90 | camera horizontally level until you decide to experiment with crazy camera angles.
91 |
92 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
93 | #ifndef CAMERAH
94 | #define CAMERAH
95 |
96 | #include "ray.h"
97 |
98 | class camera {
99 | public:
100 | camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect) {
101 | // vfov is top to bottom in degrees
102 | vec3 u, v, w;
103 | float theta = vfov*M_PI/180;
104 | float half_height = tan(theta/2);
105 | float half_width = aspect * half_height;
106 | origin = lookatfrom;
107 | w = unit_vector(lookfrom - lookat);
108 | u = unit_vector(cross(vup, w));
109 | v = cross(w, u);
110 | lower_left_corner = origin - half_width*u - half_height*v - w;
111 | horizontal = 2*half_width*u;
112 | vertical = 2*half_height*v;
113 | }
114 | ray get_ray(float s, float t) {
115 | return ray(origin,
116 | lower_left_corner + s*horizontal + t*vertical - origin);
117 | }
118 |
119 | vec3 origin;
120 | vec3 lower_left_corner;
121 | vec3 horizontal;
122 | vec3 vertical;
123 | };
124 | #endif
125 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
126 |
127 | This allows us to change the viewpoint:
128 |
129 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
130 | camera cam(vec3(-2,2,1), vec3(0,0,-1), vec3(0,1,0), 90, float(nx)/float(ny));
131 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
132 |
133 | to get:
134 |
135 | 
136 |
137 | And we can change field of view to get:
138 |
139 | 
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/book/ch11.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 11: Defocus Blur**
12 |
13 | Now our final feature: defocus blur. Note, all photographers will call it “depth of field” so be
14 | aware of only using “defocus blur” among friends.
15 |
16 | The reason we defocus blur in real cameras is because they need a big hole (rather than just a
17 | pinhole) to gather light. This would defocus everything, but if we stick a lens in the hole, there
18 | will be a certain distance where everything is in focus. The distance to that plane where things are
19 | in focus is controlled by the distance between the lens and the film/sensor. That is why you see the
20 | lens move relative to the camera when you change what is in focus (that may happen in your phone
21 | camera too, but the sensor moves). The “aperture” is a hole to control how big the lens is
22 | effectively. For a real camera, if you need more light you make the aperture bigger, and will get
23 | more defocus blur. For our virtual camera, we can have a perfect sensor and never need more light,
24 | so we only have an aperture when we want defocus blur.
25 |
26 | A real camera has a compound lens that is complicated. For our code we could simulate the order:
27 | sensor, then lens, then aperture, and figure out where to send the rays and flip the image once
28 | computed (the image is projected upside down on the film). Graphics people usually use a thin lens
29 | approximation.
30 |
31 | 
32 |
33 | We also don’t need to simulate any of the inside of the camera. For the purposes of rendering an
34 | image outside the camera, that would be unnecessary complexity. Instead I usually start rays from
35 | the surface of the lens, and send them toward a virtual film plane, by finding the projection of the
36 | film on the plane that is in focus (at the distance `focus_dist`).
37 |
38 | 
39 |
40 | For that we just need to have the ray origins be on a disk around `lookfrom` rather than from a
41 | point:
42 |
43 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
44 | #ifndef CAMERAH
45 | #define CAMERAH
46 |
47 | #include "random.h"
48 | #include "ray.h"
49 |
50 | vec3 random_in_unit_disk() {
51 | vec3 p;
52 | do {
53 | p = 2.0*vec3(random_double(),random_double(),0) - vec3(1,1,0);
54 | } while (dot(p,p) >= 1.0);
55 | return p;
56 | }
57 |
58 | class camera {
59 | public:
60 | camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect,
61 | float aperture, float focus_dist)
62 | {
63 | lens_radius = aperture / 2;
64 | float theta = vfov*M_PI/180;
65 | float half_height = tan(theta/2);
66 | float half_width = aspect * half_height;
67 | origin = lookfrom;
68 | w = unit_vector(lookfrom - lookat);
69 | u = unit_vector(cross(vup, w));
70 | v = cross(w, u);
71 | lower_left_corner = origin
72 | - half_width * focus_dist * u
73 | - half_height * focus_dist * v
74 | - focus_dist * w;
75 | horizontal = 2*half_width*focus_dist*u;
76 | vertical = 2*half_height*focus_dist*v;
77 | }
78 | ray get_ray(float s, float t) {
79 | vec3 rd = lens_radius*random_in_unit_disk();
80 | vec3 offset = u * rd.x() + v * rd.y();
81 | return ray(origin + offset,
82 | lower_left_corner + s*horizontal + t*vertical
83 | - origin - offset);
84 | }
85 |
86 | vec3 origin;
87 | vec3 lower_left_corner;
88 | vec3 horizontal;
89 | vec3 vertical;
90 | vec3 u, v, w;
91 | float lens_radius;
92 | };
93 | #endif
94 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
95 |
96 | Using a big aperture:
97 |
98 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
99 | vec3 lookfrom(3,3,2);
100 | vec3 lookat(0,0,-1);
101 | float dist_to_focus = (lookfrom-lookat).length();
102 | float aperture = 2.0;
103 |
104 | camera cam(lookfrom, lookat, vec3(0,1,0), 20,
105 | float(nx)/float(ny), aperture, dist_to_focus);
106 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107 |
108 | We get:
109 |
110 | 
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/book/ch12.md.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Chapter 12: Where next?**
12 |
13 | First let’s make the image on the cover of this book -- lots of random spheres:
14 |
15 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
16 | hitable *random_scene() {
17 | int n = 500;
18 | hitable **list = new hitable*[n+1];
19 | list[0] = new sphere(vec3(0,-1000,0), 1000, new lambertian(vec3(0.5, 0.5, 0.5)));
20 | int i = 1;
21 | for (int a = -11; a < 11; a++) {
22 | for (int b = -11; b < 11; b++) {
23 | float choose_mat = random_double();
24 | vec3 center(a+0.9*random_double(),0.2,b+0.9*random_double());
25 | if ((center-vec3(4,0.2,0)).length() > 0.9) {
26 | if (choose_mat < 0.8) { // diffuse
27 | list[i++] = new sphere(center, 0.2,
28 | new lambertian(vec3(random_double()*random_double(),
29 | random_double()*random_double(),
30 | random_double()*random_double())
31 | )
32 | );
33 | }
34 | else if (choose_mat < 0.95) { // metal
35 | list[i++] = new sphere(center, 0.2,
36 | new metal(vec3(0.5*(1 + random_double()),
37 | 0.5*(1 + random_double()),
38 | 0.5*(1 + random_double())),
39 | 0.5*random_double()));
40 | }
41 | else { // glass
42 | list[i++] = new sphere(center, 0.2, new dielectric(1.5));
43 | }
44 | }
45 | }
46 | }
47 |
48 | list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5));
49 | list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1)));
50 | list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0));
51 |
52 | return new hitable_list(list,i);
53 | }
54 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
55 |
56 | This gives:
57 |
58 | 
59 |
60 | An interesting thing you might note is the glass balls don’t really have shadows which makes them
61 | look like they are floating. This is not a bug (you don’t see glass balls much in real life, where
62 | they also look a bit strange and indeed seem to float on cloudy days). A point on the big sphere
63 | under a glass ball still has lots of light hitting it because the sky is re-ordered rather than
64 | blocked.
65 |
66 | You now have a cool ray tracer! What next?
67 |
68 | 1. Lights. You can do this explicitly, by sending shadow rays to lights. Or it can be done
69 | implicitly by making some objects emit light,
70 |
71 | 2. Biasing scattered rays toward them, and then downweighting those rays to cancel out the bias.
72 | Both work. I am in the minority in favoring the latter approach.
73 |
74 | 3. Triangles. Most cool models are in triangle form. The model I/O is the worst and almost
75 | everybody tries to get somebody else’s code to do this.
76 |
77 | 4. Surface textures. This lets you paste images on like wall paper. Pretty easy and a good thing
78 | to do.
79 |
80 | 5. Solid textures. Ken Perlin has his code online. Andrew Kensler has some very cool info at his
81 | blog.
82 |
83 | 6. Volumes and media. Cool stuff and will challenge your software architecture. I favor making
84 | volumes have the hitable interface and probabilistically have intersections based on density.
85 | Your rendering code doesn’t even have to know it has volumes with that method.
86 |
87 | 7. Parallelism. Run $N$ copies of your code on $N$ cores with different random seeds. Average the
88 | $N$ runs. This averaging can also be done hierarchically where $N/2$ pairs can be averaged to
89 | get $N/4$ images, and pairs of those can be averaged. That method of parallelism should extend
90 | well into the thousands of cores with very little coding.
91 |
92 | Have fun, and please send me your cool images!
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
WARNING Content Under Development
5 |
6 | See release page for latest official PDF version.
7 |
8 |
9 |
10 |
11 | **Book Rough Content**
12 |
13 | - [Acknowledgements](book/ack.md.html)
14 | - [Chapter 0](book/ch00.md.html)
15 | - [Chapter 1](book/ch01.md.html)
16 | - [Chapter 2](book/ch02.md.html)
17 | - [Chapter 3](book/ch03.md.html)
18 | - [Chapter 4](book/ch04.md.html)
19 | - [Chapter 5](book/ch05.md.html)
20 | - [Chapter 6](book/ch06.md.html)
21 | - [Chapter 7](book/ch07.md.html)
22 | - [Chapter 8](book/ch08.md.html)
23 | - [Chapter 9](book/ch09.md.html)
24 | - [Chapter 10](book/ch10.md.html)
25 | - [Chapter 11](book/ch11.md.html)
26 | - [Chapter 12](book/ch12.md.html)
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/camera.h:
--------------------------------------------------------------------------------
1 | #ifndef CAMERAH
2 | #define CAMERAH
3 | //==================================================================================================
4 | // Written in 2016 by Peter Shirley
5 | //
6 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
7 | // neighboring rights to this software to the public domain worldwide. This software is distributed
8 | // without any warranty.
9 | //
10 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
11 | // with this software. If not, see .
12 | //==================================================================================================
13 |
14 | #include "ray.h"
15 | #include "random.h"
16 |
17 | vec3 random_in_unit_disk() {
18 | vec3 p;
19 | do {
20 | p = 2.0*vec3(random_double(),random_double(),0) - vec3(1,1,0);
21 | } while (dot(p,p) >= 1.0);
22 | return p;
23 | }
24 |
25 | class camera {
26 | public:
27 | camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect, float aperture, float focus_dist) {
28 | // vfov is top to bottom in degrees
29 | lens_radius = aperture / 2;
30 | float theta = vfov*M_PI/180;
31 | float half_height = tan(theta/2);
32 | float half_width = aspect * half_height;
33 | origin = lookfrom;
34 | w = unit_vector(lookfrom - lookat);
35 | u = unit_vector(cross(vup, w));
36 | v = cross(w, u);
37 | lower_left_corner = origin - half_width*focus_dist*u -half_height*focus_dist*v - focus_dist*w;
38 | horizontal = 2*half_width*focus_dist*u;
39 | vertical = 2*half_height*focus_dist*v;
40 | }
41 | ray get_ray(float s, float t) {
42 | vec3 rd = lens_radius*random_in_unit_disk();
43 | vec3 offset = u * rd.x() + v * rd.y();
44 | return ray(origin + offset, lower_left_corner + s*horizontal + t*vertical - origin - offset);
45 | }
46 |
47 | vec3 origin;
48 | vec3 lower_left_corner;
49 | vec3 horizontal;
50 | vec3 vertical;
51 | vec3 u, v, w;
52 | float lens_radius;
53 | };
54 |
55 |
56 | #endif
57 |
--------------------------------------------------------------------------------
/src/hitable.h:
--------------------------------------------------------------------------------
1 | #ifndef HITABLEH
2 | #define HITABLEH
3 | //==================================================================================================
4 | // Written in 2016 by Peter Shirley
5 | //
6 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
7 | // neighboring rights to this software to the public domain worldwide. This software is distributed
8 | // without any warranty.
9 | //
10 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
11 | // with this software. If not, see .
12 | //==================================================================================================
13 |
14 | #include "ray.h"
15 |
16 | class material;
17 |
18 | struct hit_record
19 | {
20 | float t;
21 | vec3 p;
22 | vec3 normal;
23 | material *mat_ptr;
24 | };
25 |
26 | class hitable {
27 | public:
28 | virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const = 0;
29 | };
30 |
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/src/hitable_list.h:
--------------------------------------------------------------------------------
1 | #ifndef HITABLELISTH
2 | #define HITABLELISTH
3 | //==================================================================================================
4 | // Written in 2016 by Peter Shirley
5 | //
6 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
7 | // neighboring rights to this software to the public domain worldwide. This software is distributed
8 | // without any warranty.
9 | //
10 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
11 | // with this software. If not, see .
12 | //==================================================================================================
13 |
14 | #include "hitable.h"
15 |
16 | class hitable_list: public hitable {
17 | public:
18 | hitable_list() {}
19 | hitable_list(hitable **l, int n) { list = l; list_size = n; }
20 | virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
21 | hitable **list;
22 | int list_size;
23 | };
24 |
25 | bool hitable_list::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
26 | hit_record temp_rec;
27 | bool hit_anything = false;
28 | double closest_so_far = t_max;
29 | for (int i = 0; i < list_size; i++) {
30 | if (list[i]->hit(r, t_min, closest_so_far, temp_rec)) {
31 | hit_anything = true;
32 | closest_so_far = temp_rec.t;
33 | rec = temp_rec;
34 | }
35 | }
36 | return hit_anything;
37 | }
38 |
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/src/main.cc:
--------------------------------------------------------------------------------
1 | //==================================================================================================
2 | // Written in 2016 by Peter Shirley
3 | //
4 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
5 | // neighboring rights to this software to the public domain worldwide. This software is distributed
6 | // without any warranty.
7 | //
8 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
9 | // with this software. If not, see .
10 | //==================================================================================================
11 |
12 | #include
13 | #include "sphere.h"
14 | #include "hitable_list.h"
15 | #include "float.h"
16 | #include "camera.h"
17 | #include "material.h"
18 | #include "random.h"
19 |
20 |
21 | vec3 color(const ray& r, hitable *world, int depth) {
22 | hit_record rec;
23 | if (world->hit(r, 0.001, MAXFLOAT, rec)) {
24 | ray scattered;
25 | vec3 attenuation;
26 | if (depth < 50 && rec.mat_ptr->scatter(r, rec, attenuation, scattered)) {
27 | return attenuation*color(scattered, world, depth+1);
28 | }
29 | else {
30 | return vec3(0,0,0);
31 | }
32 | }
33 | else {
34 | vec3 unit_direction = unit_vector(r.direction());
35 | float t = 0.5*(unit_direction.y() + 1.0);
36 | return (1.0-t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
37 | }
38 | }
39 |
40 |
41 | hitable *random_scene() {
42 | int n = 500;
43 | hitable **list = new hitable*[n+1];
44 | list[0] = new sphere(vec3(0,-1000,0), 1000, new lambertian(vec3(0.5, 0.5, 0.5)));
45 | int i = 1;
46 | for (int a = -11; a < 11; a++) {
47 | for (int b = -11; b < 11; b++) {
48 | float choose_mat = random_double();
49 | vec3 center(a+0.9*random_double(),0.2,b+0.9*random_double());
50 | if ((center-vec3(4,0.2,0)).length() > 0.9) {
51 | if (choose_mat < 0.8) { // diffuse
52 | list[i++] = new sphere(
53 | center, 0.2,
54 | new lambertian(vec3(random_double()*random_double(),
55 | random_double()*random_double(),
56 | random_double()*random_double()))
57 | );
58 | }
59 | else if (choose_mat < 0.95) { // metal
60 | list[i++] = new sphere(
61 | center, 0.2,
62 | new metal(vec3(0.5*(1 + random_double()),
63 | 0.5*(1 + random_double()),
64 | 0.5*(1 + random_double())),
65 | 0.5*random_double())
66 | );
67 | }
68 | else { // glass
69 | list[i++] = new sphere(center, 0.2, new dielectric(1.5));
70 | }
71 | }
72 | }
73 | }
74 |
75 | list[i++] = new sphere(vec3(0, 1, 0), 1.0, new dielectric(1.5));
76 | list[i++] = new sphere(vec3(-4, 1, 0), 1.0, new lambertian(vec3(0.4, 0.2, 0.1)));
77 | list[i++] = new sphere(vec3(4, 1, 0), 1.0, new metal(vec3(0.7, 0.6, 0.5), 0.0));
78 |
79 | return new hitable_list(list,i);
80 | }
81 |
82 |
83 | int main() {
84 | int nx = 1200;
85 | int ny = 800;
86 | int ns = 10;
87 | std::cout << "P3\n" << nx << " " << ny << "\n255\n";
88 | hitable *world = random_scene();
89 |
90 | vec3 lookfrom(13,2,3);
91 | vec3 lookat(0,0,0);
92 | float dist_to_focus = 10.0;
93 | float aperture = 0.1;
94 |
95 | camera cam(lookfrom, lookat, vec3(0,1,0), 20, float(nx)/float(ny), aperture, dist_to_focus);
96 |
97 | for (int j = ny-1; j >= 0; j--) {
98 | for (int i = 0; i < nx; i++) {
99 | vec3 col(0, 0, 0);
100 | for (int s=0; s < ns; s++) {
101 | float u = float(i + random_double()) / float(nx);
102 | float v = float(j + random_double()) / float(ny);
103 | ray r = cam.get_ray(u, v);
104 | col += color(r, world,0);
105 | }
106 | col /= float(ns);
107 | col = vec3( sqrt(col[0]), sqrt(col[1]), sqrt(col[2]) );
108 | int ir = int(255.99*col[0]);
109 | int ig = int(255.99*col[1]);
110 | int ib = int(255.99*col[2]);
111 | std::cout << ir << " " << ig << " " << ib << "\n";
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/material.h:
--------------------------------------------------------------------------------
1 | #ifndef MATERIALH
2 | #define MATERIALH
3 | //==================================================================================================
4 | // Written in 2016 by Peter Shirley
5 | //
6 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
7 | // neighboring rights to this software to the public domain worldwide. This software is distributed
8 | // without any warranty.
9 | //
10 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
11 | // with this software. If not, see .
12 | //==================================================================================================
13 |
14 | #include "ray.h"
15 | #include "hitable.h"
16 | #include "random.h"
17 |
18 | struct hit_record;
19 |
20 |
21 | float schlick(float cosine, float ref_idx) {
22 | float r0 = (1-ref_idx) / (1+ref_idx);
23 | r0 = r0*r0;
24 | return r0 + (1-r0)*pow((1 - cosine),5);
25 | }
26 |
27 |
28 | bool refract(const vec3& v, const vec3& n, float ni_over_nt, vec3& refracted) {
29 | vec3 uv = unit_vector(v);
30 | float dt = dot(uv, n);
31 | float discriminant = 1.0 - ni_over_nt*ni_over_nt*(1-dt*dt);
32 | if (discriminant > 0) {
33 | refracted = ni_over_nt*(uv - n*dt) - n*sqrt(discriminant);
34 | return true;
35 | }
36 | else
37 | return false;
38 | }
39 |
40 |
41 | vec3 reflect(const vec3& v, const vec3& n) {
42 | return v - 2*dot(v,n)*n;
43 | }
44 |
45 |
46 | vec3 random_in_unit_sphere() {
47 | vec3 p;
48 | do {
49 | p = 2.0*vec3(random_double(),random_double(),random_double()) - vec3(1,1,1);
50 | } while (p.squared_length() >= 1.0);
51 | return p;
52 | }
53 |
54 |
55 | class material {
56 | public:
57 | virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const = 0;
58 | };
59 |
60 |
61 | class lambertian : public material {
62 | public:
63 | lambertian(const vec3& a) : albedo(a) {}
64 | virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const {
65 | vec3 target = rec.p + rec.normal + random_in_unit_sphere();
66 | scattered = ray(rec.p, target-rec.p);
67 | attenuation = albedo;
68 | return true;
69 | }
70 |
71 | vec3 albedo;
72 | };
73 |
74 |
75 | class metal : public material {
76 | public:
77 | metal(const vec3& a, float f) : albedo(a) { if (f < 1) fuzz = f; else fuzz = 1; }
78 | virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const {
79 | vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
80 | scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere());
81 | attenuation = albedo;
82 | return (dot(scattered.direction(), rec.normal) > 0);
83 | }
84 | vec3 albedo;
85 | float fuzz;
86 | };
87 |
88 |
89 | class dielectric : public material {
90 | public:
91 | dielectric(float ri) : ref_idx(ri) {}
92 | virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const {
93 | vec3 outward_normal;
94 | vec3 reflected = reflect(r_in.direction(), rec.normal);
95 | float ni_over_nt;
96 | attenuation = vec3(1.0, 1.0, 1.0);
97 | vec3 refracted;
98 | float reflect_prob;
99 | float cosine;
100 | if (dot(r_in.direction(), rec.normal) > 0) {
101 | outward_normal = -rec.normal;
102 | ni_over_nt = ref_idx;
103 | // cosine = ref_idx * dot(r_in.direction(), rec.normal) / r_in.direction().length();
104 | cosine = dot(r_in.direction(), rec.normal) / r_in.direction().length();
105 | cosine = sqrt(1 - ref_idx*ref_idx*(1-cosine*cosine));
106 | }
107 | else {
108 | outward_normal = rec.normal;
109 | ni_over_nt = 1.0 / ref_idx;
110 | cosine = -dot(r_in.direction(), rec.normal) / r_in.direction().length();
111 | }
112 | if (refract(r_in.direction(), outward_normal, ni_over_nt, refracted))
113 | reflect_prob = schlick(cosine, ref_idx);
114 | else
115 | reflect_prob = 1.0;
116 | if (random_double() < reflect_prob)
117 | scattered = ray(rec.p, reflected);
118 | else
119 | scattered = ray(rec.p, refracted);
120 | return true;
121 | }
122 |
123 | float ref_idx;
124 | };
125 |
126 |
127 | #endif
128 |
--------------------------------------------------------------------------------
/src/random.h:
--------------------------------------------------------------------------------
1 | #ifndef RANDOMH
2 | #define RANDOMH
3 |
4 | #include
5 |
6 | inline double random_double() {
7 | return rand() / (RAND_MAX + 1.0);
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/src/ray.h:
--------------------------------------------------------------------------------
1 | #ifndef RAYH
2 | #define RAYH
3 | //==================================================================================================
4 | // Written in 2016 by Peter Shirley
5 | //
6 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
7 | // neighboring rights to this software to the public domain worldwide. This software is distributed
8 | // without any warranty.
9 | //
10 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
11 | // with this software. If not, see .
12 | //==================================================================================================
13 |
14 | #include "vec3.h"
15 |
16 |
17 | class ray
18 | {
19 | public:
20 | ray() {}
21 | ray(const vec3& a, const vec3& b) { A = a; B = b; }
22 | vec3 origin() const { return A; }
23 | vec3 direction() const { return B; }
24 | vec3 point_at_parameter(float t) const { return A + t*B; }
25 |
26 | vec3 A;
27 | vec3 B;
28 | };
29 |
30 |
31 | #endif
32 |
--------------------------------------------------------------------------------
/src/sphere.h:
--------------------------------------------------------------------------------
1 | #ifndef SPHEREH
2 | #define SPHEREH
3 | //==================================================================================================
4 | // Written in 2016 by Peter Shirley
5 | //
6 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
7 | // neighboring rights to this software to the public domain worldwide. This software is distributed
8 | // without any warranty.
9 | //
10 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
11 | // with this software. If not, see .
12 | //==================================================================================================
13 |
14 | #include "hitable.h"
15 |
16 |
17 | class sphere: public hitable {
18 | public:
19 | sphere() {}
20 | sphere(vec3 cen, float r, material *m) : center(cen), radius(r), mat_ptr(m) {};
21 | virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
22 | vec3 center;
23 | float radius;
24 | material *mat_ptr;
25 | };
26 |
27 |
28 | bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
29 | vec3 oc = r.origin() - center;
30 | float a = dot(r.direction(), r.direction());
31 | float b = dot(oc, r.direction());
32 | float c = dot(oc, oc) - radius*radius;
33 | float discriminant = b*b - a*c;
34 | if (discriminant > 0) {
35 | float temp = (-b - sqrt(discriminant))/a;
36 | if (temp < t_max && temp > t_min) {
37 | rec.t = temp;
38 | rec.p = r.point_at_parameter(rec.t);
39 | rec.normal = (rec.p - center) / radius;
40 | rec.mat_ptr = mat_ptr;
41 | return true;
42 | }
43 | temp = (-b + sqrt(discriminant)) / a;
44 | if (temp < t_max && temp > t_min) {
45 | rec.t = temp;
46 | rec.p = r.point_at_parameter(rec.t);
47 | rec.normal = (rec.p - center) / radius;
48 | rec.mat_ptr = mat_ptr;
49 | return true;
50 | }
51 | }
52 | return false;
53 | }
54 |
55 |
56 | #endif
57 |
--------------------------------------------------------------------------------
/src/vec3.h:
--------------------------------------------------------------------------------
1 | #ifndef VEC3H
2 | #define VEC3H
3 | //==================================================================================================
4 | // Written in 2016 by Peter Shirley
5 | //
6 | // To the extent possible under law, the author(s) have dedicated all copyright and related and
7 | // neighboring rights to this software to the public domain worldwide. This software is distributed
8 | // without any warranty.
9 | //
10 | // You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication along
11 | // with this software. If not, see .
12 | //==================================================================================================
13 |
14 | #include
15 | #include
16 | #include
17 |
18 |
19 | class vec3 {
20 | public:
21 | vec3() {}
22 | vec3(float e0, float e1, float e2) { e[0] = e0; e[1] = e1; e[2] = e2; }
23 | inline float x() const { return e[0]; }
24 | inline float y() const { return e[1]; }
25 | inline float z() const { return e[2]; }
26 | inline float r() const { return e[0]; }
27 | inline float g() const { return e[1]; }
28 | inline float b() const { return e[2]; }
29 |
30 | inline const vec3& operator+() const { return *this; }
31 | inline vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }
32 | inline float operator[](int i) const { return e[i]; }
33 | inline float& operator[](int i) { return e[i]; }
34 |
35 | inline vec3& operator+=(const vec3 &v2);
36 | inline vec3& operator-=(const vec3 &v2);
37 | inline vec3& operator*=(const vec3 &v2);
38 | inline vec3& operator/=(const vec3 &v2);
39 | inline vec3& operator*=(const float t);
40 | inline vec3& operator/=(const float t);
41 |
42 | inline float length() const { return sqrt(e[0]*e[0] + e[1]*e[1] + e[2]*e[2]); }
43 | inline float squared_length() const { return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; }
44 | inline void make_unit_vector();
45 |
46 | float e[3];
47 | };
48 |
49 |
50 | inline std::istream& operator>>(std::istream &is, vec3 &t) {
51 | is >> t.e[0] >> t.e[1] >> t.e[2];
52 | return is;
53 | }
54 |
55 | inline std::ostream& operator<<(std::ostream &os, const vec3 &t) {
56 | os << t.e[0] << " " << t.e[1] << " " << t.e[2];
57 | return os;
58 | }
59 |
60 | inline void vec3::make_unit_vector() {
61 | float k = 1.0 / sqrt(e[0]*e[0] + e[1]*e[1] + e[2]*e[2]);
62 | e[0] *= k; e[1] *= k; e[2] *= k;
63 | }
64 |
65 | inline vec3 operator+(const vec3 &v1, const vec3 &v2) {
66 | return vec3(v1.e[0] + v2.e[0], v1.e[1] + v2.e[1], v1.e[2] + v2.e[2]);
67 | }
68 |
69 | inline vec3 operator-(const vec3 &v1, const vec3 &v2) {
70 | return vec3(v1.e[0] - v2.e[0], v1.e[1] - v2.e[1], v1.e[2] - v2.e[2]);
71 | }
72 |
73 | inline vec3 operator*(const vec3 &v1, const vec3 &v2) {
74 | return vec3(v1.e[0] * v2.e[0], v1.e[1] * v2.e[1], v1.e[2] * v2.e[2]);
75 | }
76 |
77 | inline vec3 operator/(const vec3 &v1, const vec3 &v2) {
78 | return vec3(v1.e[0] / v2.e[0], v1.e[1] / v2.e[1], v1.e[2] / v2.e[2]);
79 | }
80 |
81 | inline vec3 operator*(float t, const vec3 &v) {
82 | return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
83 | }
84 |
85 | inline vec3 operator/(vec3 v, float t) {
86 | return vec3(v.e[0]/t, v.e[1]/t, v.e[2]/t);
87 | }
88 |
89 | inline vec3 operator*(const vec3 &v, float t) {
90 | return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
91 | }
92 |
93 | inline float dot(const vec3 &v1, const vec3 &v2) {
94 | return v1.e[0] * v2.e[0]
95 | + v1.e[1] * v2.e[1]
96 | + v1.e[2] * v2.e[2];
97 | }
98 |
99 | inline vec3 cross(const vec3 &v1, const vec3 &v2) {
100 | return vec3(v1.e[1] * v2.e[2] - v1.e[2] * v2.e[1],
101 | v1.e[2] * v2.e[0] - v1.e[0] * v2.e[2],
102 | v1.e[0] * v2.e[1] - v1.e[1] * v2.e[0]);
103 | }
104 |
105 | inline vec3& vec3::operator+=(const vec3 &v){
106 | e[0] += v.e[0];
107 | e[1] += v.e[1];
108 | e[2] += v.e[2];
109 | return *this;
110 | }
111 |
112 | inline vec3& vec3::operator*=(const vec3 &v){
113 | e[0] *= v.e[0];
114 | e[1] *= v.e[1];
115 | e[2] *= v.e[2];
116 | return *this;
117 | }
118 |
119 | inline vec3& vec3::operator/=(const vec3 &v){
120 | e[0] /= v.e[0];
121 | e[1] /= v.e[1];
122 | e[2] /= v.e[2];
123 | return *this;
124 | }
125 |
126 | inline vec3& vec3::operator-=(const vec3& v) {
127 | e[0] -= v.e[0];
128 | e[1] -= v.e[1];
129 | e[2] -= v.e[2];
130 | return *this;
131 | }
132 |
133 | inline vec3& vec3::operator*=(const float t) {
134 | e[0] *= t;
135 | e[1] *= t;
136 | e[2] *= t;
137 | return *this;
138 | }
139 |
140 | inline vec3& vec3::operator/=(const float t) {
141 | float k = 1.0f/t;
142 |
143 | e[0] *= k;
144 | e[1] *= k;
145 | e[2] *= k;
146 | return *this;
147 | }
148 |
149 | inline vec3 unit_vector(vec3 v) {
150 | return v / v.length();
151 | }
152 |
153 |
154 | #endif
155 |
--------------------------------------------------------------------------------