├── .gitignore
├── 3d-viewing-pinhole-camera
├── boat.obj
├── geometry.h
└── pinhole.cpp
├── README.md
├── bezier-curve-rendering-utah-teapot
├── geometry.h
├── teapot.cpp
└── teapotdata.h
├── build-raytracer-core
├── area-light-impl.cc
├── area-light-impl.ilk
├── area-light.cc
├── intro-lighting-kraemer-remapping-quad-tri.gif
├── intro-lighting-quadtree.gif
├── math.h
├── mc-integration.cc
├── pointlight.cc
├── quad.cc
├── quad.ilk
├── random_dir.cc
├── source.mel
├── spotlight.cc
├── test.cc
└── triangle-sampling.cc
├── cam-nav-controls
└── 3d-nav-controls.cc
├── colors
└── mcbeth.cpp
├── computing-pixel-coordinates-of-3d-point
├── geometry.h
├── perspproj.cpp
└── xtree.obj
├── digital-images
├── readwrite.cpp
└── xmas.ppm
├── geometry
└── geometry.cpp
├── global-illumination-path-tracing
├── geometry.h
├── indirectdiffuse.cpp
└── objects.zip
├── interpolation
├── .gitignore
└── interpolation.cpp
├── introduction-acceleration-structure
├── acceleration.cpp
└── teapotdata.h
├── introduction-polygon-mesh
├── cow.geo
├── geometry.h
└── raytracepolymesh.cpp
├── introduction-rendering
├── raytracer
├── raytracer.cpp
└── untitled.ppm
├── introduction-to-lighting
├── .gitignore
├── area-light-impl.cc
├── math.h
├── pointlight.cc
└── spotlight.cc
├── introduction-to-ray-tracing
└── raytracer.cpp
├── introduction-to-shading
├── geometry.zip
└── shading.cpp
├── introduction-to-texturing
├── cube1.h
├── pixar-texture3.pbm
├── sphere.h
└── texturing.c
├── matrix-inverse
└── MatrixInverse.cpp
├── minimal-ray-tracer-rendering-simple-shapes
├── geometry.h
├── raybox.cpp
└── simpleshapes.cpp
├── monte-carlo-methods-in-practice
├── mcintegration.cpp
└── mcsim.cpp
├── obj-file-format
├── objimporter.cc
└── zombie.obj
├── perlin-noise-part-2
└── perlinnoise.cpp
├── perspective-and-orthographic-projection-matrix
├── geometry.h
├── glorthoprojmatrix.cpp
├── glprojmatrix.cpp
├── projmatrix.cpp
├── teapot.obj
└── vertexdata.h
├── phong-shader-BRDF
├── geometry.h
└── phong.cpp
├── polygon-mesh
├── cow.geo
├── geometry.h
├── loadgeometry.cpp
├── raster3d.cpp
└── test.geo
├── procedural-patterns-noise-part-1
└── noise.cpp
├── rasterization-practical-implementation
├── cow.h
├── cow.obj
├── geometry.h
└── raster3d.cpp
├── ray-tracing-generating-camera-rays
├── camerarays.cpp
└── geometry.h
├── ray-tracing-overview
└── whitted.cpp
├── ray-tracing-rendering-a-triangle
├── geometry.h
└── raytri.cpp
├── simple-image-manipulations
├── image.cpp
└── testimages.zip
├── simulating-sky
└── skycolor.cpp
├── transforming-objects-using-matrices
├── geometry.h
├── raytracetransform.cpp
└── teapot.geo
├── volume-rendering-for-developers
├── cachefiles.zip
├── raymarch-chap2.cpp
├── raymarch-chap3.cpp
├── raymarch-chap4.cpp
├── raymarch-chap5.cpp
└── raymarch-chap6.cpp
└── windowing
├── sample.pbm
└── window.cc
/.gitignore:
--------------------------------------------------------------------------------
1 | *.exe
2 | *.ppm
3 | *.pbm
4 | *.pdb
5 | fourier-transform/
6 |
--------------------------------------------------------------------------------
/3d-viewing-pinhole-camera/boat.obj:
--------------------------------------------------------------------------------
1 | v -2.570266 0.780534 -0.000024
2 | v -0.892643 0.022582 0.018577
3 | v 1.687845 -0.017131 0.022032
4 | v 3.465899 0.025667 0.018577
5 | v -2.570266 0.789692 -0.001202
6 | v -0.892643 0.251213 0.935730
7 | v 1.687845 0.251213 1.109724
8 | v 3.503150 0.252933 0.935730
9 | v -2.570266 1.055815 -0.001347
10 | v -0.892643 1.055815 1.048658
11 | v 1.687845 1.055815 1.243651
12 | v 3.634242 1.052729 1.048658
13 | v -2.570266 1.055815 0.000000
14 | v -0.892643 1.055815 0.000000
15 | v 1.687845 1.055815 0.000000
16 | v 3.634242 1.052729 0.000000
17 | v -2.570266 1.055815 0.001347
18 | v -0.892643 1.055815 -1.048658
19 | v 1.687845 1.055815 -1.243651
20 | v 3.634242 1.052729 -1.048658
21 | v -2.570266 0.789692 0.001202
22 | v -0.892643 0.251213 -0.935730
23 | v 1.687845 0.251213 -1.109724
24 | v 3.503150 0.252933 -0.935730
25 | v 3.503150 0.252933 0.000000
26 | v -2.570266 0.789692 0.000000
27 | v 1.109142 1.217887 0.000000
28 | v 1.145029 6.617012 0.000000
29 | v 4.087809 1.238261 0.000000
30 | v -2.569333 1.177139 -0.081683
31 | v 0.983535 6.494768 -0.081683
32 | v -0.721124 1.136391 -0.081683
33 | v 0.929704 6.454020 0.000000
34 | v -0.792899 1.279010 0.000000
35 | v 0.911760 1.299384 0.000000
36 | f 5 1 6
37 | f 1 2 6
38 | f 2 3 6
39 | f 6 3 7
40 | f 4 8 3
41 | f 3 8 7
42 | f 6 10 5
43 | f 5 10 9
44 | f 6 7 10
45 | f 10 7 11
46 | f 8 12 7
47 | f 7 12 11
48 | f 10 14 9
49 | f 9 14 13
50 | f 11 15 10
51 | f 10 15 14
52 | f 11 12 15
53 | f 15 12 16
54 | f 18 17 14
55 | f 13 14 17
56 | f 14 15 18
57 | f 18 15 19
58 | f 16 20 15
59 | f 15 20 19
60 | f 17 18 21
61 | f 21 18 22
62 | f 19 23 18
63 | f 18 23 22
64 | f 19 20 23
65 | f 23 20 24
66 | f 21 22 1
67 | f 22 2 1
68 | f 23 3 22
69 | f 22 3 2
70 | f 23 24 3
71 | f 3 24 4
72 | f 4 24 25
73 | f 4 25 8
74 | f 25 24 16
75 | f 16 24 20
76 | f 25 16 8
77 | f 8 16 12
78 | f 1 26 21
79 | f 1 5 26
80 | f 21 26 17
81 | f 17 26 13
82 | f 26 5 13
83 | f 13 5 9
84 | f 27 28 29
85 | f 30 31 32
86 | f 33 35 34
--------------------------------------------------------------------------------
/3d-viewing-pinhole-camera/pinhole.cpp:
--------------------------------------------------------------------------------
1 | //[header]
2 | // This program implements a physical pinhole camera model similar to the model
3 | // used in popular 3D packages such as Maya.
4 | //[/header]
5 | //[compile]
6 | // Download the pinhole.cpp and geometry.h files to the same folder.
7 | // Open a shell/terminal, and run the following command where the files are saved:
8 | //
9 | // c++ pinhole.cpp -o pinhole -std=c++11
10 | //
11 | // Run with: ./pinhole. Open the file ./pinhole.svg in any Internet browser to see
12 | // the result.
13 | //[/compile]
14 | //[ignore]
15 | // Copyright (C) 2012 www.scratchapixel.com
16 | //
17 | // This program is free software: you can redistribute it and/or modify
18 | // it under the terms of the GNU General Public License as published by
19 | // the Free Software Foundation, either version 3 of the License, or
20 | // (at your option) any later version.
21 | //
22 | // This program is distributed in the hope that it will be useful,
23 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | // GNU General Public License for more details.
26 | //
27 | // You should have received a copy of the GNU General Public License
28 | // along with this program. If not, see .
29 | //[/ignore]
30 |
31 | #include
32 | #include
33 | #include
34 |
35 | #include "geometry.h"
36 |
37 | #include
38 | #define M_PI 3.1415926
39 | //[comment]
40 | // List of vertices making up the object
41 | //[/comment]
42 | const Vec3f verts[146] = {
43 | { -2.5703, 0.78053, -2.4e-05}, { -0.89264, 0.022582, 0.018577},
44 | { 1.6878, -0.017131, 0.022032}, { 3.4659, 0.025667, 0.018577},
45 | { -2.5703, 0.78969, -0.001202}, { -0.89264, 0.25121, 0.93573},
46 | { 1.6878, 0.25121, 1.1097}, { 3.5031, 0.25293, 0.93573},
47 | { -2.5703, 1.0558, -0.001347}, { -0.89264, 1.0558, 1.0487},
48 | { 1.6878, 1.0558, 1.2437}, { 3.6342, 1.0527, 1.0487},
49 | { -2.5703, 1.0558, 0}, { -0.89264, 1.0558, 0},
50 | { 1.6878, 1.0558, 0}, { 3.6342, 1.0527, 0},
51 | { -2.5703, 1.0558, 0.001347}, { -0.89264, 1.0558, -1.0487},
52 | { 1.6878, 1.0558, -1.2437}, { 3.6342, 1.0527, -1.0487},
53 | { -2.5703, 0.78969, 0.001202}, { -0.89264, 0.25121, -0.93573},
54 | { 1.6878, 0.25121, -1.1097}, { 3.5031, 0.25293, -0.93573},
55 | { 3.5031, 0.25293, 0}, { -2.5703, 0.78969, 0},
56 | { 1.1091, 1.2179, 0}, { 1.145, 6.617, 0},
57 | { 4.0878, 1.2383, 0}, { -2.5693, 1.1771, -0.081683},
58 | { 0.98353, 6.4948, -0.081683}, { -0.72112, 1.1364, -0.081683},
59 | { 0.9297, 6.454, 0}, { -0.7929, 1.279, 0},
60 | { 0.91176, 1.2994, 0}
61 | };
62 |
63 | const uint32_t numTris = 51;
64 |
65 | //[comment]
66 | // Triangle index array. A triangle has 3 vertices. Each successive group of 3
67 | // integers in this array represent the positions of the vertices in the vertex
68 | // array making up one triangle of that object. For example, the first 3 integers
69 | // from this array, 8/7/9 represent the positions of the vertices making up the
70 | // the first triangle. You can access these vertices with the following code:
71 | //
72 | // verts[8]; /* first vertex */
73 | //
74 | // verts[7]; /* second vertex */
75 | //
76 | // verts[9]; /* third vertex */
77 | //
78 | // 6/5/5 are the positions of the vertices in the vertex array making up the second
79 | // triangle, and so on.
80 | // To find the indices of the n-th triangle, use the following code:
81 | //
82 | // tris[n * 3]; /* index of the first vertex in the verts array */
83 | //
84 | // tris[n * 3 + 1]; /* index of the second vertexin the verts array */
85 | //
86 | // tris[n * 3 + 2]; /* index of the third vertex in the verts array */
87 | //[/comment]
88 | const uint32_t tris[numTris * 3] = {
89 | 4, 0, 5, 0, 1, 5, 1, 2, 5, 5, 2, 6, 3, 7, 2,
90 | 2, 7, 6, 5, 9, 4, 4, 9, 8, 5, 6, 9, 9, 6, 10,
91 | 7, 11, 6, 6, 11, 10, 9, 13, 8, 8, 13, 12, 10, 14, 9,
92 | 9, 14, 13, 10, 11, 14, 14, 11, 15, 17, 16, 13, 12, 13, 16,
93 | 13, 14, 17, 17, 14, 18, 15, 19, 14, 14, 19, 18, 16, 17, 20,
94 | 20, 17, 21, 18, 22, 17, 17, 22, 21, 18, 19, 22, 22, 19, 23,
95 | 20, 21, 0, 21, 1, 0, 22, 2, 21, 21, 2, 1, 22, 23, 2,
96 | 2, 23, 3, 3, 23, 24, 3, 24, 7, 24, 23, 15, 15, 23, 19,
97 | 24, 15, 7, 7, 15, 11, 0, 25, 20, 0, 4, 25, 20, 25, 16,
98 | 16, 25, 12, 25, 4, 12, 12, 4, 8, 26, 27, 28, 29, 30, 31,
99 | 32, 34, 33
100 | };
101 |
102 | //[comment]
103 | // Compute the 2D pixel coordinates of a point defined in world space. This function
104 | // requires the point original world coordinates of course, the world-to-camera
105 | // matrix (which you can get from computing the inverse of the camera-to-world matrix,
106 | // the matrix transforming the camera), the canvas dimension and the image width and
107 | // height in pixels.
108 | //
109 | // The canvas coordinates (bottom, left, top, right) are also passed to the function
110 | // so we can test the point raster coordinates against the canvas coordinates. If the
111 | // point in raster space lies within the canvas boundaries, the function returns true,
112 | // false otherwise.
113 | //[/comment]
114 | bool computePixelCoordinates(
115 | const Vec3f &pWorld,
116 | const Matrix44f &worldToCamera,
117 | const float &b,
118 | const float &l,
119 | const float &t,
120 | const float &r,
121 | const float &near,
122 | const uint32_t &imageWidth,
123 | const uint32_t &imageHeight,
124 | Vec2i &pRaster)
125 | {
126 | Vec3f pCamera;
127 | worldToCamera.multVecMatrix(pWorld, pCamera);
128 | Vec2f pScreen;
129 | pScreen.x = pCamera.x / -pCamera.z * near;
130 | pScreen.y = pCamera.y / -pCamera.z * near;
131 |
132 | Vec2f pNDC;
133 | pNDC.x = (pScreen.x + r) / (2 * r);
134 | pNDC.y = (pScreen.y + t) / (2 * t);
135 | pRaster.x = (int)(pNDC.x * imageWidth);
136 | pRaster.y = (int)((1 - pNDC.y) * imageHeight);
137 |
138 | bool visible = true;
139 | if (pScreen.x < l || pScreen.x > r || pScreen.y < b || pScreen.y > t)
140 | visible = false;
141 |
142 | return visible;
143 | }
144 | //[comment]
145 | // Settings of the physical camera model:
146 | //
147 | // - focal length in millimetre
148 | //
149 | // - film dimensions (width and height in inches)
150 | //
151 | // Other settings:
152 | //
153 | // - clipping planes (the canvas is positionned at the near clipping plane)
154 | //
155 | // - image dimensions in pixels
156 | //
157 | // - fit film mode (overscan or fit)
158 | //[/comment]
159 | float focalLength = 35; // in mm
160 | // 35mm Full Aperture in inches
161 | float filmApertureWidth = 0.825;
162 | float filmApertureHeight = 0.446;
163 | static const float inchToMm = 25.4;
164 | float nearClippingPlane = 0.1;
165 | float farClipingPlane = 1000;
166 | // image resolution in pixels
167 | uint32_t imageWidth = 512;
168 | uint32_t imageHeight = 512;
169 |
170 | enum FitResolutionGate { kFill = 0, kOverscan };
171 | FitResolutionGate fitFilm = kOverscan;
172 |
173 | int main(int argc, char **argv)
174 | {
175 | //[comment]
176 | // First compute the canvas coordinates. The canvas is positionned by choice,
177 | // at the near clipping plane. By changing the near clipping plane value, you
178 | // will change the position of the canvas, but this won't change the output of the
179 | // program.
180 | //[/comment]
181 | float filmAspectRatio = filmApertureWidth / filmApertureHeight;
182 | float deviceAspectRatio = imageWidth / (float)imageHeight;
183 |
184 | float top = ((filmApertureHeight * inchToMm / 2) / focalLength) * nearClippingPlane;
185 | float right = ((filmApertureWidth * inchToMm / 2) / focalLength) * nearClippingPlane;
186 |
187 | float xscale = 1;
188 | float yscale = 1;
189 |
190 | switch (fitFilm) {
191 | default:
192 | case kFill:
193 | if (filmAspectRatio > deviceAspectRatio) {
194 | xscale = deviceAspectRatio / filmAspectRatio;
195 | }
196 | else {
197 | yscale = filmAspectRatio / deviceAspectRatio;
198 | }
199 | break;
200 | case kOverscan:
201 | if (filmAspectRatio > deviceAspectRatio) {
202 | yscale = filmAspectRatio / deviceAspectRatio;
203 | }
204 | else {
205 | xscale = deviceAspectRatio / filmAspectRatio;
206 | }
207 | break;
208 | }
209 |
210 | right *= xscale;
211 | top *= yscale;
212 |
213 | float bottom = -top;
214 | float left = -right;
215 |
216 | printf("Screen window coordinates: %f %f %f %f\n", bottom, left, top, right);
217 | printf("Film Aspect Ratio: %f\nDevice Aspect Ratio: %f\n", filmAspectRatio, deviceAspectRatio);
218 | printf("Angle of view: %f (deg)\n", 2 * atan((filmApertureWidth * inchToMm / 2) / focalLength) * 180 / M_PI);
219 |
220 | //[comment]
221 | // Project the triangles vertices onto the plane. If at least one of the vertices lies outside
222 | // the canvas boundaries (if the function computePixelCoordinates returns false), the triangle
223 | // is drawn in red and black otherwise. The result is store in a SVG file.
224 | //[/comment]
225 | std::ofstream ofs;
226 | ofs.open("./pinhole.svg");
227 | ofs << "\n";
248 | ofs.close();
249 |
250 | return 0;
251 | }
252 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Official Scratchapixel website's code repo.
2 |
3 | See [www.scratchapixel.com](www.scratchapixel.com)
4 |
--------------------------------------------------------------------------------
/build-raytracer-core/area-light-impl.ilk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/build-raytracer-core/area-light-impl.ilk
--------------------------------------------------------------------------------
/build-raytracer-core/intro-lighting-kraemer-remapping-quad-tri.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/build-raytracer-core/intro-lighting-kraemer-remapping-quad-tri.gif
--------------------------------------------------------------------------------
/build-raytracer-core/intro-lighting-quadtree.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/build-raytracer-core/intro-lighting-quadtree.gif
--------------------------------------------------------------------------------
/build-raytracer-core/math.h:
--------------------------------------------------------------------------------
1 | #ifndef _MATH_H_
2 | #define _MATH_H_
3 |
4 | template
5 | class Vec2 {
6 | public:
7 | using type = T;
8 | Vec2() noexcept : x(0), y(0) {}
9 | Vec2(T xx) : x(xx), y(xx) {}
10 | Vec2(T xx, T yy) : x(xx), y(yy) {}
11 | T x, y;
12 | };
13 |
14 | template
15 | class Vec3 {
16 | public:
17 | using type = T;
18 | Vec3() noexcept : x(0), y(0), z(0) {}
19 | Vec3(T xx) : x(xx), y(xx), z(xx) {}
20 | Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
21 | constexpr unsigned int Dimensions() const noexcept {
22 | return 3;
23 | }
24 | constexpr T& operator[](int i) noexcept {
25 | return (&x)[i];
26 | }
27 | constexpr const T& operator[](int i) const noexcept {
28 | return (&x)[i];
29 | }
30 | // Return a reference to the instance it modifies
31 | const Vec3& Normalize() noexcept {
32 | T len = std::sqrt(x * x + y * y + z * z);
33 | if (len != T(0)) [[likely]] {
34 | x /= len;
35 | y /= len;
36 | z /= len;
37 | }
38 | return *this;
39 | }
40 | constexpr Vec3 Cross(const Vec3& v) const noexcept {
41 | return {y * v.z - z * v.y,
42 | z * v.x - x * v.z,
43 | x * v.y - y * v.x};
44 | }
45 | constexpr T Dot(const Vec3& v) const noexcept {
46 | return x * v.x + y * v.y + z * v.z;
47 | }
48 | constexpr Vec3 operator-(const Vec3& v) const noexcept {
49 | return {x - v.x, y - v.y, z - v.z};
50 | }
51 | constexpr Vec3 operator+(const Vec3& v) const noexcept {
52 | return {x + v.x, y + v.y, z + v.z};
53 | }
54 | Vec3& operator+=(const Vec3& v) noexcept {
55 | x += v.x;
56 | y += v.y;
57 | z += v.z;
58 | return *this;
59 | }
60 | template
61 | Vec3& operator/=(const S a) noexcept {
62 | x /= a;
63 | y /= a;
64 | z /= a;
65 | return *this;
66 | }
67 | constexpr Vec3 operator*(T a) const noexcept {
68 | return {x * a, y * a, z * a};
69 | }
70 | constexpr Vec3 operator/(T a) const noexcept {
71 | return {x / a, y / a, z / a};
72 | }
73 | friend constexpr Vec3 operator*(T a, const Vec3& v) noexcept{
74 | return {a * v.x, a * v.y, a * v.z};
75 | }
76 | // @todo not safe
77 | friend constexpr Vec3 operator/(T a, const Vec3& v) noexcept{
78 | return {a / v.x, a / v.y, a / v.z};
79 | }
80 | constexpr T Max() const noexcept {
81 | return std::max(std::max(x, y), z);
82 | }
83 | constexpr T Length() const noexcept {
84 | return std::sqrt(x * x + y * y + z * z);
85 | }
86 | constexpr bool operator==(const Vec3& v) const noexcept {
87 | return x == v.x && y == v.y && z == v.z;
88 | }
89 | friend std::ostream& operator<<(std::ostream& os, const Vec3& v) {
90 | return os << v.x << " " << v.y << " " << v.z;
91 | }
92 | T x, y, z;
93 | };
94 |
95 | template
96 | class Box {
97 | public:
98 | V min_, max_;
99 | void MakeEmpty() noexcept {
100 | min_ = std::numeric_limits::max();
101 | max_ = std::numeric_limits::lowest();
102 | }
103 | void ExtendBy(const V& point) noexcept {
104 | for (unsigned int i = 0; i < min_.Dimensions(); ++i) {
105 | if (point[i] < min_[i]) min_[i] = point[i];
106 | if (point[i] > max_[i]) max_[i] = point[i];
107 | }
108 | }
109 | void ExtendBy(const Box& box) noexcept {
110 | for (unsigned int i = 0; i < min_.Dimensions(); ++i) {
111 | if (box.min_[i] < min_[i]) min_[i] = box.min_[i];
112 | if (box.max_[i] > max_[i]) max_[i] = box.max_[i];
113 | }
114 | }
115 | };
116 |
117 | using Box3f = Box>;
118 |
119 | template
120 | class Matrix44 {
121 | public:
122 | constexpr Matrix44() noexcept {
123 | x[0][0] = 1;
124 | x[0][1] = 0;
125 | x[0][2] = 0;
126 | x[0][3] = 0;
127 | x[1][0] = 0;
128 | x[1][1] = 1;
129 | x[1][2] = 0;
130 | x[1][3] = 0;
131 | x[2][0] = 0;
132 | x[2][1] = 0;
133 | x[2][2] = 1;
134 | x[2][3] = 0;
135 | x[3][0] = 0;
136 | x[3][1] = 0;
137 | x[3][2] = 0;
138 | x[3][3] = 1;
139 | }
140 | constexpr Matrix44(
141 | T a, T b, T c, T d,
142 | T e, T f, T g, T h,
143 | T i, T j, T k, T l,
144 | T m, T n, T o, T p) noexcept {
145 | x[0][0] = a; x[0][1] = b; x[0][2] = c; x[0][3] = d;
146 | x[1][0] = e; x[1][1] = f; x[1][2] = g; x[1][3] = h;
147 | x[2][0] = i; x[2][1] = j; x[2][2] = k; x[2][3] = l;
148 | x[3][0] = m; x[3][1] = n; x[3][2] = o; x[3][3] = p;
149 | }
150 | constexpr Matrix44(const T* m) {
151 | std::memcpy(&x[0], m, sizeof(T) * 16);
152 | }
153 | constexpr bool operator==(const Matrix44& rhs) const noexcept {
154 | return x[0][0] == rhs.x[0][0] && x[0][1] == rhs.x[0][1] &&
155 | x[0][2] == rhs.x[0][2] && x[0][3] == rhs.x[0][3] &&
156 | x[1][0] == rhs.x[1][0] && x[1][1] == rhs.x[1][1] &&
157 | x[1][2] == rhs.x[1][2] && x[1][3] == rhs.x[1][3] &&
158 | x[2][0] == rhs.x[2][0] && x[2][1] == rhs.x[2][1] &&
159 | x[2][2] == rhs.x[2][2] && x[2][3] == rhs.x[2][3] &&
160 | x[3][0] == rhs.x[3][0] && x[3][1] == rhs.x[3][1] &&
161 | x[3][2] == rhs.x[3][2] && x[3][3] == rhs.x[3][3];
162 | }
163 | template
164 | void MultVecMatrix(const Vec3& src, Vec3& dst) const noexcept {
165 | S a, b, c, w;
166 |
167 | a = src.x * x[0][0] + src.y * x[1][0] + src.z * x[2][0] + x[3][0];
168 | b = src.x * x[0][1] + src.y * x[1][1] + src.z * x[2][1] + x[3][1];
169 | c = src.x * x[0][2] + src.y * x[1][2] + src.z * x[2][2] + x[3][2];
170 | w = src.x * x[0][3] + src.y * x[1][3] + src.z * x[2][3] + x[3][3];
171 |
172 | dst.x = a / w;
173 | dst.y = b / w;
174 | dst.z = c / w;
175 | }
176 | template
177 | void MultDirMatrix(const Vec3& src, Vec3& dst) const noexcept {
178 | S a, b, c;
179 |
180 | a = src.x * x[0][0] + src.y * x[1][0] + src.z * x[2][0];
181 | b = src.x * x[0][1] + src.y * x[1][1] + src.z * x[2][1];
182 | c = src.x * x[0][2] + src.y * x[1][2] + src.z * x[2][2];
183 |
184 | dst.x = a;
185 | dst.y = b;
186 | dst.z = c;
187 | }
188 | T* operator[] (int i) noexcept{
189 | return x[i];
190 | }
191 | const T* operator[] (int i) const noexcept {
192 | return x[i];
193 | }
194 | public:
195 | T x[4][4];
196 | };
197 |
198 | #endif
--------------------------------------------------------------------------------
/build-raytracer-core/mc-integration.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | template
7 | class Vec3 {
8 | public:
9 | Vec3() : x(0), y(0), z(0) {}
10 | Vec3(T xx) : x(xx), y(xx), z(xx) {}
11 | Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
12 | T Dot(const Vec3& v) const noexcept {
13 | return x * v.x + y * v.y + z * v.z;
14 | }
15 | Vec3 operator*(const Vec3& v) const noexcept {
16 | return {x * v.x, y * v.y, z * v.z};
17 | }
18 | Vec3 operator*(T r) const noexcept {
19 | return {x * r, y * r, z * r};
20 | }
21 | template
22 | Vec3& operator/=(S r) noexcept {
23 | x /= r;
24 | y /= r;
25 | z /= r;
26 | return *this;
27 | }
28 | template
29 | Vec3 operator/(S r) const noexcept {
30 | return {x / r, y / r, z / r};
31 | }
32 | Vec3 operator-(const Vec3& v) const noexcept {
33 | return {x - v.x, y - v.y, z - v.z};
34 | }
35 | T Length() const noexcept {
36 | return std::sqrt(x * x + y * y + z * z);
37 | }
38 | Vec3& Normalize() noexcept {
39 | T len = Length();
40 | if (len != 0) [[likely]]
41 | x /= len, y /= len, z /= len;
42 | return *this;
43 | }
44 | Vec3& operator+=(const Vec3& v) noexcept {
45 | x += v.x, y += v.y, z += v.z;
46 | return *this;
47 | }
48 | friend std::ostream& operator<<(std::ostream& os, const Vec3& v) {
49 | return os << v.x << " " << v.y << " " << v.z;
50 | }
51 | T x, y, z;
52 | };
53 |
54 | template
55 | class Vec2 {
56 | public:
57 | Vec2() : x(0), y(0) {}
58 | T x, y;
59 | };
60 |
61 | Vec3 Le(Vec3 x) {
62 | return (x.x < -0.4) ? 1 : 0.001;
63 | }
64 |
65 | int num_samples = 16;
66 |
67 | void example1() {
68 | Vec3 sum = 0;
69 | std::random_device rd;
70 | std::mt19937 gen(rd());
71 | std::uniform_real_distribution dist(-0.5,0.5);
72 | Vec3 x(0,0,5);
73 | Vec3 Nf(0,0,-1);
74 | Vec3 Nl(0,0,1);
75 | for (int n = 0; n < num_samples; ++n) {
76 | Vec3 sample = {dist(gen), dist(gen), 0};
77 | Vec3 d = sample - x;
78 | double r = d.Length();
79 | d.Normalize();
80 | //std::cerr << "n: " << n << ", sample: " << sample << ", Le(sample): " << Le(sample) << ", Nf.Dot(d): " << Nf.Dot(d) << std::endl;
81 | std::cerr << "emit -o \"part2\" -pos " << sample << ";\n";
82 | sum += Le(sample) * Nf.Dot(d);
83 | }
84 | sum /= num_samples;
85 | std::cerr << "Example 1: " << sum << std::endl;
86 | }
87 |
88 | void example2() {
89 | Vec3 sum = 0;
90 | std::random_device rd;
91 | std::mt19937 gen(rd());
92 | std::uniform_real_distribution dist(0, 1);
93 | Vec3 x(0,0,5);
94 | Vec3 Nf(0,0,-1);
95 | Vec3 Nl(0,0,1);
96 | for (int n = 0; n < num_samples; ++n) {
97 | double rand = dist(gen);
98 | Vec3 sample;
99 | double pdf;
100 | if (dist(gen) <= 0.8) {
101 | std::uniform_real_distribution dist_sample(-0.5, -0.4);
102 | sample = {dist_sample(gen), (2 * dist(gen) - 1) * 0.5, 0};
103 | pdf = 0.8 / 0.1; // probability / segment length
104 | }
105 | else {
106 | std::uniform_real_distribution dist_sample(-0.4, 0.5);
107 | sample = {dist_sample(gen), (2 * dist(gen) - 1) * 0.5, 0};
108 | pdf = 0.2 / 0.9; // probability / segment length
109 | }
110 | Vec3 d = sample - x;
111 | double r = d.Length();
112 | d.Normalize();
113 | //std::cerr << "n: " << n << ", sample: " << sample << ", Le(sample): " << Le(sample) << ", Nf.Dot(d): " << Nf.Dot(d) << std::endl;
114 | //std::cerr << "emit -o \"part2\" -pos " << sample << ";\n";
115 | sum += Le(sample) * Nf.Dot(d) / pdf;
116 | }
117 | sum /= num_samples;
118 | std::cerr << "Example 2: " << sum << std::endl;
119 | }
120 |
121 | int main() {
122 | example1();
123 | example2();
124 | return 0;
125 | }
126 |
--------------------------------------------------------------------------------
/build-raytracer-core/pointlight.cc:
--------------------------------------------------------------------------------
1 | // (c) www.scratchapixel.com - 2024.
2 | // Distributed under the terms of the CC BY-NC-ND 4.0 License.
3 | // https://creativecommons.org/licenses/by-nc-nd/4.0/
4 | // clang++ -Wall -Wextra -std=c++23 -o light.exe light.cc -O3
5 |
6 | #define _USE_MATH_DEFINES
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | template
17 | class Vec3 {
18 | public:
19 | Vec3() : x(0), y(0), z(0) {}
20 | Vec3(T xx) : x(xx), y(xx), z(xx) {}
21 | Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
22 | Vec3 operator+(const Vec3& v) const noexcept {
23 | return {x + v.x, y + v.y, z + v.z};
24 | }
25 | Vec3 operator-(const Vec3& v) const noexcept {
26 | return {x - v.x, y - v.y, z - v.z};
27 | }
28 | Vec3 operator*(const T& real) const noexcept {
29 | return {x * real, y * real, z * real};
30 | }
31 | friend Vec3 operator*(const T& real, const Vec3& v) noexcept {
32 | return {real * v.x, real * v.y, real * v.z};
33 | }
34 | Vec3 operator/(const T& real) const noexcept {
35 | return {x / real, y / real, z / real};
36 | }
37 | Vec3 operator-() const noexcept {
38 | return {-x, -y, -z};
39 | }
40 | T Length() const {
41 | return std::sqrtf(x * x + y * y + z * z);
42 | }
43 | Vec3& Normalize() noexcept {
44 | T len = Length();
45 | if (len != 0) [[likely]] {
46 | x /= len;
47 | y /= len;
48 | z /= len;
49 | }
50 | return *this;
51 | }
52 | T Dot(const Vec3& v) const noexcept {
53 | return x * v.x + y * v.y + z * v.z;
54 | }
55 | Vec3 Cross(const Vec3& v) const noexcept {
56 | return {y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x};
57 | }
58 | friend std::ostream& operator<<(std::ostream& os, const Vec3& v) {
59 | return os << v.x << " " << v.y << " " << v.z;
60 | }
61 | T x, y, z;
62 | };
63 |
64 | struct Hit {
65 | float u;
66 | float v;
67 | int id0{-1};
68 | int id1{-1};
69 | float t{std::numeric_limits::max()};
70 | operator bool() const { return id0 != -1; }
71 | };
72 |
73 | struct DifferentialGeometry : Hit {
74 | Vec3 P;
75 | Vec3 Ng;
76 | Vec3 Ns;
77 | };
78 |
79 | struct Ray {
80 | Vec3 orig;
81 | Vec3 dir;
82 | float near{0.1};
83 | float far{std::numeric_limits::max()};
84 | };
85 |
86 | class TriangleMesh {
87 | public:
88 | struct Triangle {
89 | uint32_t v0, v1, v2;
90 | };
91 |
92 | void PostIntersect(const Ray& ray, DifferentialGeometry& dg) {
93 | const Triangle& tri = triangles_[dg.id1];
94 | Vec3 p0 = position_[tri.v0];
95 | Vec3 p1 = position_[tri.v1];
96 | Vec3 p2 = position_[tri.v2];
97 |
98 | float u = dg.u, v = dg.v, w = 1.f - u - v, t = dg.t;
99 |
100 | const Vec3 dPdu = p1 - p0, dPdv = p2 - p0;
101 | dg.P = ray.orig + t * ray.dir;
102 | dg.Ng = dPdv.Cross(dPdu).Normalize();
103 |
104 | if (normals_.size()) {
105 | const Vec3 n0 = normals_[tri.v0], n1 = normals_[tri.v1], n2 = normals_[tri.v2];
106 | Vec3 Ns = w * n0 + u * n1 + v * n2;
107 | float len2 = Ns.Dot(Ns);
108 | Ns = len2 > 0 ? Ns / std::sqrt(len2) : dg.Ng;
109 | if (Ns.Dot(dg.Ng) < 0) Ns = -Ns;
110 | dg.Ns = Ns;
111 | }
112 | else
113 | dg.Ns = dg.Ng;
114 | }
115 |
116 | std::vector> position_;
117 | std::vector> normals_;
118 | std::vector triangles_;
119 | };
120 |
121 | class Sphere : public TriangleMesh {
122 | public:
123 | Sphere() : center_(Vec3(0,-2,-22)), radius_(2) {
124 | Triangulate();
125 | }
126 | private:
127 | Vec3 SphericalToCartesian(float theta, float phi) {
128 | return Vec3(
129 | std::sin(theta) * std::cos(phi),
130 | std::cos(theta),
131 | std::sin(theta) * std::sin(phi));
132 | }
133 | void Triangulate() {
134 | for (uint32_t theta = 0; theta <= num_theta_; ++theta) {
135 | for (uint32_t phi = 0; phi < num_phi_; ++phi) {
136 | Vec3 p = SphericalToCartesian(theta * M_PI / num_theta_, phi * 2 * M_PI / num_phi_);
137 | normals_.push_back(p);
138 | position_.push_back(center_ + p * radius_);
139 | }
140 | if (theta == 0) continue;
141 | for (uint32_t phi = 1; phi <= num_phi_; ++phi) {
142 | uint32_t p00 = (theta - 1) * num_phi_ + phi - 1;
143 | uint32_t p01 = (theta - 1) * num_phi_ + phi % num_phi_;
144 | uint32_t p10 = theta * num_phi_ + phi - 1;
145 | uint32_t p11 = theta * num_phi_ + phi % num_phi_;
146 | if (theta > 1) triangles_.push_back({p10, p01, p00});
147 | if (theta < num_theta_) triangles_.push_back({p11, p01, p10});
148 | }
149 | }
150 | }
151 | public:
152 | uint32_t num_theta_{16};
153 | uint32_t num_phi_{16};
154 | Vec3 center_;
155 | float radius_;
156 | };
157 |
158 | class Light {
159 | public:
160 | Vec3 Sample(const DifferentialGeometry& dg, Vec3& wi, float& pdf, float &t_max) const {
161 | Vec3 d = pos_ - dg.P;
162 | float distance = d.Length();
163 | wi = d / distance;
164 | pdf = distance * distance;
165 | t_max = distance;
166 | return color_;
167 | }
168 | Vec3 pos_{0,8,-22};
169 | Vec3 color_{1,1,1};
170 | };
171 |
172 | /**
173 | * Extracts the sign bit of a float, returning -0.0 for negative and 0.0 for
174 | * positive or zero. Uses SIMD operations for efficiency. _mm_set_ss sets
175 | * float x in a 128-bit vector, while _mm_set1_epi32(0x80000000) creates a
176 | * mask to isolate the sign bit. _mm_and_ps applies the mask, and _mm_cvtss_f32
177 | * converts the result back to a float.
178 | */
179 | __forceinline float signmsk(const float x) {
180 | return _mm_cvtss_f32(_mm_and_ps(_mm_set_ss(x),_mm_castsi128_ps(_mm_set1_epi32(0x80000000))));
181 | }
182 |
183 |
184 | /**
185 | * xorf performs a bitwise XOR on float x and y, returning a float.
186 | * - If x and y are both positive or both negative: No sign change in x.
187 | * - If x and y have different signs: The sign of x is "inverted".
188 | * This operation can flip the sign of x or leave it unchanged,
189 | * depending on y's sign.
190 | */
191 | __forceinline float xorf(const float x, const float y) {
192 | return _mm_cvtss_f32(_mm_xor_ps(_mm_set_ss(x),_mm_set_ss(y)));
193 | }
194 |
195 | void Intersect(const Ray& ray,
196 | Hit& hit,
197 | int obj_id,
198 | int tri_id,
199 | const TriangleMesh::Triangle& tri,
200 | const Vec3* verts) {
201 | const Vec3 p0 = verts[tri.v0];
202 | const Vec3 p1 = verts[tri.v1];
203 | const Vec3 p2 = verts[tri.v2];
204 | const Vec3 e1 = p0 - p1;
205 | const Vec3 e2 = p2 - p0;
206 | const Vec3 Ng = e1.Cross(e2);
207 |
208 | const Vec3 C = p0 - ray.orig;
209 | const Vec3 R = ray.dir.Cross(C);
210 | const float det = Ng.Dot(ray.dir);
211 | const float abs_det = std::abs(det);
212 | const float sng_det = signmsk(det);
213 | if (det == 0) [[unlikely]] return;
214 |
215 | const float U = xorf(R.Dot(e2), sng_det);
216 | if (U < 0) [[likely]] return;
217 |
218 | const float V = xorf(R.Dot(e1), sng_det);
219 | if (V < 0) [[likely]] return;
220 |
221 | const float W = abs_det - U - V;
222 | if (W < 0) [[likely]] return;
223 |
224 | const float T = xorf(Ng.Dot(C), sng_det);
225 | if (T < abs_det * ray.near || abs_det * hit.t < T) [[unlikely]] return;
226 |
227 | hit.u = U / abs_det;
228 | hit.v = V / abs_det;
229 | hit.t = T / abs_det;
230 | hit.id0 = obj_id;
231 | hit.id1 = tri_id;
232 | }
233 |
234 | bool Occluded(const Ray& ray,
235 | const TriangleMesh::Triangle& tri,
236 | const Vec3* verts) {
237 | const Vec3 p0 = verts[tri.v0];
238 | const Vec3 p1 = verts[tri.v1];
239 | const Vec3 p2 = verts[tri.v2];
240 | const Vec3 e1 = p0 - p1;
241 | const Vec3 e2 = p2 - p0;
242 | const Vec3 Ng = e1.Cross(e2);
243 |
244 | const Vec3 C = p0 - ray.orig;
245 | const Vec3 R = ray.dir.Cross(C);
246 | const float det = Ng.Dot(ray.dir);
247 | const float abs_det = abs(det);
248 | const float sgn_det = signmsk(det);
249 | if (det == 0.f) [[unlikely]] return false;
250 |
251 | const float U = xorf(R.Dot(e2),sgn_det);
252 | if (U < 0.f) [[likely]] return false;
253 |
254 | const float V = xorf(R.Dot(e1), sgn_det);
255 | if (V < 0.f) [[likely]] return false;
256 |
257 | const float W = abs_det - U - V;
258 | if (W < 0.f) [[likely]] return false;
259 |
260 | const float T = xorf(Ng.Dot(C), sgn_det);
261 | if (T < abs_det * ray.near || abs_det * ray.far < T) [[unlikely]] return false;
262 |
263 | return true;
264 | }
265 |
266 | bool Occluded(const Ray& ray, const std::vector>& prims) {
267 | for (int i = 0; i < (int)prims.size(); ++i) {
268 | const std::vector>& pos = prims[i]->position_;
269 | const std::vector& tris = prims[i]->triangles_;
270 | for (size_t j = 0; j < tris.size(); ++j) {
271 | if (Occluded(ray, tris[j], pos.data()))
272 | return true;
273 | }
274 | }
275 | return false;
276 | }
277 |
278 | constexpr uint32_t width = 960;
279 | constexpr uint32_t height = 540;
280 | constexpr float angle_of_view = 60.f;
281 |
282 | int main() {
283 | std::vector> prims;
284 | prims.push_back(std::make_unique());
285 | Light light;
286 |
287 | std::vector> verts = {
288 | {-5,-4,-17}, {5,-4,-17}, {5,-4,-27}, {-5,-4,-27},
289 | {5,-4,-27}, {5,6,-27}, {-5,6,-27}, {-5,-4,-27}};
290 | std::vector> nors = {{0,1,0},{0,0,1}};
291 |
292 | for (uint32_t i = 0; i < 2; ++i) {
293 | TriangleMesh* mesh = new TriangleMesh;
294 | for (uint32_t j = 0; j < 4; ++j) {
295 | mesh->position_.push_back(verts[i * 4 + j]);
296 | mesh->normals_.push_back(nors[i]);
297 | }
298 | mesh->triangles_.push_back({2,1,0});
299 | mesh->triangles_.push_back({3,2,0});
300 |
301 | prims.push_back(std::unique_ptr(mesh));
302 | }
303 |
304 | float scale = std::tan(angle_of_view * 0.5 * M_PI / 180.f);
305 | float aspect_ratio = width / static_cast(height);
306 | std::unique_ptr buf = std::make_unique(width * height * 3);
307 | uint8_t* pbuf = buf.get();
308 | std::memset(pbuf, 0x0, width * height * 3);
309 |
310 | for (uint32_t y = 0; y < height; ++y) {
311 | for (uint32_t x = 0; x < width; ++x, pbuf += 3) {
312 | float px = (2.f * (x + 0.5) / static_cast(width) - 1.f) * scale;
313 | float py = (1.f - 2.0f * (y + 0.5) / static_cast(height)) * scale / aspect_ratio;
314 | Vec3 dir(px, py, -1);
315 | dir.Normalize();
316 | Ray ray = {Vec3(0), dir};
317 | DifferentialGeometry dg;
318 | for (int i = 0; i < (int)prims.size(); ++i) {
319 | const std::vector>& pos = prims[i]->position_;
320 | const std::vector& tris = prims[i]->triangles_;
321 | for (size_t j = 0; j < tris.size(); ++j) {
322 | Intersect(ray, dg, i, j, tris[j], pos.data());
323 | }
324 | }
325 | if (dg) {
326 | prims[dg.id0]->PostIntersect(ray, dg);
327 | Vec3 wi;
328 | float t_max, pdf;
329 | Vec3 light_L = light.Sample(dg, wi, pdf, t_max);
330 | bool in_shadow = Occluded({dg.P, wi, 0.01f, t_max - 0.01f}, prims);
331 | if (in_shadow)
332 | continue;
333 | Vec3 L = std::pow(2.f, 7) * light_L * std::max(0.f, dg.Ns.Dot(wi)) / (M_PI * pdf);
334 | pbuf[0] = static_cast(std::min(1.f, L.x) * 255);
335 | pbuf[1] = static_cast(std::min(1.f, L.y) * 255);
336 | pbuf[2] = static_cast(std::min(1.f, L.z) * 255);
337 | }
338 | }
339 | }
340 |
341 | std::ofstream ofs("./test.ppm", std::ios::binary);
342 | ofs << "P6\n" << width << " " << height << "\n255\n";
343 | ofs.write(reinterpret_cast(buf.get()), width * height * 3);
344 | ofs.close();
345 |
346 | return 0;
347 | };
--------------------------------------------------------------------------------
/build-raytracer-core/quad.cc:
--------------------------------------------------------------------------------
1 | #define _USE_MATH_DEFINES
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | constexpr uint32_t width = 512, height = 512;
9 | uint32_t radius = 128;
10 | int32_t center[2] = {width/2, height/2};
11 | uint32_t max_depth = 4;
12 | uint8_t quad_buf[width * height];
13 | uint32_t total_num_illum_pixels = 0;
14 |
15 | bool ContainsWhitePixelsOnly(const uint8_t* buf, uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
16 | for (uint32_t j = y; j < y + h; ++j) {
17 | for (uint32_t i = x; i < x + w; ++i)
18 | if (buf[j * width + i] == 0)
19 | return false;
20 | }
21 | return true;
22 | }
23 |
24 | void DrawQuads(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
25 | for (uint32_t i = x; i < x + w; ++i) {
26 | quad_buf[y * width + i] = 128;
27 | quad_buf[std::min(height - 1, y + h) * width + i] = 128;
28 | }
29 | for (uint32_t j = y + 1; j < y + h; ++j) {
30 | quad_buf[j * width + x] = 128;
31 | quad_buf[j * width + std::min(width - 1, x + w)] = 128;
32 | }
33 | }
34 |
35 | void BuildQuadTree(const uint8_t* buf, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t depth) {
36 | std::cerr << "Depth " << depth << " " << x << " " << y << " " << w << " " << h << std::endl;
37 | if (depth == 0) {
38 | DrawQuads(x, y, w, h);
39 | return;
40 | }
41 | if (ContainsWhitePixelsOnly(buf, x, y, w, h)) {
42 | DrawQuads(x, y, w, h);
43 | total_num_illum_pixels += w * h;
44 | return;
45 | }
46 | uint32_t mid_x = w / 2, mid_y = h / 2;
47 | //DrawQuads(x, y, w, h);
48 | //if (depth == 4) return;
49 | BuildQuadTree(buf, x, y, mid_x, mid_y, depth - 1);
50 | BuildQuadTree(buf, x, y + mid_y, mid_x, h - mid_y, depth - 1);
51 | BuildQuadTree(buf, x + mid_x, y, w - mid_x, mid_y, depth - 1);
52 | BuildQuadTree(buf, x + mid_x, y + mid_y, w - mid_x, h - mid_y, depth - 1);
53 | }
54 |
55 | int main() {
56 | std::unique_ptr buf = std::make_unique(width * height);
57 | std::memset(buf.get(), 0xFF, width * height);
58 | std::memset(quad_buf, 0x0, width * height);
59 | for (int32_t j = 0; j < height; ++j) {
60 | for (int32_t i = 0; i < width; ++i) {
61 | if ((i - center[0]) * (i - center[0]) + (j - center[1]) * (j - center[1]) <= radius * radius)
62 | buf[j * width + i] = 0;
63 | }
64 | }
65 | std::cerr << "All good...\n";
66 | BuildQuadTree(buf.get(), 0, 0, width, height, max_depth);
67 | std::cerr << "Writing quad\n";
68 | std::ofstream ofs("./quad.ppm", std::ios::binary);
69 | ofs << "P5\n" << width << " " << height << "\n255\n";
70 | ofs.write((char*)quad_buf, width * height);
71 | ofs.close();
72 | std::cerr << "Result: " << ((100.f * total_num_illum_pixels) / (width * height)) << std::endl;
73 | std::cerr << 100 * (1 - M_PI * radius * radius / (float)(width * height)) << std::endl;
74 | return 0;
75 | }
76 |
--------------------------------------------------------------------------------
/build-raytracer-core/quad.ilk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/build-raytracer-core/quad.ilk
--------------------------------------------------------------------------------
/build-raytracer-core/random_dir.cc:
--------------------------------------------------------------------------------
1 |
2 | #define _USE_MATH_DEFINES
3 |
4 | #include
5 | #include
6 | #include
7 |
8 |
9 | int main() {
10 | std::random_device rd; // Obtain a random number from hardware
11 | std::mt19937 gen(rd()); // Seed the generator
12 | std::uniform_real_distribution<> distr(0.0, 1.0); // Define the range
13 | for (size_t i = 0; i < 16; ++i) {
14 | float theta = M_PI * 0.5 * distr(gen);
15 | float phi = 2 * M_PI * distr(gen);
16 | std::cerr << "curve -d 1 -p 0 0 0 -p " <<
17 | std::cos(phi) * sin(theta) << " " <<
18 | std::sin(theta) * std::sin(phi) << " " <<
19 | cos(theta) << " -k 0 -k 1;" << std::endl;
20 | }
21 | return 0;
22 | };
23 |
--------------------------------------------------------------------------------
/build-raytracer-core/triangle-sampling.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | template
6 | class Vec2 {
7 | public:
8 | Vec2() : x(T(0)), y(T(0)) {}
9 | Vec2(T xx) : x(xx), y(xx) {}
10 | Vec2(T xx, T yy) : x(xx), y(yy) {}
11 | Vec2 operator+(const Vec2 v) const noexcept {
12 | return {x + v.x, y + v.y};
13 | }
14 | friend Vec2 operator*(T a, const Vec2& v) {
15 | return {a * v.x, a * v.y};
16 | }
17 | friend std::ostream& operator<<(std::ostream& os, const Vec2& v) {
18 | return os << v.x << " " << v.y;
19 | }
20 | T x, y;
21 | };
22 |
23 | using Vec2f = Vec2;
24 |
25 | constexpr uint32_t nsamples = 512;
26 |
27 | Vec2f NaiveMethod(const Vec2f& v1, const Vec2f& v2, float u, float v) {
28 | return u * v1 + (1.f - u) * v * v2;
29 | }
30 |
31 | Vec2f BarycentricCoordinatesMethodBad(const Vec2f& v1, const Vec2f& v2, float u, float v, float w) {
32 | float sum = u + v + w; // we need to normalize the barycentric coordinates
33 | return /* (u / sum) * v0 + */ (v / sum) * v1 + (w / sum) * v2;
34 | }
35 |
36 | Vec2f BarycentricCoordinatesMethodGood(const Vec2f& v1, const Vec2f& v2, float r1, float r2) {
37 | float q = std::sqrt(r1);
38 | // v0 = (0,0) so skip calculation
39 | return /* (1 - q) * v0 + */ q * (1 - r2) * v1 + q * r2 * v2;
40 | }
41 |
42 | Vec2f KraemerMethod(const Vec2f& v1, const Vec2f& v2, float r1, float r2) {
43 | #if 1
44 | if (r1 > r2) std::swap(r1, r2);
45 | float u = r1, v = r2 - r1, w = 1 - r2;
46 | #else
47 | float q = std::abs(r1 - r2);
48 | float u = q, v = 0.5f * (r1 + r2 - q), w = 1 - 0.5 * (q + r1 + r2);
49 | #endif
50 | return /* u * v0 + */ v * v1 + w * v2;
51 | }
52 |
53 | template
54 | void SampleTriangle(SamplingMethod sample) {
55 | std::random_device rd;
56 | std::mt19937 eng(rd());
57 | std::uniform_real_distribution distr(0.f, 1.f);
58 | for (size_t i = 0; i < nsamples; ++i) {
59 | // We assume v0 = (0,0)
60 | Vec2f x = sample({2,0}, {1,2}, distr(eng), distr(eng));
61 | std::cout << "emit -object \"particleShape1\" -pos " << x << " 0;\n";
62 | }
63 | }
64 |
65 | int main() {
66 | SampleTriangle(BarycentricCoordinatesMethodGood);
67 | };
--------------------------------------------------------------------------------
/computing-pixel-coordinates-of-3d-point/xtree.obj:
--------------------------------------------------------------------------------
1 | # http://www.scratchapixel.com/lessons/3d-basic-rendering/computing-pixel-coordinates-of-3d-point
2 |
3 | v 0.000000 39.033714 0.000000
4 | v 0.762116 36.843475 0.000000
5 | v 3.000000 36.604431 0.000000
6 | v 1.000000 35.604431 0.000000
7 | v 2.016184 33.382484 0.000000
8 | v 0.000000 34.541019 0.000000
9 | v -2.016184 33.382484 0.000000
10 | v -1.000000 35.604431 0.000000
11 | v -3.000000 36.604431 0.000000
12 | v -0.762116 36.843475 0.000000
13 | v -0.040181 34.310349 -0.000000
14 | v 3.277792 30.463991 0.000000
15 | v -0.040181 30.463991 -0.000000
16 | v -0.028749 30.463991 0.000000
17 | v 3.277792 30.463991 0.000000
18 | v 1.272227 29.197426 0.000000
19 | v 1.272227 29.197426 0.000000
20 | v -0.028703 29.197426 0.000000
21 | v 1.272227 29.197426 0.000000
22 | v 5.277792 25.397711 0.000000
23 | v -0.028650 25.397711 0.000000
24 | v 1.272227 29.197426 0.000000
25 | v 5.277792 25.397711 0.000000
26 | v 3.332222 24.098793 0.000000
27 | v -0.028683 24.098793 0.000000
28 | v 7.195669 20.299080 0.000000
29 | v -0.028610 20.299080 0.000000
30 | v 5.277791 19.064854 0.000000
31 | v -0.028663 18.984453 0.000000
32 | v 9.277791 15.265141 0.000000
33 | v -0.028571 15.184738 0.000000
34 | v 9.277792 15.265141 0.000000
35 | v 7.377179 13.999161 0.000000
36 | v -0.028625 13.900555 0.000000
37 | v 9.277791 15.265141 0.000000
38 | v 12.277791 8.932283 0.000000
39 | v -0.028771 8.974176 0.000000
40 | v 12.277791 8.932283 0.000000
41 | v 10.277796 7.665712 0.000000
42 | v -0.028592 7.655210 0.000000
43 | v 15.277796 2.599426 0.000000
44 | v -0.028775 2.607727 0.000000
45 | v 15.277796 2.599426 0.000000
46 | v 13.277795 1.332857 0.000000
47 | v -0.028727 1.261691 0.000000
48 | v 18.277796 -3.733428 0.000000
49 | v 18.277796 -3.733428 0.000000
50 | v 2.272227 -1.200286 0.000000
51 | v -0.028727 -1.309806 0.000000
52 | v 4.272227 -5.000000 0.000000
53 | v 4.272227 -5.000000 0.000000
54 | v -0.028727 -5.000000 0.000000
55 | v -3.358153 30.463991 -0.000000
56 | v -3.358153 30.463991 -0.000000
57 | v -1.352588 29.197426 -0.000000
58 | v -1.352588 29.197426 -0.000000
59 | v -1.352588 29.197426 -0.000000
60 | v -5.358153 25.397711 -0.000000
61 | v -1.352588 29.197426 -0.000000
62 | v -5.358153 25.397711 -0.000000
63 | v -3.412583 24.098793 -0.000000
64 | v -7.276031 20.299080 -0.000000
65 | v -5.358152 19.064854 -0.000000
66 | v -9.358152 15.265141 -0.000000
67 | v -9.358153 15.265141 -0.000000
68 | v -7.457540 13.999161 -0.000000
69 | v -9.358152 15.265141 -0.000000
70 | v -12.358152 8.932283 -0.000000
71 | v -12.358152 8.932283 -0.000000
72 | v -10.358157 7.665712 -0.000000
73 | v -15.358157 2.599426 -0.000000
74 | v -15.358157 2.599426 -0.000000
75 | v -13.358156 1.332857 -0.000000
76 | v -18.358156 -3.733428 -0.000000
77 | v -18.358156 -3.733428 -0.000000
78 | v -2.352588 -1.200286 -0.000000
79 | v -4.352589 -5.000000 -0.000000
80 | v -4.352589 -5.000000 -0.000000
81 | v -0.000000 34.310349 0.040181
82 | v 0.000000 30.463991 -3.277792
83 | v -0.000000 30.463991 0.040181
84 | v -0.000000 30.463991 0.028749
85 | v 0.000000 30.463991 -3.277792
86 | v 0.000000 29.197426 -1.272227
87 | v 0.000000 29.197426 -1.272227
88 | v -0.000000 29.197426 0.028703
89 | v 0.000000 29.197426 -1.272227
90 | v 0.000000 25.397711 -5.277792
91 | v -0.000000 25.397711 0.028650
92 | v 0.000000 29.197426 -1.272227
93 | v 0.000000 25.397711 -5.277792
94 | v 0.000000 24.098793 -3.332222
95 | v -0.000000 24.098793 0.028683
96 | v 0.000000 20.299080 -7.195669
97 | v -0.000000 20.299080 0.028610
98 | v 0.000000 19.064854 -5.277791
99 | v -0.000000 18.984453 0.028663
100 | v 0.000000 15.265141 -9.277791
101 | v -0.000000 15.184738 0.028571
102 | v 0.000000 15.265141 -9.277792
103 | v 0.000000 13.999161 -7.377179
104 | v -0.000000 13.900555 0.028625
105 | v 0.000000 15.265141 -9.277791
106 | v 0.000000 8.932283 -12.277791
107 | v -0.000000 8.974176 0.028771
108 | v 0.000000 8.932283 -12.277791
109 | v 0.000000 7.665712 -10.277796
110 | v -0.000000 7.655210 0.028592
111 | v 0.000000 2.599426 -15.277796
112 | v -0.000000 2.607727 0.028775
113 | v 0.000000 2.599426 -15.277796
114 | v 0.000000 1.332857 -13.277795
115 | v -0.000000 1.261691 0.028727
116 | v 0.000000 -3.733428 -18.277796
117 | v 0.000000 -3.733428 -18.277796
118 | v 0.000000 -1.200286 -2.272227
119 | v -0.000000 -1.309806 0.028727
120 | v 0.000000 -5.000000 -4.272227
121 | v 0.000000 -5.000000 -4.272227
122 | v -0.000000 -5.000000 0.028727
123 | v -0.000000 30.463991 3.358153
124 | v -0.000000 30.463991 3.358153
125 | v -0.000000 29.197426 1.352588
126 | v -0.000000 29.197426 1.352588
127 | v -0.000000 29.197426 1.352588
128 | v -0.000000 25.397711 5.358153
129 | v -0.000000 29.197426 1.352588
130 | v -0.000000 25.397711 5.358153
131 | v -0.000000 24.098793 3.412583
132 | v -0.000000 20.299080 7.276031
133 | v -0.000000 19.064854 5.358152
134 | v -0.000000 15.265141 9.358152
135 | v -0.000000 15.265141 9.358153
136 | v -0.000000 13.999161 7.457540
137 | v -0.000000 15.265141 9.358152
138 | v -0.000000 8.932283 12.358152
139 | v -0.000000 8.932283 12.358152
140 | v -0.000000 7.665712 10.358157
141 | v -0.000000 2.599426 15.358157
142 | v -0.000000 2.599426 15.358157
143 | v -0.000000 1.332857 13.358156
144 | v -0.000000 -3.733428 18.358156
145 | v -0.000000 -3.733428 18.358156
146 | v -0.000000 -1.200286 2.352588
147 | v -0.000000 -5.000000 4.352589
148 | v -0.000000 -5.000000 4.352589
149 | f 9 8 10
150 | f 7 6 8
151 | f 5 4 6
152 | f 3 2 4
153 | f 1 10 2
154 | f 6 4 8
155 | f 8 4 10
156 | f 10 4 2
157 | f 11 13 12
158 | f 14 16 15
159 | f 16 14 17
160 | f 14 18 17
161 | f 19 21 20
162 | f 18 21 22
163 | f 21 24 23
164 | f 21 25 24
165 | f 24 27 26
166 | f 25 27 24
167 | f 27 28 26
168 | f 27 29 28
169 | f 28 31 30
170 | f 29 31 28
171 | f 31 33 32
172 | f 31 34 33
173 | f 28 31 35
174 | f 33 37 36
175 | f 34 37 33
176 | f 37 39 38
177 | f 37 40 39
178 | f 39 42 41
179 | f 40 42 39
180 | f 42 44 43
181 | f 42 45 44
182 | f 45 46 44
183 | f 45 48 47
184 | f 45 49 48
185 | f 49 50 48
186 | f 49 52 51
187 | f 11 53 13
188 | f 14 54 55
189 | f 56 18 55
190 | f 14 55 18
191 | f 57 58 21
192 | f 18 59 21
193 | f 21 60 61
194 | f 21 61 25
195 | f 61 62 27
196 | f 25 61 27
197 | f 27 62 63
198 | f 27 63 29
199 | f 63 64 31
200 | f 29 63 31
201 | f 31 65 66
202 | f 31 66 34
203 | f 63 67 31
204 | f 66 68 37
205 | f 34 66 37
206 | f 37 69 70
207 | f 37 70 40
208 | f 70 71 42
209 | f 40 70 42
210 | f 42 72 73
211 | f 42 73 45
212 | f 45 73 74
213 | f 45 75 76
214 | f 45 76 49
215 | f 49 76 77
216 | f 49 78 52
217 | f 79 81 80
218 | f 82 84 83
219 | f 84 82 85
220 | f 82 86 85
221 | f 87 89 88
222 | f 86 89 90
223 | f 89 92 91
224 | f 89 93 92
225 | f 92 95 94
226 | f 93 95 92
227 | f 95 96 94
228 | f 95 97 96
229 | f 96 99 98
230 | f 97 99 96
231 | f 99 101 100
232 | f 99 102 101
233 | f 96 99 103
234 | f 101 105 104
235 | f 102 105 101
236 | f 105 107 106
237 | f 105 108 107
238 | f 107 110 109
239 | f 108 110 107
240 | f 110 112 111
241 | f 110 113 112
242 | f 113 114 112
243 | f 113 116 115
244 | f 113 117 116
245 | f 117 118 116
246 | f 117 120 119
247 | f 79 121 81
248 | f 82 122 123
249 | f 124 86 123
250 | f 82 123 86
251 | f 125 126 89
252 | f 86 127 89
253 | f 89 128 129
254 | f 89 129 93
255 | f 129 130 95
256 | f 93 129 95
257 | f 95 130 131
258 | f 95 131 97
259 | f 131 132 99
260 | f 97 131 99
261 | f 99 133 134
262 | f 99 134 102
263 | f 131 135 99
264 | f 134 136 105
265 | f 102 134 105
266 | f 105 137 138
267 | f 105 138 108
268 | f 138 139 110
269 | f 108 138 110
270 | f 110 140 141
271 | f 110 141 113
272 | f 113 141 142
273 | f 113 143 144
274 | f 113 144 117
275 | f 117 144 145
276 | f 117 146 120
--------------------------------------------------------------------------------
/digital-images/readwrite.cpp:
--------------------------------------------------------------------------------
1 | //[header]
2 | // Digital Images
3 | //[/header]
4 | //[compile]
5 | // Download the readwrite.cpp and xmas.ppm file to a folder.
6 | // Open a shell/terminal, and run the following command where the files is saved:
7 | //
8 | // clang++ -o readwrite readwrite.cpp -std=c++11 -O3
9 | //
10 | // You can use c++ if you don't use clang++
11 | //
12 | // Run with: ./readwrite. Open the resulting image (ppm) in Photoshop or any program
13 | // reading PPM files.
14 | //[/compile]
15 | //[ignore]
16 | // Copyright (C) 2016 www.scratchapixel.com
17 | //
18 | // This program is free software: you can redistribute it and/or modify
19 | // it under the terms of the GNU General Public License as published by
20 | // the Free Software Foundation, either version 3 of the License, or
21 | // (at your option) any later version.
22 | //
23 | // This program is distributed in the hope that it will be useful,
24 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
25 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 | // GNU General Public License for more details.
27 | //
28 | // You should have received a copy of the GNU General Public License
29 | // along with this program. If not, see .
30 | //[/ignore]
31 |
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 |
39 | // [comment]
40 | // The main Image class
41 | // [/comment]
42 | class Image
43 | {
44 | public:
45 |
46 | struct Rgb
47 | {
48 | Rgb() : r(0), g(0), b(0) {}
49 | Rgb(float rr) : r(rr), g(rr), b(rr) {}
50 | Rgb(float rr, float gg, float bb) : r(rr), g(gg), b(bb) {}
51 | float r, g, b;
52 | };
53 |
54 | Image() : w(0), h(0), pixels(nullptr) {}
55 | Image(const unsigned int &_w, const unsigned int &_h) :
56 | w(_w), h(_h), pixels(nullptr)
57 | {
58 | pixels = new Rgb[w * h];
59 | for (int i = 0; i < w * h; ++i) pixels[i] = 0;
60 | }
61 | ~Image() { if (pixels != nullptr) delete [] pixels; }
62 | unsigned int w, h;
63 | Rgb *pixels;
64 | };
65 |
66 | // [comment]
67 | // Save an image to PPM image file
68 | // [/comment]
69 | void savePPM(const Image &img, const char *filename)
70 | {
71 | if (img.w == 0 || img.h == 0) { fprintf(stderr, "Can't save an empty image\n"); return; }
72 | std::ofstream ofs;
73 | try {
74 | ofs.open(filename, std::ios::binary); // need to spec. binary mode for Windows users
75 | if (ofs.fail()) throw("Can't open output file");
76 | ofs << "P6\n" << img.w << " " << img.h << "\n255\n";
77 | unsigned char r, g, b;
78 | // loop over each pixel in the image, clamp and convert to byte format
79 | for (int i = 0; i < img.w * img.h; ++i) {
80 | r = static_cast(std::min(1.f, img.pixels[i].r) * 255);
81 | g = static_cast(std::min(1.f, img.pixels[i].g) * 255);
82 | b = static_cast(std::min(1.f, img.pixels[i].b) * 255);
83 | ofs << r << g << b;
84 | }
85 | ofs.close();
86 | }
87 | catch (const char *err) {
88 | fprintf(stderr, "%s\n", err);
89 | ofs.close();
90 | }
91 | }
92 |
93 | // [comment]
94 | // Read a PPM image file
95 | // [/comment]
96 | Image readPPM(const char *filename)
97 | {
98 | std::ifstream ifs;
99 | ifs.open(filename, std::ios::binary); // need to spec. binary mode for Windows users
100 | Image img;
101 | try {
102 | if (ifs.fail()) { throw("Can't open input file"); }
103 | std::string header;
104 | int w, h, b;
105 | ifs >> header;
106 | if (strcmp(header.c_str(), "P6") != 0) throw("Can't read input file");
107 | ifs >> w >> h >> b;
108 | img.w = w; img.h = h;
109 | img.pixels = new Image::Rgb[w * h]; // this is throw an exception if bad_alloc
110 | ifs.ignore(256, '\n'); // skip empty lines in necessary until we get to the binary data
111 | unsigned char pix[3];
112 | // read each pixel one by one and convert bytes to floats
113 | for (int i = 0; i < w * h; ++i) {
114 | ifs.read(reinterpret_cast(pix), 3);
115 | img.pixels[i].r = pix[0] / 255.f;
116 | img.pixels[i].g = pix[1] / 255.f;
117 | img.pixels[i].b = pix[2] / 255.f;
118 | }
119 | ifs.close();
120 | }
121 | catch (const char *err) {
122 | fprintf(stderr, "%s\n", err);
123 | ifs.close();
124 | }
125 |
126 | return img;
127 | }
128 |
129 | // [comment]
130 | // Read/Write an image stored in the PPM format
131 | // [/comment]
132 | int main(int argc, char **argv)
133 | {
134 | Image I = readPPM("./xmas.ppm");
135 | savePPM(I, "./out.ppm");
136 |
137 | return 0;
138 | }
--------------------------------------------------------------------------------
/digital-images/xmas.ppm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/digital-images/xmas.ppm
--------------------------------------------------------------------------------
/global-illumination-path-tracing/objects.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/global-illumination-path-tracing/objects.zip
--------------------------------------------------------------------------------
/interpolation/.gitignore:
--------------------------------------------------------------------------------
1 | *exe
2 | interpolation
3 | *ppm
4 | *png
5 | *gif
6 | *tx
7 |
--------------------------------------------------------------------------------
/interpolation/interpolation.cpp:
--------------------------------------------------------------------------------
1 | // (c) www.scratchapixel.com - 2024.
2 | // Distributed under the terms of the CC BY-NC-ND 4.0 License.
3 | // https://creativecommons.org/licenses/by-nc-nd/4.0/
4 | // Contributors:
5 | // - Scratchpixel
6 | // - Kristopolous / Chris Mckenzie
7 | // clang++ -std=c++23 -O3 -o interpolation.exe interpolation.cpp
8 |
9 | #define _CRT_SECURE_NO_WARNINGS
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | template
19 | class Color3 {
20 | public:
21 | Color3() : r(0), g(0), b(0) {}
22 | Color3(T rr) : r(rr), g(rr), b(rr) {}
23 | Color3(T rr, T gg, T bb) : r(rr), g(gg), b(bb) {}
24 | Color3 operator * (const T& v) const {
25 | return Color3(r * v, g * v, b * v);
26 | }
27 | Color3 operator + (const Color3& c) const {
28 | return Color3(r + c.r, g + c.g, b + c.b);
29 | }
30 | friend Color3 operator * (const float& f, const Color3& c) {
31 | return Color3(c.r * f, c.g * f, c.b * f);
32 | }
33 | friend std::ostream& operator << (std::ostream& os, const Color3& c) {
34 | os << c.r << " " << c.g << " " << c.b;
35 | return os;
36 | }
37 | float r, g, b;
38 | };
39 |
40 | using Color3f = Color3;
41 |
42 | void saveToPPM(const char* fn, const Color3f* c, const int& width, const int& height) {
43 | std::ofstream ofs;
44 | // flags are necessary if your compile on Windows
45 | ofs.open(fn, std::ios::out | std::ios::binary);
46 | if (ofs.fail()) {
47 | fprintf(stderr, "ERROR: can't save image to file %s\n", fn);
48 | }
49 | else {
50 | ofs << "P6\n" << width << " " << height << "\n255\n";
51 | const Color3f* pc = c;
52 | for (int j = 0; j < height; ++j) {
53 | for (int i = 0; i < width; ++i) {
54 | char r = static_cast(std::min(255.f, 255 * pc->r + 0.5f));
55 | char g = static_cast(std::min(255.f, 255 * pc->g + 0.5f));
56 | char b = static_cast(std::min(255.f, 255 * pc->b + 0.5f));
57 | ofs << r << g << b;
58 | pc++;
59 | }
60 | }
61 | }
62 | ofs.close();
63 | }
64 |
65 | template
66 | T bilinear(
67 | const float& tx,
68 | const float& ty,
69 | const T& c00,
70 | const T& c10,
71 | const T& c01,
72 | const T& c11) {
73 | #if 1
74 | T a = c00 * (1.f - tx) + c10 * tx;
75 | T b = c01 * (1.f - tx) + c11 * tx;
76 | return a * (1.f - ty) + b * ty;
77 | #else
78 | return (1 - tx) * (1 - ty) * c00 +
79 | tx * (1 - ty) * c10 +
80 | (1.f - tx) * ty * c01 +
81 | tx * ty * c11;
82 | #endif
83 | }
84 |
85 | std::random_device rd; // Obtain a random number from hardware
86 | std::mt19937 gen(rd()); // Seed the generator
87 | std::uniform_real_distribution<> distr(0.0, 1.0); // Define the range
88 |
89 | void TestBilinearInterpolation() {
90 |
91 | // testing bilinear interpolation
92 | int imageWidth = 512;
93 | int gridSizeX = 9, gridSizeY = 9;
94 | Color3f* grid2d = new Color3f[(gridSizeX + 1) * (gridSizeY + 1)]; // lattices
95 | // fill grid with random colors
96 | Color3f c[4] = { Color3f(1,0,0), Color3f(0,1,0), Color3f(0,0,1), Color3f(1,1,0) };
97 | for (int j = 0, k = 0; j <= gridSizeY; ++j) {
98 | for (int i = 0; i <= gridSizeX; ++i, ++k) {
99 | grid2d[j * (gridSizeX + 1) + i] = Color3f(distr(gen), distr(gen), distr(gen));
100 | printf("%d %d %f\n", i, j, grid2d[j * (gridSizeX + 1) + i].r);
101 | }
102 | }
103 | // now compute our final image using bilinear interpolation
104 | Color3f* imageData = new Color3f[imageWidth * imageWidth], * pixel = imageData;
105 | for (int j = 0; j < imageWidth; ++j) {
106 | for (int i = 0; i < imageWidth; ++i) {
107 | // convert i,j to grid coordinates
108 | float gx = i / float(imageWidth) * gridSizeX; // be careful to interpolate boundaries
109 | float gy = j / float(imageWidth) * gridSizeY; // be careful to interpolate boundaries
110 | int gxi = int(gx);
111 | int gyi = int(gy);
112 | const Color3f& c00 = grid2d[gyi * (gridSizeX + 1) + gxi];
113 | const Color3f& c10 = grid2d[gyi * (gridSizeX + 1) + (gxi + 1)];
114 | const Color3f& c01 = grid2d[(gyi + 1) * (gridSizeX + 1) + gxi];
115 | const Color3f& c11 = grid2d[(gyi + 1) * (gridSizeX + 1) + (gxi + 1)];
116 | *(pixel++) = bilinear(gx - gxi, gy - gyi, c00, c10, c01, c11);
117 | }
118 | }
119 | saveToPPM("./bilinear.ppm", imageData, imageWidth, imageWidth);
120 | // uncomnent this code if you want to see what the input colors look like
121 | pixel = imageData;
122 | int cellsize = imageWidth / (gridSizeX);
123 | fprintf(stderr, "%d\n", cellsize);
124 | for (int j = 0; j < imageWidth; ++j) {
125 | for (int i = 0; i < imageWidth; ++i) {
126 | float gx = (i + cellsize / 2) / float(imageWidth);
127 | float gy = (j + cellsize / 2) / float(imageWidth);
128 | int gxi = static_cast(gx * gridSizeX);
129 | int gyi = static_cast(gy * gridSizeY);
130 | *pixel = grid2d[gyi * (gridSizeX + 1) + gxi];
131 | int mx = (i + cellsize / 2) % cellsize;
132 | int my = (j + cellsize / 2) % cellsize;
133 | int ma = cellsize / 2 + 2, mb = cellsize / 2 - 2;
134 | if (mx < ma && mx > mb && my < ma && my > mb)
135 | *pixel = Color3f(0, 0, 0);
136 | pixel++;
137 | }
138 | }
139 | saveToPPM("./inputbilinear1.ppm", imageData, imageWidth, imageWidth);
140 | delete[] imageData;
141 | }
142 |
143 | #define IX(size, i, j, k) (i * size * size + j * size + k)
144 |
145 |
146 | /**
147 | * Trilinear interpolation example. We take a cube of size "gridsize" and then
148 | * "upscale" it to scale * gridsize. To evaluate the result of a random
149 | * point within the grid, we pick the 8 point's neighbor cells and trilinearly
150 | * interpolate the results. Each of the results get written to a sequentially
151 | * named ppm file. They can be viewed, in an animation that cycles through the
152 | * slices on the command line using a tool like "mpv" like so:
153 | *
154 | * mpv --speed=10 --image-display-duration=0.1 trilinear-slice-*.ppm
155 | *
156 | * *or* if you have imagemagick you can make it into an animation like so:
157 | *
158 | * magick trilinear-slice-*.ppm demo.gif
159 | *
160 | */
161 | void TestTrilinearInterpolation() {
162 | //
163 | // Note that the size is gridSize ^ 3 and correspondingly, since we "upscale"
164 | // in each dimension, you will have (gridSize * scale) ^ 3 for the output
165 | // image. In this particular example, the initial 3D grid is 8x8x8 and the
166 | // upres grid is 128x128x128.
167 | //
168 | uint32_t grid_size = 8; // number of cells along any of x, y and z-axis
169 | uint32_t scale = 16;
170 |
171 | uint32_t src_num_verts = grid_size + 1;
172 | uint32_t target_num_verts = grid_size * scale + 1;
173 | uint32_t src_array_size = src_num_verts * src_num_verts * src_num_verts;
174 | uint32_t target_array_size = target_num_verts * target_num_verts * target_num_verts;
175 |
176 | std::unique_ptr scr_grid3d = std::make_unique(src_array_size);
177 | std::unique_ptr target_grid3d = std::make_unique(target_array_size);
178 | std::memset(target_grid3d.get(), 0x0, sizeof(Color3f) * target_array_size);
179 |
180 | for (uint32_t k = 0; k < src_num_verts; ++k) {
181 | for (uint32_t j = 0; j < src_num_verts; ++j) {
182 | for (uint32_t i = 0; i < src_num_verts; ++i) {
183 | scr_grid3d[IX(src_num_verts, i, j, k)] = Color3f(distr(gen), distr(gen), distr(gen));
184 | }
185 | }
186 | }
187 |
188 | // interpolate grid data, we assume the grid is a unit cube
189 | float gx, gy, gz;
190 | uint32_t gxi0, gyi0, gzi0, gxi1, gyi1, gzi1;
191 | float tx, ty, tz;
192 |
193 | for (uint32_t z = 0; z < target_num_verts; ++z) {
194 | gz = float(z) / scale;
195 | gzi0 = uint32_t(gz); tz = gz - gzi0;
196 | gzi1 = std::min(grid_size, gzi0 + 1);
197 |
198 | for (uint32_t y = 0; y < target_num_verts; ++y) {
199 | gy = float(y) / scale;
200 | gyi0 = uint32_t(gy); ty = gy - gyi0;
201 | gyi1 = std::min(grid_size, gyi0 + 1);
202 |
203 | for (uint32_t x = 0; x < target_num_verts; ++x) {
204 | gx = float(x) / scale;
205 | gxi0 = uint32_t(gx); tx = gx - gxi0;
206 | gxi1 = std::min(grid_size, gxi0 + 1);
207 |
208 | const Color3f& c000 = scr_grid3d[IX(src_num_verts, gxi0, gyi0, gzi0)];
209 | const Color3f& c001 = scr_grid3d[IX(src_num_verts, gxi0, gyi0, gzi1)];
210 | const Color3f& c010 = scr_grid3d[IX(src_num_verts, gxi0, gyi1, gzi0)];
211 | const Color3f& c011 = scr_grid3d[IX(src_num_verts, gxi0, gyi1, gzi1)];
212 |
213 | const Color3f& c100 = scr_grid3d[IX(src_num_verts, gxi1, gyi0, gzi0)];
214 | const Color3f& c101 = scr_grid3d[IX(src_num_verts, gxi1, gyi0, gzi1)];
215 | const Color3f& c110 = scr_grid3d[IX(src_num_verts, gxi1, gyi1, gzi0)];
216 | const Color3f& c111 = scr_grid3d[IX(src_num_verts, gxi1, gyi1, gzi1)];
217 | #if 1
218 | // interpolate vertex data in zy plane at x and x+1
219 | Color3f e = bilinear(tz, ty, c000, c001, c010, c011);
220 | Color3f f = bilinear(tz, ty, c100, c101, c110, c111);
221 |
222 | // interpolate along the x-axis
223 | Color3f g = e * (1 - tx) + f * tx;
224 |
225 | target_grid3d[IX(target_num_verts, x, y, z)] = g;
226 | #else
227 | Color3f g =
228 | (1 - tx) * (1 - ty) * (1 - tz) * c000 +
229 | tx * (1 - ty) * (1 - tz) * c100 +
230 | (1 - tx) * ty * (1 - tz) * c010 +
231 | tx * ty * (1 - tz) * c110 +
232 | (1 - tx) * (1 - ty) * tz * c001 +
233 | tx * (1 - ty) * tz * c101 +
234 | (1 - tx) * ty * tz * c011 +
235 | tx * ty * tz * c111;
236 | #endif
237 | }
238 | }
239 | }
240 | char file_name[100] = { 0 };
241 | for (uint32_t k = 0; k < grid_size * scale; ++k) {
242 | sprintf(file_name, "trilinear-slice-%04d.ppm", k);
243 | const Color3f* slice_data = target_grid3d.get() + k * target_num_verts * target_num_verts;
244 | saveToPPM(file_name, slice_data, target_num_verts, target_num_verts);
245 | }
246 | }
247 |
248 | int main(int argc, char** argv)
249 | {
250 | //TestBilinearInterpolation();
251 | TestTrilinearInterpolation();
252 |
253 | return 0;
254 | }
255 |
--------------------------------------------------------------------------------
/introduction-rendering/raytracer:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/introduction-rendering/raytracer
--------------------------------------------------------------------------------
/introduction-rendering/raytracer.cpp:
--------------------------------------------------------------------------------
1 | // [header]
2 | // A very basic raytracer example.
3 | // [/header]
4 | // [compile]
5 | // c++ -o raytracer -O3 -Wall raytracer.cpp
6 | // [/compile]
7 | // [ignore]
8 | // Copyright (C) 2012 www.scratchapixel.com
9 | //
10 | // This program is free software: you can redistribute it and/or modify
11 | // it under the terms of the GNU General Public License as published by
12 | // the Free Software Foundation, either version 3 of the License, or
13 | // (at your option) any later version.
14 | //
15 | // This program is distributed in the hope that it will be useful,
16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | // GNU General Public License for more details.
19 | //
20 | // You should have received a copy of the GNU General Public License
21 | // along with this program. If not, see .
22 | // [/ignore]
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | #if defined __linux__ || defined __APPLE__
32 | // "Compiled for Linux
33 | #else
34 | // Windows doesn't define these values by default, Linux does
35 | #define M_PI 3.141592653589793
36 | #define INFINITY 1e8
37 | #endif
38 |
39 | template
40 | class Vec3
41 | {
42 | public:
43 | T x, y, z;
44 | Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
45 | Vec3(T xx) : x(xx), y(xx), z(xx) {}
46 | Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
47 | Vec3& normalize()
48 | {
49 | T nor2 = length2();
50 | if (nor2 > 0) {
51 | T invNor = 1 / sqrt(nor2);
52 | x *= invNor, y *= invNor, z *= invNor;
53 | }
54 | return *this;
55 | }
56 | Vec3 operator * (const T &f) const { return Vec3(x * f, y * f, z * f); }
57 | Vec3 operator * (const Vec3 &v) const { return Vec3(x * v.x, y * v.y, z * v.z); }
58 | T dot(const Vec3 &v) const { return x * v.x + y * v.y + z * v.z; }
59 | Vec3 operator - (const Vec3 &v) const { return Vec3(x - v.x, y - v.y, z - v.z); }
60 | Vec3 operator + (const Vec3 &v) const { return Vec3(x + v.x, y + v.y, z + v.z); }
61 | Vec3& operator += (const Vec3 &v) { x += v.x, y += v.y, z += v.z; return *this; }
62 | Vec3& operator *= (const Vec3 &v) { x *= v.x, y *= v.y, z *= v.z; return *this; }
63 | Vec3 operator - () const { return Vec3(-x, -y, -z); }
64 | T length2() const { return x * x + y * y + z * z; }
65 | T length() const { return sqrt(length2()); }
66 | friend std::ostream & operator << (std::ostream &os, const Vec3 &v)
67 | {
68 | os << "[" << v.x << " " << v.y << " " << v.z << "]";
69 | return os;
70 | }
71 | };
72 |
73 | typedef Vec3 Vec3f;
74 |
75 | class Sphere
76 | {
77 | public:
78 | Vec3f center; /// position of the sphere
79 | float radius, radius2; /// sphere radius and radius^2
80 | Vec3f surfaceColor, emissionColor; /// surface color and emission (light)
81 | float transparency, reflection; /// surface transparency and reflectivity
82 | Sphere(
83 | const Vec3f &c,
84 | const float &r,
85 | const Vec3f &sc,
86 | const float &refl = 0,
87 | const float &transp = 0,
88 | const Vec3f &ec = 0) :
89 | center(c), radius(r), radius2(r * r), surfaceColor(sc), emissionColor(ec),
90 | transparency(transp), reflection(refl)
91 | { /* empty */ }
92 | //[comment]
93 | // Compute a ray-sphere intersection using the geometric solution
94 | //[/comment]
95 | bool intersect(const Vec3f &rayorig, const Vec3f &raydir, float &t0, float &t1) const
96 | {
97 | Vec3f l = center - rayorig;
98 | float tca = l.dot(raydir);
99 | if (tca < 0) return false;
100 | float d2 = l.dot(l) - tca * tca;
101 | if (d2 > radius2) return false;
102 | float thc = sqrt(radius2 - d2);
103 | t0 = tca - thc;
104 | t1 = tca + thc;
105 |
106 | return true;
107 | }
108 | };
109 |
110 | //[comment]
111 | // This variable controls the maximum recursion depth
112 | //[/comment]
113 | #define MAX_RAY_DEPTH 5
114 |
115 | float mix(const float &a, const float &b, const float &mix)
116 | {
117 | return b * mix + a * (1 - mix);
118 | }
119 |
120 | //[comment]
121 | // This is the main trace function. It takes a ray as argument (defined by its origin
122 | // and direction). We test if this ray intersects any of the geometry in the scene.
123 | // If the ray intersects an object, we compute the intersection point, the normal
124 | // at the intersection point, and shade this point using this information.
125 | // Shading depends on the surface property (is it transparent, reflective, diffuse).
126 | // The function returns a color for the ray. If the ray intersects an object that
127 | // is the color of the object at the intersection point, otherwise it returns
128 | // the background color.
129 | //[/comment]
130 | Vec3f trace(
131 | const Vec3f &rayorig,
132 | const Vec3f &raydir,
133 | const std::vector &spheres,
134 | const int &depth)
135 | {
136 | //if (raydir.length() != 1) std::cerr << "Error " << raydir << std::endl;
137 | float tnear = INFINITY;
138 | const Sphere* sphere = NULL;
139 | // find intersection of this ray with the sphere in the scene
140 | for (unsigned i = 0; i < spheres.size(); ++i) {
141 | float t0 = INFINITY, t1 = INFINITY;
142 | if (spheres[i].intersect(rayorig, raydir, t0, t1)) {
143 | if (t0 < 0) t0 = t1;
144 | if (t0 < tnear) {
145 | tnear = t0;
146 | sphere = &spheres[i];
147 | }
148 | }
149 | }
150 | // if there's no intersection return black or background color
151 | if (!sphere) return Vec3f(2);
152 | Vec3f surfaceColor = 0; // color of the ray/surfaceof the object intersected by the ray
153 | Vec3f phit = rayorig + raydir * tnear; // point of intersection
154 | Vec3f nhit = phit - sphere->center; // normal at the intersection point
155 | nhit.normalize(); // normalize normal direction
156 | // If the normal and the view direction are not opposite to each other
157 | // reverse the normal direction. That also means we are inside the sphere so set
158 | // the inside bool to true. Finally reverse the sign of IdotN which we want
159 | // positive.
160 | float bias = 1e-4; // add some bias to the point from which we will be tracing
161 | bool inside = false;
162 | if (raydir.dot(nhit) > 0) nhit = -nhit, inside = true;
163 | if ((sphere->transparency > 0 || sphere->reflection > 0) && depth < MAX_RAY_DEPTH) {
164 | float facingratio = -raydir.dot(nhit);
165 | // change the mix value to tweak the effect
166 | float fresneleffect = mix(pow(1 - facingratio, 3), 1, 0.1);
167 | // compute reflection direction (not need to normalize because all vectors
168 | // are already normalized)
169 | Vec3f refldir = raydir - nhit * 2 * raydir.dot(nhit);
170 | refldir.normalize();
171 | Vec3f reflection = trace(phit + nhit * bias, refldir, spheres, depth + 1);
172 | Vec3f refraction = 0;
173 | // if the sphere is also transparent compute refraction ray (transmission)
174 | if (sphere->transparency) {
175 | float ior = 1.1, eta = (inside) ? ior : 1 / ior; // are we inside or outside the surface?
176 | float cosi = -nhit.dot(raydir);
177 | float k = 1 - eta * eta * (1 - cosi * cosi);
178 | Vec3f refrdir = raydir * eta + nhit * (eta * cosi - sqrt(k));
179 | refrdir.normalize();
180 | refraction = trace(phit - nhit * bias, refrdir, spheres, depth + 1);
181 | }
182 | // the result is a mix of reflection and refraction (if the sphere is transparent)
183 | surfaceColor = (
184 | reflection * fresneleffect +
185 | refraction * (1 - fresneleffect) * sphere->transparency) * sphere->surfaceColor;
186 | }
187 | else {
188 | // it's a diffuse object, no need to raytrace any further
189 | for (unsigned i = 0; i < spheres.size(); ++i) {
190 | if (spheres[i].emissionColor.x > 0) {
191 | // this is a light
192 | Vec3f transmission = 1;
193 | Vec3f lightDirection = spheres[i].center - phit;
194 | lightDirection.normalize();
195 | for (unsigned j = 0; j < spheres.size(); ++j) {
196 | if (i != j) {
197 | float t0, t1;
198 | if (spheres[j].intersect(phit + nhit * bias, lightDirection, t0, t1)) {
199 | transmission = 0;
200 | break;
201 | }
202 | }
203 | }
204 | surfaceColor += sphere->surfaceColor * transmission *
205 | std::max(float(0), nhit.dot(lightDirection)) * spheres[i].emissionColor;
206 | }
207 | }
208 | }
209 |
210 | return surfaceColor + sphere->emissionColor;
211 | }
212 |
213 | //[comment]
214 | // Main rendering function. We compute a camera ray for each pixel of the image
215 | // trace it and return a color. If the ray hits a sphere, we return the color of the
216 | // sphere at the intersection point, else we return the background color.
217 | //[/comment]
218 | void render(const std::vector &spheres)
219 | {
220 | unsigned width = 640, height = 480;
221 | Vec3f *image = new Vec3f[width * height], *pixel = image;
222 | float invWidth = 1 / float(width), invHeight = 1 / float(height);
223 | float fov = 30, aspectratio = width / float(height);
224 | float angle = tan(M_PI * 0.5 * fov / 180.);
225 | // Trace rays
226 | for (unsigned y = 0; y < height; ++y) {
227 | for (unsigned x = 0; x < width; ++x, ++pixel) {
228 | float xx = (2 * ((x + 0.5) * invWidth) - 1) * angle * aspectratio;
229 | float yy = (1 - 2 * ((y + 0.5) * invHeight)) * angle;
230 | Vec3f raydir(xx, yy, -1);
231 | raydir.normalize();
232 | *pixel = trace(Vec3f(0), raydir, spheres, 0);
233 | }
234 | }
235 | // Save result to a PPM image (keep these flags if you compile under Windows)
236 | std::ofstream ofs("./untitled.ppm", std::ios::out | std::ios::binary);
237 | ofs << "P6\n" << width << " " << height << "\n255\n";
238 | for (unsigned i = 0; i < width * height; ++i) {
239 | ofs << (unsigned char)(std::min(float(1), image[i].x) * 255) <<
240 | (unsigned char)(std::min(float(1), image[i].y) * 255) <<
241 | (unsigned char)(std::min(float(1), image[i].z) * 255);
242 | }
243 | ofs.close();
244 | delete [] image;
245 | }
246 |
247 | //[comment]
248 | // In the main function, we will create the scene which is composed of 5 spheres
249 | // and 1 light (which is also a sphere). Then, once the scene description is complete
250 | // we render that scene, by calling the render() function.
251 | //[/comment]
252 | int main(int argc, char **argv)
253 | {
254 | srand48(13);
255 | std::vector spheres;
256 | // position, radius, surface color, reflectivity, transparency, emission color
257 | spheres.push_back(Sphere(Vec3f( 0.0, -10004, -20), 10000, Vec3f(0.20, 0.20, 0.20), 0, 0.0));
258 | spheres.push_back(Sphere(Vec3f( 0.0, 0, -20), 4, Vec3f(1.00, 0.32, 0.36), 1, 0.5));
259 | spheres.push_back(Sphere(Vec3f( 5.0, -1, -15), 2, Vec3f(0.90, 0.76, 0.46), 1, 0.0));
260 | spheres.push_back(Sphere(Vec3f( 5.0, 0, -25), 3, Vec3f(0.65, 0.77, 0.97), 1, 0.0));
261 | spheres.push_back(Sphere(Vec3f(-5.5, 0, -15), 3, Vec3f(0.90, 0.90, 0.90), 1, 0.0));
262 | // light
263 | spheres.push_back(Sphere(Vec3f( 0.0, 20, -30), 3, Vec3f(0.00, 0.00, 0.00), 0, 0.0, Vec3f(3)));
264 | render(spheres);
265 |
266 | return 0;
267 | }
--------------------------------------------------------------------------------
/introduction-rendering/untitled.ppm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scratchapixel/scratchapixel-code/47dd2e86cc02b1aad62ce2a24d0568f1e5b06cce/introduction-rendering/untitled.ppm
--------------------------------------------------------------------------------
/introduction-to-lighting/.gitignore:
--------------------------------------------------------------------------------
1 | test.cc
2 | *.mel
3 |
--------------------------------------------------------------------------------
/introduction-to-lighting/math.h:
--------------------------------------------------------------------------------
1 | // (c) www.scratchapixel.com - 2024.
2 | // Distributed under the terms of the CC BY-NC-ND 4.0 License.
3 | // https://creativecommons.org/licenses/by-nc-nd/4.0/
4 |
5 | #ifndef _MATH_H_
6 | #define _MATH_H_
7 |
8 | #include
9 |
10 | template
11 | class Vec2 {
12 | public:
13 | using type = T;
14 | Vec2() noexcept : x(0), y(0) {}
15 | Vec2(T xx) : x(xx), y(xx) {}
16 | Vec2(T xx, T yy) : x(xx), y(yy) {}
17 | T x, y;
18 | };
19 |
20 | template
21 | class Vec3 {
22 | public:
23 | using type = T;
24 | Vec3() noexcept : x(0), y(0), z(0) {}
25 | Vec3(T xx) : x(xx), y(xx), z(xx) {}
26 | Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
27 | constexpr unsigned int Dimensions() const noexcept {
28 | return 3;
29 | }
30 | constexpr T& operator[](int i) noexcept {
31 | return (&x)[i];
32 | }
33 | constexpr const T& operator[](int i) const noexcept {
34 | return (&x)[i];
35 | }
36 | // Return a reference to the instance it modifies
37 | const Vec3& Normalize() noexcept {
38 | T len = std::sqrt(x * x + y * y + z * z);
39 | if (len != T(0)) [[likely]] {
40 | x /= len;
41 | y /= len;
42 | z /= len;
43 | }
44 | return *this;
45 | }
46 | constexpr Vec3 Cross(const Vec3& v) const noexcept {
47 | return {y * v.z - z * v.y,
48 | z * v.x - x * v.z,
49 | x * v.y - y * v.x};
50 | }
51 | constexpr T Dot(const Vec3& v) const noexcept {
52 | return x * v.x + y * v.y + z * v.z;
53 | }
54 | constexpr Vec3 operator-(const Vec3& v) const noexcept {
55 | return {x - v.x, y - v.y, z - v.z};
56 | }
57 | constexpr Vec3 operator+(const Vec3& v) const noexcept {
58 | return {x + v.x, y + v.y, z + v.z};
59 | }
60 | Vec3& operator+=(const Vec3& v) noexcept {
61 | x += v.x;
62 | y += v.y;
63 | z += v.z;
64 | return *this;
65 | }
66 | template
67 | Vec3& operator/=(const S a) noexcept {
68 | x /= a;
69 | y /= a;
70 | z /= a;
71 | return *this;
72 | }
73 | constexpr Vec3 operator*(T a) const noexcept {
74 | return {x * a, y * a, z * a};
75 | }
76 | constexpr Vec3 operator/(T a) const noexcept {
77 | return {x / a, y / a, z / a};
78 | }
79 | friend constexpr Vec3 operator*(T a, const Vec3& v) noexcept{
80 | return {a * v.x, a * v.y, a * v.z};
81 | }
82 | // @todo not safe
83 | friend constexpr Vec3 operator/(T a, const Vec3& v) noexcept{
84 | return {a / v.x, a / v.y, a / v.z};
85 | }
86 | constexpr T Max() const noexcept {
87 | return std::max(std::max(x, y), z);
88 | }
89 | constexpr T Length() const noexcept {
90 | return std::sqrt(x * x + y * y + z * z);
91 | }
92 | constexpr bool operator==(const Vec3& v) const noexcept {
93 | return x == v.x && y == v.y && z == v.z;
94 | }
95 | friend std::ostream& operator<<(std::ostream& os, const Vec3& v) {
96 | return os << v.x << " " << v.y << " " << v.z;
97 | }
98 | T x, y, z;
99 | };
100 |
101 | template
102 | class Box {
103 | public:
104 | V min_, max_;
105 | void MakeEmpty() noexcept {
106 | min_ = std::numeric_limits::max();
107 | max_ = std::numeric_limits::lowest();
108 | }
109 | void ExtendBy(const V& point) noexcept {
110 | for (unsigned int i = 0; i < min_.Dimensions(); ++i) {
111 | if (point[i] < min_[i]) min_[i] = point[i];
112 | if (point[i] > max_[i]) max_[i] = point[i];
113 | }
114 | }
115 | void ExtendBy(const Box& box) noexcept {
116 | for (unsigned int i = 0; i < min_.Dimensions(); ++i) {
117 | if (box.min_[i] < min_[i]) min_[i] = box.min_[i];
118 | if (box.max_[i] > max_[i]) max_[i] = box.max_[i];
119 | }
120 | }
121 | };
122 |
123 | using Box3f = Box>;
124 |
125 | template
126 | class Matrix44 {
127 | public:
128 | static const Matrix44 kIdentity;
129 | constexpr Matrix44() noexcept {
130 | x[0][0] = 1;
131 | x[0][1] = 0;
132 | x[0][2] = 0;
133 | x[0][3] = 0;
134 | x[1][0] = 0;
135 | x[1][1] = 1;
136 | x[1][2] = 0;
137 | x[1][3] = 0;
138 | x[2][0] = 0;
139 | x[2][1] = 0;
140 | x[2][2] = 1;
141 | x[2][3] = 0;
142 | x[3][0] = 0;
143 | x[3][1] = 0;
144 | x[3][2] = 0;
145 | x[3][3] = 1;
146 | }
147 | constexpr Matrix44(
148 | T a, T b, T c, T d,
149 | T e, T f, T g, T h,
150 | T i, T j, T k, T l,
151 | T m, T n, T o, T p) noexcept {
152 | x[0][0] = a; x[0][1] = b; x[0][2] = c; x[0][3] = d;
153 | x[1][0] = e; x[1][1] = f; x[1][2] = g; x[1][3] = h;
154 | x[2][0] = i; x[2][1] = j; x[2][2] = k; x[2][3] = l;
155 | x[3][0] = m; x[3][1] = n; x[3][2] = o; x[3][3] = p;
156 | }
157 | constexpr Matrix44(const T* m) {
158 | std::memcpy(&x[0], m, sizeof(T) * 16);
159 | }
160 | constexpr Matrix44& operator=(const Matrix44& v) noexcept {
161 | x[0][0] = v.x[0][0];
162 | x[0][1] = v.x[0][1];
163 | x[0][2] = v.x[0][2];
164 | x[0][3] = v.x[0][3];
165 | x[1][0] = v.x[1][0];
166 | x[1][1] = v.x[1][1];
167 | x[1][2] = v.x[1][2];
168 | x[1][3] = v.x[1][3];
169 | x[2][0] = v.x[2][0];
170 | x[2][1] = v.x[2][1];
171 | x[2][2] = v.x[2][2];
172 | x[2][3] = v.x[2][3];
173 | x[3][0] = v.x[3][0];
174 | x[3][1] = v.x[3][1];
175 | x[3][2] = v.x[3][2];
176 | x[3][3] = v.x[3][3];
177 | return *this;
178 | }
179 | constexpr bool operator==(const Matrix44& rhs) const noexcept {
180 | return x[0][0] == rhs.x[0][0] && x[0][1] == rhs.x[0][1] &&
181 | x[0][2] == rhs.x[0][2] && x[0][3] == rhs.x[0][3] &&
182 | x[1][0] == rhs.x[1][0] && x[1][1] == rhs.x[1][1] &&
183 | x[1][2] == rhs.x[1][2] && x[1][3] == rhs.x[1][3] &&
184 | x[2][0] == rhs.x[2][0] && x[2][1] == rhs.x[2][1] &&
185 | x[2][2] == rhs.x[2][2] && x[2][3] == rhs.x[2][3] &&
186 | x[3][0] == rhs.x[3][0] && x[3][1] == rhs.x[3][1] &&
187 | x[3][2] == rhs.x[3][2] && x[3][3] == rhs.x[3][3];
188 | }
189 | template
190 | void MultVecMatrix(const Vec3& src, Vec3& dst) const noexcept {
191 | S a, b, c, w;
192 |
193 | a = src.x * x[0][0] + src.y * x[1][0] + src.z * x[2][0] + x[3][0];
194 | b = src.x * x[0][1] + src.y * x[1][1] + src.z * x[2][1] + x[3][1];
195 | c = src.x * x[0][2] + src.y * x[1][2] + src.z * x[2][2] + x[3][2];
196 | w = src.x * x[0][3] + src.y * x[1][3] + src.z * x[2][3] + x[3][3];
197 |
198 | dst.x = a / w;
199 | dst.y = b / w;
200 | dst.z = c / w;
201 | }
202 | template
203 | void MultDirMatrix(const Vec3& src, Vec3& dst) const noexcept {
204 | S a, b, c;
205 |
206 | a = src.x * x[0][0] + src.y * x[1][0] + src.z * x[2][0];
207 | b = src.x * x[0][1] + src.y * x[1][1] + src.z * x[2][1];
208 | c = src.x * x[0][2] + src.y * x[1][2] + src.z * x[2][2];
209 |
210 | dst.x = a;
211 | dst.y = b;
212 | dst.z = c;
213 | }
214 | T* operator[] (int i) noexcept{
215 | return x[i];
216 | }
217 | const T* operator[] (int i) const noexcept {
218 | return x[i];
219 | }
220 | constexpr Matrix44 Transposed () const noexcept {
221 | return Matrix44 (
222 | x[0][0],
223 | x[1][0],
224 | x[2][0],
225 | x[3][0],
226 | x[0][1],
227 | x[1][1],
228 | x[2][1],
229 | x[3][1],
230 | x[0][2],
231 | x[1][2],
232 | x[2][2],
233 | x[3][2],
234 | x[0][3],
235 | x[1][3],
236 | x[2][3],
237 | x[3][3]);
238 | }
239 | constexpr const Matrix44& Invert() noexcept {
240 | *this = Inverse();
241 | return *this;
242 | }
243 | constexpr Matrix44 Inverse() const noexcept {
244 | if (x[0][3] != 0 || x[1][3] != 0 || x[2][3] != 0 || x[3][3] != 1)
245 | abort();
246 | // return gjInverse();
247 |
248 | Matrix44 s (
249 | x[1][1] * x[2][2] - x[2][1] * x[1][2],
250 | x[2][1] * x[0][2] - x[0][1] * x[2][2],
251 | x[0][1] * x[1][2] - x[1][1] * x[0][2],
252 | 0,
253 |
254 | x[2][0] * x[1][2] - x[1][0] * x[2][2],
255 | x[0][0] * x[2][2] - x[2][0] * x[0][2],
256 | x[1][0] * x[0][2] - x[0][0] * x[1][2],
257 | 0,
258 |
259 | x[1][0] * x[2][1] - x[2][0] * x[1][1],
260 | x[2][0] * x[0][1] - x[0][0] * x[2][1],
261 | x[0][0] * x[1][1] - x[1][0] * x[0][1],
262 | 0,
263 |
264 | 0,
265 | 0,
266 | 0,
267 | 1);
268 |
269 | T r = x[0][0] * s.x[0][0] + x[0][1] * s.x[1][0] + x[0][2] * s.x[2][0];
270 |
271 | if (std::abs(r) >= 1) {
272 | for (int i = 0; i < 3; ++i) {
273 | for (int j = 0; j < 3; ++j) {
274 | s.x[i][j] /= r;
275 | }
276 | }
277 | }
278 | else {
279 | T mr = std::abs (r) / std::numeric_limits::min ();
280 |
281 | for (int i = 0; i < 3; ++i) {
282 | for (int j = 0; j < 3; ++j) {
283 | if (mr > std::abs (s.x[i][j])) {
284 | s.x[i][j] /= r;
285 | }
286 | else {
287 | return Matrix44 ();
288 | }
289 | }
290 | }
291 | }
292 |
293 | s.x[3][0] = -x[3][0] * s.x[0][0] - x[3][1] * s.x[1][0] - x[3][2] * s.x[2][0];
294 | s.x[3][1] = -x[3][0] * s.x[0][1] - x[3][1] * s.x[1][1] - x[3][2] * s.x[2][1];
295 | s.x[3][2] = -x[3][0] * s.x[0][2] - x[3][1] * s.x[1][2] - x[3][2] * s.x[2][2];
296 |
297 | return s;
298 | }
299 | friend std::ostream& operator<< (std::ostream& s, const Matrix44& m) {
300 | std::ios_base::fmtflags oldFlags = s.flags();
301 | int width;
302 |
303 | if (s.flags() & std::ios_base::fixed) {
304 | s.setf(std::ios_base::showpoint);
305 | width = static_cast (s.precision ()) + 5;
306 | }
307 | else {
308 | s.setf(std::ios_base::scientific);
309 | s.setf(std::ios_base::showpoint);
310 | width = static_cast (s.precision ()) + 8;
311 | }
312 |
313 | s << "(" << std::setw(width) << m[0][0] << " " << std::setw(width)
314 | << m[0][1] << " " << std::setw(width) << m[0][2] << " "
315 | << std::setw(width) << m[0][3] << "\n"
316 | <<
317 |
318 | " " << std::setw(width) << m[1][0] << " " << std::setw(width)
319 | << m[1][1] << " " << std::setw(width) << m[1][2] << " "
320 | << std::setw(width) << m[1][3] << "\n"
321 | <<
322 |
323 | " " << std::setw(width) << m[2][0] << " " << std::setw(width)
324 | << m[2][1] << " " << std::setw(width) << m[2][2] << " "
325 | << std::setw(width) << m[2][3] << "\n"
326 | <<
327 |
328 | " " << std::setw(width) << m[3][0] << " " << std::setw(width)
329 | << m[3][1] << " " << std::setw(width) << m[3][2] << " "
330 | << std::setw(width) << m[3][3] << ")\n";
331 |
332 | s.flags(oldFlags);
333 | return s;
334 | }
335 | public:
336 | T x[4][4];
337 | };
338 |
339 | template
340 | void ExtractScaling(const Matrix44& mat, Vec3& scale) {
341 | Vec3 row[3];
342 |
343 | row[0] = Vec3(mat[0][0], mat[0][1], mat[0][2]);
344 | row[1] = Vec3(mat[1][0], mat[1][1], mat[1][2]);
345 | row[2] = Vec3(mat[2][0], mat[2][1], mat[2][2]);
346 |
347 | scale.x = row[0].Length();
348 | scale.y = row[1].Length();
349 | scale.z = row[2].Length();
350 | }
351 |
352 | template
353 | const Matrix44 Matrix44::kIdentity = Matrix44();
354 |
355 | #endif
--------------------------------------------------------------------------------
/introduction-to-lighting/pointlight.cc:
--------------------------------------------------------------------------------
1 | // (c) www.scratchapixel.com - 2024.
2 | // Distributed under the terms of the CC BY-NC-ND 4.0 License.
3 | // https://creativecommons.org/licenses/by-nc-nd/4.0/
4 | // clang++ -Wall -Wextra -std=c++23 -o light.exe light.cc -O3
5 |
6 | #define _USE_MATH_DEFINES
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | template
17 | class Vec3 {
18 | public:
19 | Vec3() : x(0), y(0), z(0) {}
20 | Vec3(T xx) : x(xx), y(xx), z(xx) {}
21 | Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
22 | Vec3 operator+(const Vec3& v) const noexcept {
23 | return {x + v.x, y + v.y, z + v.z};
24 | }
25 | Vec3 operator-(const Vec3& v) const noexcept {
26 | return {x - v.x, y - v.y, z - v.z};
27 | }
28 | Vec3 operator*(const T& real) const noexcept {
29 | return {x * real, y * real, z * real};
30 | }
31 | friend Vec3 operator*(const T& real, const Vec3& v) noexcept {
32 | return {real * v.x, real * v.y, real * v.z};
33 | }
34 | Vec3 operator/(const T& real) const noexcept {
35 | return {x / real, y / real, z / real};
36 | }
37 | Vec3 operator-() const noexcept {
38 | return {-x, -y, -z};
39 | }
40 | T Length() const {
41 | return std::sqrtf(x * x + y * y + z * z);
42 | }
43 | Vec3& Normalize() noexcept {
44 | T len = Length();
45 | if (len != 0) [[likely]] {
46 | x /= len;
47 | y /= len;
48 | z /= len;
49 | }
50 | return *this;
51 | }
52 | T Dot(const Vec3& v) const noexcept {
53 | return x * v.x + y * v.y + z * v.z;
54 | }
55 | Vec3 Cross(const Vec3& v) const noexcept {
56 | return {y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x};
57 | }
58 | friend std::ostream& operator<<(std::ostream& os, const Vec3& v) {
59 | return os << v.x << " " << v.y << " " << v.z;
60 | }
61 | T x, y, z;
62 | };
63 |
64 | struct Hit {
65 | float u;
66 | float v;
67 | int id0{-1};
68 | int id1{-1};
69 | float t{std::numeric_limits::max()};
70 | operator bool() const { return id0 != -1; }
71 | };
72 |
73 | struct DifferentialGeometry : Hit {
74 | Vec3 P;
75 | Vec3 Ng;
76 | Vec3 Ns;
77 | };
78 |
79 | struct Ray {
80 | Vec3 orig;
81 | Vec3 dir;
82 | float near{0.1};
83 | float far{std::numeric_limits::max()};
84 | };
85 |
86 | class TriangleMesh {
87 | public:
88 | struct Triangle {
89 | uint32_t v0, v1, v2;
90 | };
91 |
92 | void PostIntersect(const Ray& ray, DifferentialGeometry& dg) {
93 | const Triangle& tri = triangles_[dg.id1];
94 | Vec3 p0 = position_[tri.v0];
95 | Vec3 p1 = position_[tri.v1];
96 | Vec3 p2 = position_[tri.v2];
97 |
98 | float u = dg.u, v = dg.v, w = 1.f - u - v, t = dg.t;
99 |
100 | const Vec3 dPdu = p1 - p0, dPdv = p2 - p0;
101 | dg.P = ray.orig + t * ray.dir;
102 | dg.Ng = dPdv.Cross(dPdu).Normalize();
103 |
104 | if (normals_.size()) {
105 | const Vec3 n0 = normals_[tri.v0], n1 = normals_[tri.v1], n2 = normals_[tri.v2];
106 | Vec3