1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,8 @@ |
1 |
+[ |
|
2 |
+ { "include": ["@<glm/detail/.*>", "private", "<glm/glm.hpp>", "public"] }, |
|
3 |
+ { "include": ["@<glm/ext/(matrix|vector)_(float|u?int).*>", "public", "<glm/glm.hpp>", "public"] }, |
|
4 |
+ { "symbol": ["glm::lookAt", "public", "<glm/ext/matrix_transform.hpp>", "public"] }, |
|
5 |
+ { "symbol": ["glm::perspective", "public", "<glm/ext/matrix_clip_space.hpp>", "public"] }, |
|
6 |
+ { "symbol": ["glm::unProject", "public", "<glm/ext/matrix_projection.hpp>", "public"] }, |
|
7 |
+ { "symbol": ["glm::convertLinearToSRGB", "public", "<glm/gtc/color_space.hpp>", "public"] }, |
|
8 |
+] |
... | ... |
@@ -5,6 +5,8 @@ A [C++11][]/[GLM][] toy [raytracer][]. |
5 | 5 |
The primary goal is to provide a clear, modular base to try out different |
6 | 6 |
experiments on. |
7 | 7 |
|
8 |
+![output](doc/output.tga) |
|
9 |
+ |
|
8 | 10 |
[`raytracer`]: https://git.rcrnstn.net/rcrnstn/raytracer |
9 | 11 |
[C++11]: https://en.wikipedia.org/wiki/C++11 |
10 | 12 |
[GLM]: https://glm.g-truc.net |
13 | 15 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,203 @@ |
1 |
+/// Includes |
|
2 |
+ |
|
3 |
+#include <array> |
|
4 |
+#include <fstream> |
|
5 |
+#include <iostream> |
|
6 |
+#include <limits> |
|
7 |
+#include <string> |
|
8 |
+#include <vector> |
|
9 |
+ |
|
10 |
+#define GLM_FORCE_SWIZZLE |
|
11 |
+#include <glm/glm.hpp> |
|
12 |
+#include <glm/fwd.hpp> |
|
13 |
+#include <glm/ext/matrix_transform.hpp> // https://glm.g-truc.net/0.9.9/api/a00247.html |
|
14 |
+#include <glm/ext/matrix_clip_space.hpp> // https://glm.g-truc.net/0.9.9/api/a00243.html |
|
15 |
+#include <glm/ext/matrix_projection.hpp> // https://glm.g-truc.net/0.9.9/api/a00245.html |
|
16 |
+#include <glm/gtc/color_space.hpp> // https://glm.g-truc.net/0.9.9/api/a00289.html |
|
17 |
+// #include <glm/gtx/io.hpp> // https://glm.g-truc.net/0.9.9/api/a00332.html |
|
18 |
+ |
|
19 |
+ |
|
20 |
+/// Namespaces |
|
21 |
+ |
|
22 |
+using namespace glm; |
|
23 |
+ |
|
24 |
+ |
|
25 |
+/// Constants |
|
26 |
+ |
|
27 |
+auto const infinity = std::numeric_limits<float>::infinity(); |
|
28 |
+ |
|
29 |
+ |
|
30 |
+/// Types |
|
31 |
+ |
|
32 |
+//// Ray |
|
33 |
+struct Ray |
|
34 |
+{ |
|
35 |
+ vec3 origin; |
|
36 |
+ vec3 direction; |
|
37 |
+}; |
|
38 |
+ |
|
39 |
+//// Trace |
|
40 |
+struct Trace |
|
41 |
+{ |
|
42 |
+ float distance; |
|
43 |
+}; |
|
44 |
+ |
|
45 |
+//// Circle |
|
46 |
+struct Circle |
|
47 |
+{ |
|
48 |
+ vec3 center; |
|
49 |
+ float radius; |
|
50 |
+ Trace trace(Ray const & ray) const |
|
51 |
+ { |
|
52 |
+ auto const offs = center - ray.origin; |
|
53 |
+ auto const proj = dot(offs, ray.direction); |
|
54 |
+ if (proj < 0.0F) // Past. |
|
55 |
+ return {infinity}; |
|
56 |
+ auto const perp2 = dot(offs, offs) - proj * proj; |
|
57 |
+ auto const pene2 = radius * radius - perp2; |
|
58 |
+ if (pene2 < 0.0F) // Miss. |
|
59 |
+ return {infinity}; |
|
60 |
+ auto const dist = proj - sqrt(pene2); |
|
61 |
+ if (dist < 0.0F) // Inside. |
|
62 |
+ return {infinity}; |
|
63 |
+ return {dist}; |
|
64 |
+ } |
|
65 |
+}; |
|
66 |
+ |
|
67 |
+//// Shapes |
|
68 |
+using Shapes = std::vector<Circle>; |
|
69 |
+ |
|
70 |
+//// Scene |
|
71 |
+struct Scene |
|
72 |
+{ |
|
73 |
+ Shapes shapes; |
|
74 |
+ Trace trace(Ray const & ray) const |
|
75 |
+ { |
|
76 |
+ auto nearest = Trace{infinity}; |
|
77 |
+ for (auto const & shape : shapes) |
|
78 |
+ { |
|
79 |
+ auto const trace = shape.trace(ray); |
|
80 |
+ if (trace.distance < nearest.distance) |
|
81 |
+ nearest = trace; |
|
82 |
+ } |
|
83 |
+ return nearest; |
|
84 |
+ } |
|
85 |
+}; |
|
86 |
+ |
|
87 |
+//// Camera |
|
88 |
+struct Camera |
|
89 |
+{ |
|
90 |
+ vec3 position; |
|
91 |
+ vec3 target; |
|
92 |
+ float fovy; |
|
93 |
+ float near; |
|
94 |
+ float far; |
|
95 |
+ Ray ray(uvec2 const & size, uvec2 const & pixel) const |
|
96 |
+ { |
|
97 |
+ auto const aspect = (float)size.x / (float)size.y; |
|
98 |
+ auto const viewport = uvec4(0, 0, size); |
|
99 |
+ auto const window = vec3(vec2(pixel) + 0.5F, 0.0F); |
|
100 |
+ auto const up = vec3(0.0F, 1.0F, 0.0F); |
|
101 |
+ auto const view = glm::lookAt(position, target, up); |
|
102 |
+ auto const proj = glm::perspective(fovy, aspect, near, far); |
|
103 |
+ auto const world = glm::unProject(window, view, proj, viewport); |
|
104 |
+ auto const direction = normalize(world.xyz() - position); |
|
105 |
+ return Ray{position, direction}; |
|
106 |
+ } |
|
107 |
+}; |
|
108 |
+ |
|
109 |
+//// ACES |
|
110 |
+struct ACES |
|
111 |
+{ |
|
112 |
+ vec3 whitepoint; |
|
113 |
+ vec3 transform(vec3 color) const |
|
114 |
+ { |
|
115 |
+ color /= whitepoint; |
|
116 |
+ return |
|
117 |
+ (color * (2.51F * color + 0.03F)) / |
|
118 |
+ (color * (2.43F * color + 0.59F) + 0.14F); |
|
119 |
+ } |
|
120 |
+}; |
|
121 |
+ |
|
122 |
+//// TGA |
|
123 |
+struct TGA |
|
124 |
+{ |
|
125 |
+ uvec2 size; |
|
126 |
+ std::string path; |
|
127 |
+ template<typename ColorFunc> |
|
128 |
+ void render(ColorFunc const & color_func) const |
|
129 |
+ { |
|
130 |
+ auto frame_size = size.x * size.y; |
|
131 |
+ auto frame = std::vector<u8vec4>(frame_size); |
|
132 |
+ auto header = std::array<u8, 18>{ |
|
133 |
+ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
134 |
+ (u8)(size.x >> 0U), |
|
135 |
+ (u8)(size.x >> 8U), |
|
136 |
+ (u8)(size.y >> 0U), |
|
137 |
+ (u8)(size.y >> 8U), |
|
138 |
+ 32, 0, |
|
139 |
+ }; |
|
140 |
+ for (auto y = 0U; y < size.y; ++y) |
|
141 |
+ { |
|
142 |
+ for (auto x = 0U; x < size.x; ++x) |
|
143 |
+ { |
|
144 |
+ auto const color = color_func(size, uvec2(x, y)); |
|
145 |
+ auto const index = y * size.x + x; |
|
146 |
+ frame[index] = clamp(color.bgra(), 0.0F, 1.0F) * 255.0F + 0.5F; |
|
147 |
+ } |
|
148 |
+ } |
|
149 |
+ auto ostream = std::ofstream(path, std::ios::binary); |
|
150 |
+ write(ostream, header); |
|
151 |
+ write(ostream, frame); |
|
152 |
+ } |
|
153 |
+ template<typename Collection> |
|
154 |
+ void write(std::ostream & ostream, Collection const & collection) const |
|
155 |
+ { |
|
156 |
+ ostream.write( |
|
157 |
+ (char const *)collection.data(), |
|
158 |
+ (std::streamsize)(sizeof(*collection.data()) * collection.size()) |
|
159 |
+ ); |
|
160 |
+ } |
|
161 |
+}; |
|
162 |
+ |
|
163 |
+ |
|
164 |
+/// Main |
|
165 |
+ |
|
166 |
+int main(int argc, char const * argv[]) |
|
167 |
+{ |
|
168 |
+ //// Arguments |
|
169 |
+ if (argc != 2) |
|
170 |
+ { |
|
171 |
+ std::cerr << "Usage: raytrace <path>" << std::endl; |
|
172 |
+ return 0; |
|
173 |
+ } |
|
174 |
+ auto const * path = argv[1]; // NOLINT |
|
175 |
+ //// Configure |
|
176 |
+ auto const camera = Camera{ |
|
177 |
+ vec3(0.0F, 0.0F, 0.0F), // position |
|
178 |
+ vec3(0.0F, 0.0F, -1.0F), // target |
|
179 |
+ radians(45.0F), // fovy |
|
180 |
+ 0.01F, // near |
|
181 |
+ 100.0F, // far |
|
182 |
+ }; |
|
183 |
+ auto const scene = Scene{ |
|
184 |
+ // shapes |
|
185 |
+ { |
|
186 |
+ {vec3( 2.0F, -1.0F, -10.0F), 3.0F}, |
|
187 |
+ {vec3( 1.0F, 2.0F, -7.0F), 2.0F}, |
|
188 |
+ {vec3(-1.0F, 0.0F, -4.0F), 1.0F}, |
|
189 |
+ {vec3( 0.0F, -1.0F, -3.0F), 0.2F}, |
|
190 |
+ }, |
|
191 |
+ }; |
|
192 |
+ auto const tonemap = ACES{vec3(1.0F)}; |
|
193 |
+ auto const output = TGA{uvec2(640, 480), path}; |
|
194 |
+ //// Render |
|
195 |
+ output.render([&](uvec2 const & size, uvec2 const & pixel) { |
|
196 |
+ auto const ray = camera.ray(size, pixel); |
|
197 |
+ auto const trace = scene.trace(ray); |
|
198 |
+ auto const rgb = vec3(min(trace.distance / 100.0F, 100.0F)); |
|
199 |
+ auto const srgb = convertLinearToSRGB(tonemap.transform(rgb)); |
|
200 |
+ auto const alpha = 1.0F; |
|
201 |
+ return vec4(srgb, alpha); |
|
202 |
+ }); |
|
203 |
+} |