RayTracer 1.0
Ray tracing is a technique used to generate realistic digital images by simulating the inverse path of light. Our goal is to create a program able to generate an image from a file describing the scene.
 
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1/*
2** EPITECH PROJECT, 2025
3** B-OOP-400-MAR-4-1-raytracer-selim.bouasker
4** File description:
5** Main.cpp
6*/
7#include <thread>
8#include <cmath>
9#include <algorithm>
10#include <limits>
11#include "../include/Main.hpp"
13#include "../include/Utils.hpp"
20#include "../include/Sphere.hpp"
21#include "../include/Plane.hpp"
22#include "../include/Camera.hpp"
23#include "../include/Scene.hpp"
24
25
27{
28 std::cout << "USAGE: ./raytracer <SCENE_FILE>\n";
29 std::cout << " SCENE_FILE: scene configuration\n";
30}
31
32void Main::parseArguments(int argc, char **argv, std::string &file, bool &isDebug)
33{
34 if (argc == 2 && std::string(argv[1]) == "unitest")
35 std::exit(0);
36
37 if (argc == 2 && std::string(argv[1]) == "-help") {
38 printHelp();
39 std::exit(0);
40 }
41
42 if (argc == 3 && std::string(argv[2]) == "-d") {
43 file = argv[1];
44 isDebug = true;
45 return;
46 }
47
48 if (argc != 2) {
49 throw RayTracerException("USAGE: ./raytracer <SCENE_FILE>");
50 }
51
52 file = argv[1];
53 if (!is_valid_cfg(file)) {
54 throw RayTracerException("Error: SCENE_FILE must have .cfg extension");
55 }
56}
57
59{
60 std::cout << "CAMERA:\n";
61 std::cout << "resolution: " << cfg.camera.width << " × " << cfg.camera.height << std::endl;
62 std::cout << "position = (" << cfg.camera.position.x << ", " << cfg.camera.position.y << ", " << cfg.camera.position.z << ")\n";
63 std::cout << "rotation = (" << cfg.camera.rotation.x << ", " << cfg.camera.rotation.y << ", " << cfg.camera.rotation.z << ")\n";
64 std::cout << "fieldOfView = " << cfg.camera.fieldOfView << std::endl << std::endl;
65
66 std::cout << "SPHERES:\n";
67 for (const auto &s : cfg.spheres) {
68 std::cout << "centre = (" << s.center.x << ", " << s.center.y << ", " << s.center.z << ")"
69 << " rayon = " << s.radius
70 << " couleur = (" << s.color.r << ", " << s.color.g << ", " << s.color.b << ")\n";
71 }
72
73 std::cout << "PLANES:\n";
74 for (const auto &p : cfg.planes) {
75 std::cout << "axe = '" << p.axis << "'"
76 << " position = " << p.position
77 << " couleur = (" << p.color.r << ", " << p.color.g << ", " << p.color.b << ")\n";
78 }
79
80 std::cout << "LIGHTS:\n";
81 std::cout << "ambient = " << cfg.ambient
82 << " diffuse = " << cfg.diffuse << "\n";
83}
84
85void Main::calculPPM(const Config::Scene &cfg, Display &display)
86{
87 RayTracer::Scene scene;
88
89 float aspect = static_cast<float>(cfg.camera.width) / cfg.camera.height;
90 float scale = std::tan((cfg.camera.fieldOfView * 0.5f) * M_PI / 180.0f);
91 Math::Vector3D bs{2.0f * scale * aspect, 0.0f, 0.0f};
92 Math::Vector3D ls{0.0f, 2.0f * scale, 0.0f};
93 Math::Point3D origin{
94 cfg.camera.position.x - bs.x * 0.5f,
95 cfg.camera.position.y - ls.y * 0.5f,
96 cfg.camera.position.z - 1.0f
97 };
98
99 scene.setCamera(
101 cfg.camera.position,
102 RayTracer::Rectangle3D(origin, bs, ls)
103 )
104 );
105
106 scene.setAmbientLight(
107 std::make_unique<RayTracer::AmbientLight>(
108 static_cast<float>(cfg.ambient)
109 )
110 );
111
112 // Add lights
113 for (const auto &d : cfg.directionals) {
114 scene.addLight(
115 std::make_unique<RayTracer::DirectionalLight>(
116 d.direction,
117 static_cast<float>(cfg.diffuse)
118 )
119 );
120 }
121
122 // Add point lights
123 for (const auto &p : cfg.points) {
124 scene.addLight(
125 std::make_unique<RayTracer::PointLight>(
126 p.position,
127 static_cast<float>(cfg.diffuse)
128 )
129 );
130 }
131
132 for (const auto &s : cfg.spheres) {
133 scene.addObject(
134 std::make_shared<RayTracer::Sphere>(
135 s.center,
136 s.radius,
137 std::make_shared<RayTracer::FlatColor>(s.color)
138 )
139 );
140 }
141
142 for (const auto &p : cfg.planes) {
143 Math::Vector3D normal{0.0f, 0.0f, 0.0f};
144 if (p.axis == 'X') normal.x = 1.0f;
145 else if (p.axis == 'Y') normal.y = 1.0f;
146 else normal.z = 1.0f;
147
148 Math::Point3D pt{0.0f, 0.0f, 0.0f};
149 if (p.axis == 'X') pt.x = p.position;
150 else if (p.axis == 'Y') pt.y = p.position;
151 else pt.z = p.position;
152
153 scene.addObject(
154 std::make_shared<RayTracer::Plane>(
155 pt,
156 normal,
157 std::make_shared<RayTracer::FlatColor>(p.color)
158 )
159 );
160 }
161
162 int w = cfg.camera.width;
163 int h = cfg.camera.height;
164 // std::cout << "P3\n" << w << " " << h << "\n255\n";
165
166 for (int y = 0; y < h; ++y) {
167 for (int x = 0; x < w; ++x) {
168 double u = (x + 0.5) / w;
169 double v = (y + 0.5) / h;
170 RayTracer::Ray ray = scene.getCamera().ray(u, v);
171
172 Color finalColor(0, 0, 0);
173 double closest_t = std::numeric_limits<double>::max();
174 std::shared_ptr<RayTracer::IPrimitive> closestObject = nullptr;
175 Math::Point3D hitPoint;
176 Math::Vector3D normal;
177
178 for (const auto &obj : scene.getObjects()) {
179 double t;
180 Math::Point3D pt;
182 if (obj->intersect(ray, t, pt, n) && t < closest_t) {
183 closest_t = t;
184 closestObject = obj;
185 hitPoint = pt;
186 normal = n;
187 }
188 }
189
190 if (closestObject) {
191 // Start with ambient
192 if (scene.getAmbient()) {
193 finalColor = scene.getAmbient()->illuminate(ray, *closestObject, hitPoint);
194 }
195 // Lighting
196 for (const auto &light : scene.getLights()) {
197 if (auto *dirLight = dynamic_cast<RayTracer::DirectionalLight *>(light.get())) {
198 Math::Vector3D lightDir = dirLight->getDirection() * -1.0;
199 lightDir = lightDir / lightDir.length();
200
201 // Shadow ray
202 const double bias = 1e-4;
203 Math::Point3D shadowOrigin = hitPoint + normal * bias;
204 RayTracer::Ray shadowRay(shadowOrigin, lightDir);
205 bool shadowed = false;
206
207 for (const auto &obj : scene.getObjects()) {
208 if (obj != closestObject) {
209 double tTmp;
210 Math::Point3D tmpPt;
211 Math::Vector3D tmpN;
212 if (obj->intersect(shadowRay, tTmp, tmpPt, tmpN)) {
213 shadowed = true;
214 break;
215 }
216 }
217 }
218 if (!shadowed) {
219 double diff = std::max(0.0, normal.dot(lightDir));
220 Color lightColor = light->illuminate(ray, *closestObject, hitPoint);
221 finalColor.r = std::min(finalColor.r + static_cast<int>(lightColor.r * diff), 255);
222 finalColor.g = std::min(finalColor.g + static_cast<int>(lightColor.g * diff), 255);
223 finalColor.b = std::min(finalColor.b + static_cast<int>(lightColor.b * diff), 255);
224 }
225
226 } else if (auto *pLight = dynamic_cast<RayTracer::PointLight *>(light.get())) {
227 Math::Vector3D lightDir = pLight->getPosition() - hitPoint;
228 double dist = lightDir.length();
229 lightDir = lightDir / dist;
230
231 const double bias = 1e-4;
232 Math::Point3D shadowOrig = hitPoint + normal * bias;
233 RayTracer::Ray shadowRay(shadowOrig, lightDir);
234 bool inShadow = false;
235 for (const auto &obj : scene.getObjects()) {
236 if (obj != closestObject) {
237 double tTmp;
238 Math::Point3D tmpPt;
239 Math::Vector3D tmpN;
240 if (obj->intersect(shadowRay, tTmp, tmpPt, tmpN) && tTmp < dist) {
241 inShadow = true;
242 break;
243 }
244 }
245 }
246 if (!inShadow) {
247 double diff = std::max(0.0, normal.dot(lightDir));
248 Color c = light->illuminate(ray, *closestObject, hitPoint);
249 finalColor.r = std::min(finalColor.r + static_cast<int>(c.r * diff), 255);
250 finalColor.g = std::min(finalColor.g + static_cast<int>(c.g * diff), 255);
251 finalColor.b = std::min(finalColor.b + static_cast<int>(c.b * diff), 255);
252 }
253 }
254 }
255 }
256
257 display.pushPixel(x, y, Display::Pixel(finalColor.r, finalColor.g, finalColor.b));
258 // std::cout << pixel.toPPM() << "\n";
259 }
260 }
261 display.notifyDone();
262}
263
264int main(int argc, char **argv)
265{
266 try {
267 Main main;
268 std::string file;
269 bool isDebug = false;
270 main.parseArguments(argc, argv, file, isDebug);
271
272 auto cfg = Config::parseScene(file);
273
274 if (isDebug) {
275 main.debug_config(cfg);
276 return 0;
277 }
278
279 Display display(cfg.camera.width, cfg.camera.height);
280 display.init();
281 std::thread worker(&Main::calculPPM, &main, cfg, std::ref(display));
282 display.run();
283 if (worker.joinable())
284 worker.join();
285
286 } catch (const RayTracerException &e) {
287 std::cerr << e.what() << std::endl;
288 return 84;
289 }
290
291 return 0;
292}
bool is_valid_cfg(const std::string &f)
Definition Utils.cpp:10
int r
Definition Color.hpp:13
int g
Definition Color.hpp:13
int b
Definition Color.hpp:13
Math::Point3D position
Math::Vector3D rotation
std::vector< Plane > planes
std::vector< Point > points
std::vector< Sphere > spheres
std::vector< Directional > directionals
void run()
Definition Display.cpp:33
void notifyDone()
Definition Display.cpp:26
void init()
Definition Display.cpp:12
void pushPixel(int x, int y, const Pixel &px)
Definition Display.cpp:19
Definition Main.hpp:13
void parseArguments(int argc, char **argv, std::string &file, bool &isDebug)
Definition main.cpp:32
void printHelp()
Definition main.cpp:26
void debug_config(const Config::Scene &cfg)
Definition main.cpp:58
void calculPPM(const Config::Scene &cfg, Display &display)
Definition main.cpp:85
double length() const
Definition Math3D.cpp:15
double dot(const Vector3D &o) const
Definition Math3D.cpp:20
Ray ray(double u, double v) const
Definition Camera.cpp:26
void addObject(std::shared_ptr< IPrimitive > obj)
Definition Scene.cpp:12
const std::vector< std::unique_ptr< ILight > > & getLights() const
Definition Scene.cpp:32
void setCamera(const Camera &cam)
Definition Scene.cpp:42
const std::unique_ptr< ILight > & getAmbient() const
Definition Scene.cpp:37
void setAmbientLight(std::unique_ptr< ILight > light)
Definition Scene.cpp:22
const std::vector< std::shared_ptr< IPrimitive > > & getObjects() const
Definition Scene.cpp:27
void addLight(std::unique_ptr< ILight > light)
Definition Scene.cpp:17
const Camera & getCamera() const
Definition Scene.cpp:47
int main(int argc, char **argv)
Definition main.cpp:264
Scene parseScene(const std::string &path)