Euphoria
raytracer.cc
Go to the documentation of this file.
1 #include "core/raytracer.h"
2 
3 #include "core/image.h"
4 
5 #include "core/intersection.h"
6 #include "core/sphere.h"
7 #include "base/random.h"
8 #include "base/polarcoord.h"
9 #include "base/numeric.h"
10 #include "base/angle.h"
11 
12 #include <limits>
13 #include <cmath>
14 
16 {
18  (
19  float aray_distance,
20  const vec3f& aposition,
21  const unit3f& anormal,
22  std::shared_ptr<raytracer::Material> amaterial
23  )
24  : ray_distance(aray_distance)
25  , position(aposition)
26  , normal(anormal)
27  , material(amaterial)
28  {
29  }
30 
31 
33  {
36  std::shared_ptr<raytracer::Material> material;
37 
39  (
40  const core::Sphere& asphere,
41  const vec3f& aposition,
42  std::shared_ptr<raytracer::Material> amaterial
43  )
44  : sphere(asphere)
45  , position(aposition)
46  , material(amaterial)
47  {
48  }
49 
50  [[nodiscard]] std::optional<Collision>
51  get_collision(const UnitRay3f& ray, const Range<float>& range) const override
52  {
53  const auto hit_index = get_intersection
54  (
55  ray,
56  sphere,
57  position
58  );
59  if(is_within(range, hit_index))
60  {
61  const auto hit_position = ray.get_point(hit_index);
62  const auto hit_normal = vec3f::from_to(position, hit_position).get_normalized();
63  return Collision
64  {
65  hit_index,
66  hit_position,
67  hit_normal,
68  material
69  };
70  }
71  else
72  {
73  return std::nullopt;
74  }
75  }
76  };
77 
78 
79  std::shared_ptr<Object>
81  (
82  const Sphere& sphere,
83  const vec3f& position,
84  std::shared_ptr<raytracer::Material> material
85  )
86  {
87  return std::make_shared<SphereObject>
88  (
89  sphere,
90  position,
91  material
92  );
93  }
94 
95 
96  std::optional<Collision>
97  Scene::get_collision(const UnitRay3f& ray, const Range<float>& range) const
98  {
99  std::optional<Collision> r = std::nullopt;
100 
101  for(const auto& o: objects)
102  {
103  const auto h = o->get_collision(ray, range);
104  if(r.has_value() == false)
105  {
106  r = h;
107  }
108  else
109  {
110  if(h.has_value())
111  {
112  if(h->ray_distance < r->ray_distance)
113  {
114  r = h;
115  }
116  }
117  }
118  }
119 
120  return r;
121  }
122 
123 
124  Rgb
125  to_rgb(const unit3f& normal)
126  {
127  return Rgb
128  {
129  (normal.x + 1)/2,
130  (normal.y + 1)/2,
131  (normal.z + 1)/2
132  };
133  }
134 
135 
137  {
138  return get_random_unit3(random) * random->get_next_float01();
139  }
140 
141 
142  struct DiffuseMaterial : public Material
143  {
145 
146  explicit DiffuseMaterial(const Rgb& aalbedo)
147  : albedo(aalbedo)
148  {
149  }
150 
151  std::optional<ScatterResult>
153  (
154  const UnitRay3f& /*ray*/,
155  const Collision& hit,
156  Random* random
157  ) override
158  {
159  const auto target = hit.position + hit.normal + get_random_vec3_in_unit_sphere(random);
160  const auto reflected_ray = UnitRay3f::from_to(hit.position, target);
161 
162  return ScatterResult
163  {
164  albedo,
165  reflected_ray
166  };
167  }
168  };
169 
170 
171  vec3f get_reflected(const vec3f& v, const unit3f& normal) {
172  return v - 2 * v.dot(normal) * normal;
173  }
174 
175 
177  {
179  float fuzz;
180 
181  explicit MetalMaterial
182  (
183  const Rgb& aalbedo,
184  float afuzz
185  )
186  : albedo(aalbedo)
187  , fuzz(keep_within(r01, afuzz))
188  {
189  }
190 
191  std::optional<ScatterResult>
193  (
194  const UnitRay3f& ray,
195  const Collision& hit,
196  Random* random
197  ) override
198  {
199  const auto reflected = get_reflected
200  (
201  ray.dir,
202  hit.normal
203  );
204  const auto scattered = UnitRay3f::from_to
205  (
206  hit.position,
208  );
209  const auto scatter_dot = scattered.dir.dot(hit.normal);
210  if(scatter_dot > 0)
211  {
212  return ScatterResult
213  {
214  albedo,
215  scattered
216  };
217  }
218  else
219  {
220  return std::nullopt;
221  }
222  }
223  };
224 
225 
226  std::optional<vec3f>
228  (
229  const unit3f& uv,
230  const unit3f& normal,
231  float ni
232  )
233  {
234  const auto dt = uv.dot(normal);
235  const auto discriminant = 1.0f - ni*ni*(1.0f-dt*dt);
236  if (discriminant > 0)
237  {
238  return ni * (uv - normal * dt) - normal * sqrt(discriminant);
239  }
240  else
241  {
242  return std::nullopt;
243  }
244  }
245 
246 
247  float
248  calc_fresnel_factor(float cosine, float ref_idx)
249  {
250  const float r0 = square
251  (
252  (1-ref_idx) / (1+ref_idx)
253  );
254  return r0 + (1-r0)*std::pow((1.0f - cosine), 5.0f);
255  }
256 
257 
259  {
262 
264  (
265  const Rgb& aalbedo,
266  float arefractive_index
267  )
268  : albedo(aalbedo)
269  , refractive_index(arefractive_index)
270  {
271  }
272 
273  std::optional<ScatterResult>
275  (
276  const UnitRay3f& ray,
277  const Collision& hit,
278  Random* random
279  ) override
280  {
281  const auto dr = ray.dir.dot(hit.normal);
282  const auto dot_result = dr > 0.0f;
283 
284  const auto outward_normal = dot_result ? -hit.normal : hit.normal;
285  const auto ni = dot_result ? refractive_index : 1.0f/refractive_index;
286 
287  const auto refracted = get_refracted(ray.dir, outward_normal, ni);
288 
289  if(refracted.has_value())
290  {
291  const auto cosine = dot_result
292  ? refractive_index * dr
293  : -dr
294  ;
295  const auto reflection_probability = calc_fresnel_factor(cosine, refractive_index);
296  if( random->get_next_float01() >= reflection_probability )
297  {
298  return ScatterResult
299  {
300  albedo,
302  (
303  hit.position,
304  refracted.value()
305  )
306  };
307  }
308  }
309  const auto reflected = get_reflected
310  (
311  ray.dir,
312  hit.normal
313  );
314  return ScatterResult
315  {
316  albedo,
318  (
319  hit.position,
320  reflected
321  )
322  };
323  }
324  };
325 
326 
327  std::shared_ptr<Material>
329  (
330  const Rgb& albedo
331  )
332  {
333  return std::make_shared<DiffuseMaterial>
334  (
335  albedo
336  );
337  }
338 
339 
340  std::shared_ptr<Material>
342  (
343  const Rgb& albedo,
344  float fuzz
345  )
346  {
347  return std::make_shared<MetalMaterial>
348  (
349  albedo,
350  fuzz
351  );
352  }
353 
354 
355  std::shared_ptr<Material>
357  (
358  const Rgb& albedo,
359  float refractive_index
360  )
361  {
362  return std::make_shared<DielectricMaterial>
363  (
364  albedo,
365  refractive_index
366  );
367  }
368 
369 
370  Rgb
372  (
373  const Scene& scene,
374  const UnitRay3f& ray,
375  Random* random,
376  int depth
377  )
378  {
379  const auto h = scene.get_collision
380  (
381  ray,
383  );
384  if(h.has_value())
385  {
386  if(depth < 50)
387  {
388  auto scatter = h->material->get_scattered
389  (
390  ray,
391  h.value(),
392  random
393  );
394  if(scatter.has_value())
395  {
396  const auto c = get_color
397  (
398  scene,
399  scatter->scattered,
400  random,
401  depth + 1
402  );
403  return scatter->attenuation * c;
404  }
405  else
406  {
407  return NamedColor::black;
408  }
409  }
410  else
411  {
412  return NamedColor::black;
413  }
414  }
415  const auto t = (ray.dir.y+1)/2.0f;
416  return lerp_rgb
417  (
418  Rgb(1.0f, 1.0f, 1.0f),
419  t,
420  Rgb(0.5f, 0.7f, 1.0f)
421  );
422  }
423 
424 
425  struct Camera
426  {
431 
432  static Camera create(const Angle& vfov, float aspect)
433  {
434  const auto half_height = tan(vfov/2.0f);
435  const auto half_width = aspect * half_height;
436 
437  const auto lower_left_corner = vec3f{-half_width, -half_height, -1.0};
438  const auto horizontal = vec3f{2*half_width, 0.0f, 0.0f};
439  const auto vertical = vec3f{0.0f, 2*half_height, 0.0f};
440  const auto origin = vec3f{0.0f, 0.0f, 0.0f};
441 
442  return Camera
443  {
445  horizontal,
446  vertical,
447  origin
448  };
449  }
450 
451  [[nodiscard]] UnitRay3f
452  get_ray(float u, float v) const
453  {
455  }
456  };
457 
458 
459  Rgb
461  {
462  return {sqrt(color.r), sqrt(color.g), sqrt(color.b)};
463  }
464 
465  void
466  raytrace(Image* aimage, const raytracer::Scene& scene, int number_of_samples)
467  {
468  Image& img = *aimage;
469 
470  auto rand = Random{};
471  const auto aspect_ratio = static_cast<float>(img.width) / static_cast<float>(img.height);
472  const auto camera = Camera::create(Angle::from_degrees(90), aspect_ratio);
473 
474  std::cout << "Rendering ";
475  for(int y=0; y<img.height; y+=1)
476  {
477  for(int x=0; x<img.width; x+=1)
478  {
479  Rgb color = NamedColor::black;
480  for(int sample = 0; sample < number_of_samples; sample += 1)
481  {
482  const auto u = (static_cast<float>(x) + rand.get_next_float01()) / static_cast<float>(img.width);
483  const auto v = (static_cast<float>(y) + rand.get_next_float01()) / static_cast<float>(img.height);
484  const auto ray = camera.get_ray(u, v);
485  const auto sample_color = get_color(scene, ray, &rand, 0);
486  color += sample_color;
487  }
488  color = color/static_cast<float>(number_of_samples);
489  color = correct_color_using_gamma2(color);
490  img.set_pixel(x,y, to_rgbi(color));
491  }
492  }
493 
494  std::cout << "Rendering done :)\n";
495  }
496 }
497 
Rgb to_rgb(const unit3f &normal)
Definition: raytracer.cc:125
std::shared_ptr< Material > create_metal_material(const Rgb &albedo, float fuzz)
Definition: raytracer.cc:342
std::shared_ptr< Object > create_sphere(const Sphere &sphere, const vec3f &position, std::shared_ptr< raytracer::Material > material)
Definition: raytracer.cc:81
float calc_fresnel_factor(float cosine, float ref_idx)
Definition: raytracer.cc:248
void raytrace(Image *aimage, const raytracer::Scene &scene, int number_of_samples)
Definition: raytracer.cc:466
Rgb correct_color_using_gamma2(const Rgb &color)
Definition: raytracer.cc:460
std::shared_ptr< Material > create_dielectric_material(const Rgb &albedo, float refractive_index)
Definition: raytracer.cc:357
std::shared_ptr< Material > create_diffuse_material(const Rgb &albedo)
Definition: raytracer.cc:329
vec3f get_random_vec3_in_unit_sphere(Random *random)
Definition: raytracer.cc:136
vec3f get_reflected(const vec3f &v, const unit3f &normal)
Definition: raytracer.cc:171
std::optional< vec3f > get_refracted(const unit3f &uv, const unit3f &normal, float ni)
Definition: raytracer.cc:228
Rgb get_color(const Scene &scene, const UnitRay3f &ray, Random *random, int depth)
Definition: raytracer.cc:372
std::optional< float > get_intersection(const UnitRay3f &ray, const CollisionMesh &mesh)
T keep_within(const Range< T > &range, T value)
Definition: range.h:115
float sqrt(float r)
Definition: numeric.cc:101
Rgbi to_rgbi(const Rgb &c)
Definition: rgb.cc:352
Range< T > make_range(T min, T max)
Definition: range.h:39
float tan(const Angle &ang)
Definition: angle.cc:75
float square(float r)
Definition: numeric.cc:94
unit3f get_random_unit3(Random *random)
Definition: polarcoord.cc:42
Rgb lerp_rgb(const Rgb &from, float v, const Rgb &to)
Definition: rgb.cc:410
constexpr Range< float > r01
Definition: range.h:57
bool is_within(const Range< T > &range, T value)
Definition: range.h:108
size2f max(const size2f lhs, const size2f rhs)
Definition: size2.cc:149
constexpr static Angle from_degrees(float degrees)
Definition: angle.h:16
WEL512 Random Number Generator.
Definition: random.h:21
Definition: rgb.h:62
unit3f dir
Definition: ray.h:11
vec3f get_point(float at) const
Definition: ray.cc:31
static UnitRay3f from_to(const vec3f &from, const vec3f &to)
Definition: ray.cc:17
void set_pixel(int x, int y, const Rgbai &color)
Definition: image.cc:104
int height
Definition: image.h:33
UnitRay3f get_ray(float u, float v) const
Definition: raytracer.cc:452
static Camera create(const Angle &vfov, float aspect)
Definition: raytracer.cc:432
Collision(float aray_distance, const vec3f &aposition, const unit3f &anormal, std::shared_ptr< raytracer::Material > amaterial)
Definition: raytracer.cc:18
DielectricMaterial(const Rgb &aalbedo, float arefractive_index)
Definition: raytracer.cc:264
std::optional< ScatterResult > get_scattered(const UnitRay3f &ray, const Collision &hit, Random *random) override
Definition: raytracer.cc:275
std::optional< ScatterResult > get_scattered(const UnitRay3f &, const Collision &hit, Random *random) override
Definition: raytracer.cc:153
DiffuseMaterial(const Rgb &aalbedo)
Definition: raytracer.cc:146
std::optional< ScatterResult > get_scattered(const UnitRay3f &ray, const Collision &hit, Random *random) override
Definition: raytracer.cc:193
MetalMaterial(const Rgb &aalbedo, float afuzz)
Definition: raytracer.cc:182
std::vector< std::shared_ptr< Object > > objects
Definition: raytracer.h:113
std::optional< Collision > get_collision(const UnitRay3f &ray, const Range< float > &range) const
Definition: raytracer.cc:97
SphereObject(const core::Sphere &asphere, const vec3f &aposition, std::shared_ptr< raytracer::Material > amaterial)
Definition: raytracer.cc:39
std::shared_ptr< raytracer::Material > material
Definition: raytracer.cc:36
std::optional< Collision > get_collision(const UnitRay3f &ray, const Range< float > &range) const override
Definition: raytracer.cc:51
Definition: vec3.h:48
float y
Definition: vec3.h:50
unit3f get_normalized() const
Definition: vec3.cc:174
static vec3f from_to(const vec3f &from, const vec3f &to)
Definition: vec3.cc:127
float dot(const vec3f &rhs) const
Definition: vec3.cc:276