1
- #include < cstdio>
2
- #include < cstdlib>
3
- #include < memory>
4
- #include < vector>
5
- #include < utility>
6
- #include < cstdint>
7
1
#include < iostream>
8
2
#include < fstream>
9
- #include < cmath>
10
- #include < limits>
11
- #include < random>
12
3
13
- #include < Core/geometry.hpp>
14
-
15
- const float kInfinity = std::numeric_limits<float >::max();
16
- std::random_device rd;
17
- std::mt19937 gen (rd());
18
- std::uniform_real_distribution<> dis (0 , 1 );
19
-
20
- inline
21
- float clamp (const float &lo, const float &hi, const float &v)
22
- { return std::max (lo, std::min (hi, v)); }
23
-
24
- inline
25
- float deg2rad (const float °)
26
- { return deg * M_PI / 180 ; }
27
-
28
- inline
29
- Vec3f mix (const Vec3f &a, const Vec3f& b, const float &mixValue)
30
- { return a * (1 - mixValue) + b * mixValue; }
31
-
32
- struct Options
33
- {
34
- uint32_t width;
35
- uint32_t height;
36
- float fov;
37
- Matrix44f cameraToWorld;
38
- };
39
-
40
- // [comment]
41
- // Object base class
42
- // [/comment]
43
- class Object
44
- {
45
- public:
46
- Object () : color(dis(gen), dis(gen), dis(gen)) {}
47
- virtual ~Object () {}
48
- // Method to compute the intersection of the object with a ray
49
- // Returns true if an intersection was found, false otherwise
50
- // See method implementation in children class for details
51
- virtual bool intersect (const Vec3f &, const Vec3f &, float &) const = 0;
52
- // Method to compute the surface data such as normal and texture coordnates at the intersection point.
53
- // See method implementation in children class for details
54
- virtual void getSurfaceData (const Vec3f &, Vec3f &, Vec2f &) const = 0;
55
- Vec3f color;
56
- };
57
-
58
- // [comment]
59
- // Compute the roots of a quadratic equation
60
- // [/comment]
61
- bool solveQuadratic (const float &a, const float &b, const float &c, float &x0, float &x1)
62
- {
63
- float discr = b * b - 4 * a * c;
64
- if (discr < 0 ) return false ;
65
- else if (discr == 0 ) {
66
- x0 = x1 = - 0.5 * b / a;
67
- }
68
- else {
69
- float q = (b > 0 ) ?
70
- -0.5 * (b + sqrt (discr)) :
71
- -0.5 * (b - sqrt (discr));
72
- x0 = q / a;
73
- x1 = c / q;
74
- }
75
-
76
- return true ;
77
- }
78
-
79
- // [comment]
80
- // Sphere class. A sphere type object
81
- // [/comment]
82
- class Sphere : public Object
83
- {
84
- public:
85
- Sphere (const Vec3f &c, const float &r) : radius(r), radius2(r *r ), center(c) {}
86
- // [comment]
87
- // Ray-sphere intersection test
88
- //
89
- // \param orig is the ray origin
90
- //
91
- // \param dir is the ray direction
92
- //
93
- // \param[out] is the distance from the ray origin to the intersection point
94
- //
95
- // [/comment]
96
- bool intersect (const Vec3f &orig, const Vec3f &dir, float &t) const
97
- {
98
- float t0, t1; // solutions for t if the ray intersects
99
- #if 0
100
- // geometric solution
101
- Vec3f L = center - orig;
102
- float tca = L.dotProduct(dir);
103
- if (tca < 0) return false;
104
- float d2 = L.dotProduct(L) - tca * tca;
105
- if (d2 > radius2) return false;
106
- float thc = sqrt(radius2 - d2);
107
- t0 = tca - thc;
108
- t1 = tca + thc;
109
- #else
110
- // analytic solution
111
- Vec3f L = orig - center;
112
- float a = dir.dotProduct (dir);
113
- float b = 2 * dir.dotProduct (L);
114
- float c = L.dotProduct (L) - radius2;
115
- if (!solveQuadratic (a, b, c, t0, t1)) return false ;
116
- #endif
117
- if (t0 > t1) std::swap (t0, t1);
118
-
119
- if (t0 < 0 ) {
120
- t0 = t1; // if t0 is negative, let's use t1 instead
121
- if (t0 < 0 ) return false ; // both t0 and t1 are negative
122
- }
123
-
124
- t = t0;
125
-
126
- return true ;
127
- }
128
- // [comment]
129
- // Set surface data such as normal and texture coordinates at a given point on the surface
130
- //
131
- // \param Phit is the point ont the surface we want to get data on
132
- //
133
- // \param[out] Nhit is the normal at Phit
134
- //
135
- // \param[out] tex are the texture coordinates at Phit
136
- //
137
- // [/comment]
138
- void getSurfaceData (const Vec3f &Phit, Vec3f &Nhit, Vec2f &tex) const
139
- {
140
- Nhit = Phit - center;
141
- Nhit.normalize ();
142
- // In this particular case, the normal is simular to a point on a unit sphere
143
- // centred around the origin. We can thus use the normal coordinates to compute
144
- // the spherical coordinates of Phit.
145
- // atan2 returns a value in the range [-pi, pi] and we need to remap it to range [0, 1]
146
- // acosf returns a value in the range [0, pi] and we also need to remap it to the range [0, 1]
147
- tex.x = (1 + atan2 (Nhit.z , Nhit.x ) / M_PI) * 0.5 ;
148
- tex.y = acosf (Nhit.y ) / M_PI;
149
- }
150
- float radius, radius2;
151
- Vec3f center;
152
- };
153
-
154
- // [comment]
155
- // Returns true if the ray intersects an object. The variable tNear is set to the closest intersection distance and hitObject
156
- // is a pointer to the intersected object. The variable tNear is set to infinity and hitObject is set null if no intersection
157
- // was found.
158
- // [/comment]
159
- bool trace (const Vec3f &orig, const Vec3f &dir, const std::vector<std::unique_ptr<Object>> &objects, float &tNear, const Object *&hitObject)
160
- {
161
- tNear = kInfinity ;
162
- std::vector<std::unique_ptr<Object>>::const_iterator iter = objects.begin ();
163
- for (; iter != objects.end (); ++iter) {
164
- float t = kInfinity ;
165
- if ((*iter)->intersect (orig, dir, t) && t < tNear) {
166
- hitObject = iter->get ();
167
- tNear = t;
168
- }
169
- }
170
-
171
- return (hitObject != nullptr );
172
- }
173
-
174
- // [comment]
175
- // Compute the color at the intersection point if any (returns background color otherwise)
176
- // [/comment]
177
- Vec3f castRay (
178
- const Vec3f &orig, const Vec3f &dir,
179
- const std::vector<std::unique_ptr<Object>> &objects)
180
- {
181
- Vec3f hitColor = 0 ;
182
- const Object *hitObject = nullptr ; // this is a pointer to the hit object
183
- float t; // this is the intersection distance from the ray origin to the hit point
184
- if (trace (orig, dir, objects, t, hitObject)) {
185
- Vec3f Phit = orig + dir * t;
186
- Vec3f Nhit;
187
- Vec2f tex;
188
- hitObject->getSurfaceData (Phit, Nhit, tex);
189
- // Use the normal and texture coordinates to shade the hit point.
190
- // The normal is used to compute a simple facing ratio and the texture coordinate
191
- // to compute a basic checker board pattern
192
- float scale = 4 ;
193
- float pattern = (fmodf (tex.x * scale, 1 ) > 0.5 ) ^ (fmodf (tex.y * scale, 1 ) > 0.5 );
194
- hitColor = std::max (0 .f , Nhit.dotProduct (-dir)) * mix (hitObject->color , hitObject->color * 0.8 , pattern);
4
+ #include < Core/Camera.hpp>
5
+ #include < Core/Constants.hpp>
6
+ #include < Core/Parser.hpp>
7
+ #include < Core/Renderer.hpp>
8
+ #include < Core/Scene.hpp>
9
+ #include < Lights/AmbientLight.hpp>
10
+ #include < Lights/AreaLight.hpp>
11
+ #include < Shapes/Sphere.hpp>
12
+
13
+ void Scene1 (int width, int height, float fov, int samples);
14
+
15
+ int main (int argc, char * argv[]) {
16
+ if (Parser::hasOption (argv, argv + argc, " --help" )) {
17
+ std::cout << " Usage: " << argv[0 ];
18
+ std::cout << " [-s samples=" << DEFAULT_SAMPLES << " ]" ;
19
+ std::cout << " [-w width=" << DEFAULT_WIDTH << " ]" ;
20
+ std::cout << " [-h height=" << DEFAULT_HEIGHT << " ]" ;
21
+ std::cout << " [-f fov=" << DEFAULT_FOV << " ]" ;
22
+ std::cout << " [--help]" << std::endl;
23
+
24
+ return EXIT_SUCCESS;
195
25
}
196
26
197
- return hitColor;
198
- }
199
-
200
- // [comment]
201
- // The main render function. This where we iterate over all pixels in the image, generate
202
- // primary rays and cast these rays into the scene. The content of the framebuffer is
203
- // saved to a file.
204
- // [/comment]
205
- void render (
206
- const Options &options,
207
- const std::vector<std::unique_ptr<Object>> &objects)
208
- {
209
- Vec3f *framebuffer = new Vec3f[options.width * options.height ];
210
- Vec3f *pix = framebuffer;
211
- float scale = tan (deg2rad (options.fov * 0.5 ));
212
- float imageAspectRatio = options.width / (float )options.height ;
213
- // [comment]
214
- // Don't forget to transform the ray origin (which is also the camera origin
215
- // by transforming the point with coordinates (0,0,0) to world-space using the
216
- // camera-to-world matrix.
217
- // [/comment]
218
- Vec3f orig;
219
- options.cameraToWorld .multVecMatrix (Vec3f (0 ), orig);
220
- for (uint32_t j = 0 ; j < options.height ; ++j) {
221
- for (uint32_t i = 0 ; i < options.width ; ++i) {
222
- // [comment]
223
- // Generate primary ray direction. Compute the x and y position
224
- // of the ray in screen space. This gives a point on the image plane
225
- // at z=1. From there, we simply compute the direction by normalized
226
- // the resulting vec3f variable. This is similar to taking the vector
227
- // between the point on the image plane and the camera origin, which
228
- // in camera space is (0,0,0):
229
- //
230
- // ray.dir = normalize(Vec3f(x,y,-1) - Vec3f(0));
231
- // [/comment]
232
- #ifdef MAYA_STYLE
233
- float x = (2 * (i + 0.5 ) / (float )options.width - 1 ) * scale;
234
- float y = (1 - 2 * (j + 0.5 ) / (float )options.height ) * scale * 1 / imageAspectRatio;
235
- #else
236
-
237
- float x = (2 * (i + 0.5 ) / (float )options.width - 1 ) * imageAspectRatio * scale;
238
- float y = (1 - 2 * (j + 0.5 ) / (float )options.height ) * scale;
239
- #endif
240
- // [comment]
241
- // Don't forget to transform the ray direction using the camera-to-world matrix.
242
- // [/comment]
243
- Vec3f dir;
244
- options.cameraToWorld .multDirMatrix (Vec3f (x, y, -1 ), dir);
245
- dir.normalize ();
246
- *(pix++) = castRay (orig, dir, objects);
247
- }
248
- }
27
+ char * s = Parser::getOption (argv, argv + argc, " -s" );
28
+ char * w = Parser::getOption (argv, argv + argc, " -w" );
29
+ char * h = Parser::getOption (argv, argv + argc, " -h" );
30
+ char * f = Parser::getOption (argv, argv + argc, " -f" );
249
31
250
- // Save result to a PPM image (keep these flags if you compile under Windows)
251
- std::ofstream ofs (" ./out.ppm" , std::ios::out | std::ios::binary);
252
- ofs << " P6\n " << options.width << " " << options.height << " \n 255\n " ;
253
- for (uint32_t i = 0 ; i < options.height * options.width ; ++i) {
254
- char r = (char )(255 * clamp (0 , 1 , framebuffer[i].x ));
255
- char g = (char )(255 * clamp (0 , 1 , framebuffer[i].y ));
256
- char b = (char )(255 * clamp (0 , 1 , framebuffer[i].z ));
257
- ofs << r << g << b;
258
- }
32
+ int samples = (s) ? std::atoi (s) : DEFAULT_SAMPLES;
33
+ int width = (w) ? std::atoi (w) : DEFAULT_WIDTH;
34
+ int height = (h) ? std::atoi (h) : DEFAULT_HEIGHT;
35
+ float fov = (f) ? (float )(std::atoi (f)) : DEFAULT_FOV;
259
36
260
- ofs. close ( );
37
+ Scene1 (width, height, fov, samples );
261
38
262
- delete [] framebuffer ;
39
+ return EXIT_SUCCESS ;
263
40
}
264
41
265
- // [comment]
266
- // In the main function of the program, we create the scene (create objects)
267
- // as well as set the options for the render (image widht and height etc.).
268
- // We then call the render function().
269
- // [/comment]
270
- int main (int argc, char **argv)
271
- {
272
- // creating the scene (adding objects and lights)
273
- std::vector<std::unique_ptr<Object>> objects;
42
+ auto updateProgress = [](float progress) {
43
+ int progressValue = (int )(progress * 100 );
274
44
275
- // generate a scene made of random spheres
276
- uint32_t numSpheres = 32 ;
277
- gen.seed (0 );
278
- for (uint32_t i = 0 ; i < numSpheres; ++i) {
279
- Vec3f randPos ((0.5 - dis (gen)) * 10 , (0.5 - dis (gen)) * 10 , (0.5 + dis (gen) * 10 ));
280
- float randRadius = (0.5 + dis (gen) * 0.5 );
281
- objects.push_back (std::unique_ptr<Object>(new Sphere (randPos, randRadius)));
45
+ std::cout << " |" ;
46
+ for (int i = 0 ; i < 100 ; i += 2 ) {
47
+ std::cout << (i < progressValue ? " =" : " -" );
282
48
}
49
+ std::cout << " | " << progressValue << " %\r " ;
283
50
284
- // setting up options
285
- Options options;
286
- options.width = 640 ;
287
- options.height = 480 ;
288
- options.fov = 51.52 ;
289
- options.cameraToWorld = Matrix44f (0.945519 , 0 , -0.325569 , 0 , -0.179534 , 0.834209 , -0.521403 , 0 , 0.271593 , 0.551447 , 0.78876 , 0 , 4.208271 , 8.374532 , 17.932925 , 1 );
290
-
291
- // finally, render
292
- render (options, objects);
51
+ std::cout.flush ();
52
+ };
293
53
294
- return 0 ;
54
+ void Scene1 (int width, int height, float fov, int samples) {
55
+ std::cout << " Generating scene..." << std::endl;
56
+ std::clock_t t = std::clock ();
57
+
58
+ Scene scene = Scene ();
59
+
60
+ Sphere s0 = Sphere ( Vector3 (0 , -10008 , 20 ), 10000 , Color (20 , 120 , 100 ), 0.2 , 0.5 , 0.0 , 128.0 , 0.0 );
61
+ Sphere s1 = Sphere ( Vector3 (0 , 0 , 20 ), 4 , Color (165 , 10 , 14 ), 0.3 , 0.8 , 0.5 , 128.0 , 0.4 );
62
+ Sphere s2 = Sphere ( Vector3 (5 , -1 , 15 ), 2 , Color (235 , 179 , 41 ), 0.4 , 0.6 , 0.4 , 128.0 , 0.4 );
63
+ Sphere s3 = Sphere ( Vector3 (5 , 0 , 25 ), 3 , Color (6 , 72 , 111 ), 0.3 , 0.8 , 0.1 , 128.0 , 0.4 );
64
+ s3.setGlossiness (0.1 );
65
+ Sphere s4 = Sphere ( Vector3 (-3.5 , -1 , 10 ), 2 , Color (8 , 88 , 56 ), 0.4 , 0.6 , 0.5 , 64.0 , 0.4 );
66
+ Sphere s5 = Sphere ( Vector3 (-5.5 , 0 , 15 ), 3 , Color (51 , 51 , 51 ), 0.3 , 0.8 , 0.25 , 32.0 , 0.0 );
67
+
68
+ scene.addObject ( &s0 );
69
+ scene.addObject ( &s1 );
70
+ scene.addObject ( &s2 );
71
+ scene.addObject ( &s3 );
72
+ scene.addObject ( &s4 );
73
+ scene.addObject ( &s5 );
74
+
75
+ // Add light to scene
76
+ scene.addAmbientLight ( AmbientLight ( Vector3 (1.0 ) ) );
77
+ AreaLight l0 = AreaLight ( Vector3 (0 , 20 , 35 ), Vector3 (1.4 ) );
78
+ AreaLight l1 = AreaLight ( Vector3 (20 , 20 , 35 ), Vector3 (1.8 ) );
79
+ scene.addLight ( &l0 );
80
+ scene.addLight ( &l1 );
81
+
82
+ // Add camera
83
+ Camera camera = Camera ( Vector3 (0 ,0 ,-20 ), width, height, fov);
84
+ camera.setPosition (Vector3 (0 , 20 , -20 ));
85
+ camera.setAngleX (30 * M_PI / 180.0 );
86
+
87
+ // Create Renderer
88
+ Renderer r = Renderer (width, height, scene, camera);
89
+ r.render (samples, updateProgress);
90
+
91
+ t = std::clock () - t;
92
+ std::cout << std::endl << " Scene complete." << std::endl << std::endl;
93
+ std::cout << " Time ellpased: " << ((float )t) / CLOCKS_PER_SEC << " seconds." << std::endl;
295
94
}
0 commit comments