Phongo Clap RT  1.0
Simple Raytracing Renderer
Renderer.cpp
Go to the documentation of this file.
1 
4 #include <iostream>
5 #include <vector>
6 #include <cmath>
7 #include <algorithm>
8 #include "Renderer.h"
9 #include "Ray.h"
10 #include "Material.h"
11 #include <ngl/Vec3.h>
12 #include <ngl/Types.h>
13 
14 // from https://www.ross.click/2011/02/creating-a-progress-bar-in-c-or-any-other-console-app/ ROSS HEMSLEY
15 // Process has done i out of n rounds,
16 // and we want a bar of width w and resolution r.
17 
21 inline void Renderer::loadBar(int x, int n, int r, int w)
22 {
23  // Only update r times.
24  if ( x % (n/r +1) != 0 ) return;
25 
26  // Calculuate the ratio of complete-to-incomplete.
27  float ratio = x/(float)n;
28  int c = ratio * w;
29 
30  // Show the percentage complete.
31  printf("%3d%% [", (int)(ratio*100) );
32 
33  // Show the load bar.
34  for (int x=0; x<c; x++)
35  printf("=");
36 
37  for (int x=c; x<w; x++)
38  printf(" ");
39 
40  // ANSI Control codes to go back to the previous line and clear it.
41  printf("]\n\033[F\033[J");
42 }
46 
48 {
49  for(unsigned int i = 0; i < m_scene->m_objects.size(); i++)
50  {
51  delete m_scene->m_objects.at(i);
52  }
53  for(unsigned int i = 0; i < m_scene->m_lights.size(); i++)
54  {
55  delete m_scene->m_lights.at(i);
56  }
57 }
58 
59 void Renderer::bind(Scene *_scene, Film *_film, Camera *_camera, int _depth, int _anti_aliasing, std::string _image_name)
60 {
61  m_scene = _scene;
62  m_film = _film;
63  m_camera = _camera;
66  m_anti_aliasing = _anti_aliasing;
67  m_max_depth = _depth;
68  m_bg_colour = ngl::Vec3(0,0,0);
69  m_image_name = _image_name;
70 }
71 
72 int Renderer::getIndexClosest(std::vector<double> _interxs)
73 {
74  int index_min_val;
75  if(_interxs.size() == 0) {return -1;}
76  else if(_interxs.size() == 1)
77  {
78  if(_interxs.at(0) > 0) {return 0;}
79  else {return -1;}
80  }
81  else
82  {
83  double max = 0;
84  for (unsigned int i = 0; i < _interxs.size(); i++)
85  {
86  if(max < _interxs.at(i)) {max = _interxs.at(i);}
87  }
88  if (max > 0)
89  {
90  for (unsigned int i = 0; i < _interxs.size(); i++)
91  {
92  if(_interxs.at(i) > 0 && _interxs.at(i) <= max)
93  {
94  max = _interxs.at(i);
95  index_min_val = i;
96  }
97  }
98  return index_min_val;
99  }
100  else {return -1;}
101  }
102 }
103 
104 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
105  * RAYCASTLGORITHM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
106  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
107 bool Renderer::raycast(ngl::Vec3 _from, int _avoid)
108 {
109  bool shadowed = false;
110  for(unsigned int i = 0; i < m_scene->m_lights.size(); i++)
111  {
112  // create vector that will store intersection values for parameter t in the primary ray
113  std::vector<double> intersections;
114 
115  ngl::Vec3 dir = m_scene->m_lights.at(i)->m_pos - _from;
116  float distance = dir.length();
117  dir.normalize();
118  geo::Ray fire_ray(_from, dir);
119 
120  // iterate over objects in the scene and find intersections
121  for(unsigned int j = 0; j < m_scene->m_objects.size(); j++)
122  {
123  intersections.push_back( m_scene->m_objects.at(j)->getIntersection(fire_ray));
124  }
125 
126  for(unsigned int k = 0; k < intersections.size(); k++)
127  {
128  if(intersections.at(k) < 0.1) continue;
129  if(intersections.at(k) < -1) continue;
130  int closest_index = getIndexClosest(intersections);
131  if (closest_index == -1 || closest_index == _avoid) continue;
132  if(intersections.at(k) > distance) continue;
133  shadowed = true;
134  }
135  }
136  return shadowed;
137 }
138 
139 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
140  * TRACE ALGORITHM * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
141  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
142 ngl::Colour Renderer::trace(ngl::Vec3 _from, ngl::Vec3 _direction, int depth)
143 {
144  // create vector that will store intersection values for parameter t in the primary ray
145  // a primary ray has a form like R = O + t * D where O is the origin vector, and D direction.
146  std::vector<double> intersections;
147  geo::Ray cam_ray(_from,_direction);
148 
149  // iterate over objects in the scene and find intersections
150  for(unsigned int i = 0; i < m_scene->m_objects.size(); i++)
151  {
152  // each Shape subclass (Sphere, Plane...) has its own method for calculating intersections
153  intersections.push_back( m_scene->m_objects.at(i)->getIntersection(cam_ray));
154  }
155 
156  // find closest object
157  int closest_index = getIndexClosest(intersections);
158 
159  // if no intersections are found RETURN black =
160  if(closest_index == -1) {return ngl::Colour(0,0,0,1);}
161 
162  // calculate pHit (position of the new intersection) and nHit (normal at hit point)
163  ngl::Vec3 pHit = _from + intersections.at(closest_index) * _direction;
164  ngl::Vec3 nHit = m_scene->m_objects.at(closest_index)->getNormalAt(pHit);
165 
166  // calculate if we are inside or outside
167  bool inside = false;
168  if(_direction.dot(nHit) > 0)
169  {
170  nHit = -nHit;
171  inside = true;
172  }
173 
174  float bias = 0.01;
175  // calculate if point is obscured or shadowed
176  bool isObscured = raycast(pHit + nHit * bias, closest_index);
177 
178  // // // // // // // // // // // // //
179  // put all contributions together //
180  // // // // // // // // // // // // //
181 
182  // is the object reflective or refractive???
183  if ((m_scene->m_objects.at(closest_index)->getMaterial()->isReflective() ||
184  m_scene->m_objects.at(closest_index)->getMaterial()->isRefractive()) &&
185  depth < m_max_depth)
186  {
187  ngl::Colour crfr(0,0,0,1);
188  ngl::Colour crfl(0,0,0,1);
189  // check whether it is REFLECTIVE
190  if (m_scene->m_objects.at(closest_index)->getMaterial()->isReflective())
191  {
192  // calculate reflection dir
193  float bias = 0.01;
194  ngl::Vec3 refl_dir = _direction - nHit * 2 * _direction.dot(nHit);
195  refl_dir.normalize();
196 
197  // fire ray along reflection direction from hit point
198  crfl = trace(pHit + bias*nHit, refl_dir, depth+1);
199  }
200 
201  // check whether it is REFRACTIVE
202  if (m_scene->m_objects.at(closest_index)->getMaterial()->isRefractive())
203  {
204  // calculate refrection dir (transmission ray)
205  float ior = m_scene->m_objects.at(closest_index)->getMaterial()->getIOR();
206  float eta = inside;
207  float bias = 0.01;
208  float cosi = -nHit.dot(_direction);
209 
210  if (eta == true) // we are inside
211  {
212  eta = ior;
213  }
214  else // we are outside
215  {
216  eta = 1 / ior;
217  }
218 
219  float k = 1 - eta * eta * (1 - cosi * cosi);
220  ngl::Vec3 refr_dir = _direction * eta + nHit * (eta * cosi - sqrt(k));
221  refr_dir.normalize();
222  crfr = trace(pHit - nHit * bias, refr_dir, depth+1);
223  }
224 
225  ngl::Colour surfaceColor = m_scene->m_objects.at(closest_index)->getColour(pHit);
226  float cosineFactor = std::max(-nHit.dot(cam_ray.getDirection()),(float)0);
227  float attenuation;
228 
229  ngl::Colour Ka(1,0,0.4,1);
230  ngl::Colour Kd;
231  ngl::Colour Ks;
232 
233  float ambient_intensity = 0.05;
234 
235  ngl::Colour ambient_contrib = Ka * surfaceColor * ambient_intensity;
236  ngl::Colour diffuse_contrib(0,0,0,1);
237  ngl::Colour specular_contrib(0,0,0,1);
238 
239  for(unsigned int m = 0; m < m_scene->m_lights.size(); m++)
240  {
241  ngl::Vec3 v_distance = m_scene->m_lights.at(m)->m_pos - pHit;
242  float distance = v_distance.length();
243  float radius = 8;
244  attenuation = 1 - pow(distance/radius,2);
245 
246  Kd = m_scene->m_lights.at(m)->m_diff_col;
247  Ks = m_scene->m_lights.at(m)->m_spec_col;
248 
249  ngl::Vec3 L = m_scene->m_lights.at(m)->m_pos - pHit;
250  L.normalize();
251  ngl::Vec3 N = nHit;
252  ngl::Vec3 R = 2 * (L.dot(N) * N) - L;
253  R.normalize();
254 
255  diffuse_contrib += (surfaceColor * (Kd * pow(std::max(L.dot(N),(float)0),2) * m_scene->m_lights.at(m)->m_diff_int))*attenuation;
256  specular_contrib += ((Ks * pow(std::max(R.dot(-_direction),(float)0),900)*400 * m_scene->m_lights.at(m)->m_spec_int))*attenuation;
257  }
258 
259  specular_contrib.clamp(0,0.8);
260 
261  ngl::Colour s01 = crfl * m_scene->m_objects.at(closest_index)->getMaterial()->getReflIntensity();
262  ngl::Colour s02 = crfr * m_scene->m_objects.at(closest_index)->getMaterial()->getTransparency();
263  ngl::Colour s03 = s01 + s02;
264  ngl::Colour diffuseColor = m_scene->m_objects.at(closest_index)->getColour(pHit) * cosineFactor * m_scene->m_objects.at(closest_index)->getMaterial()->getDiffuseIntensity();
265 
266  // Do PHONG MODEL calculations stuff. By now I keep it VERY VERY simple
267  ngl::Colour outRadiance = diffuseColor + s03 + specular_contrib + ambient_contrib;
268  outRadiance.clamp(0,1);
269 
270  return isObscured ? outRadiance * 0.7f : outRadiance;
271  }
272 
273  // if it is not REFLECTIVE nor REFRACTIVE
274  else
275  {
276  ngl::Colour surfaceColor = m_scene->m_objects.at(closest_index)->getColour(pHit);
277  float attenuation;
278 
279  ngl::Colour Ka(1,0,0.4,1);
280  ngl::Colour Kd;
281  ngl::Colour Ks;
282 
283  float ambient_intensity = 0.05;
284 
285  ngl::Colour ambient_contrib = Ka * surfaceColor * ambient_intensity;
286  ngl::Colour diffuse_contrib(0,0,0,1);
287  ngl::Colour specular_contrib(0,0,0,1);
288 
289  for(unsigned int m = 0; m < m_scene->m_lights.size(); m++)
290  {
291  ngl::Vec3 v_distance = m_scene->m_lights.at(m)->m_pos - pHit;
292  float distance = v_distance.length();
293  float radius = 8;
294  attenuation = 1 - pow(distance/radius,2);
295 
296  Kd = m_scene->m_lights.at(m)->m_diff_col;
297  Ks = m_scene->m_lights.at(m)->m_spec_col;
298 
299  ngl::Vec3 L = m_scene->m_lights.at(m)->m_pos - pHit;
300  L.normalize();
301  ngl::Vec3 N = nHit;
302  ngl::Vec3 R = 2 * (L.dot(N) * N) - L;
303  R.normalize();
304 
305  float spec_hardness = m_scene->m_objects.at(closest_index)->getMaterial()->m_spec_hardness;
306 
307  diffuse_contrib += (surfaceColor * (Kd * pow(std::max(L.dot(N),(float)0),2) * m_scene->m_lights.at(m)->m_diff_int))*attenuation;
308  specular_contrib += ((Ks * pow(std::max(R.dot(-_direction),(float)0),spec_hardness) * m_scene->m_lights.at(m)->m_spec_int))*attenuation;
309  }
310 
311  diffuse_contrib.clamp(0,1);
312  specular_contrib.clamp(0,1);
313 
314  ngl::Colour outRadiance = diffuse_contrib + specular_contrib + ambient_contrib;
315 
316  outRadiance.clamp(0,1);
317 
318  return isObscured ? outRadiance * 0.7f : outRadiance;
319  }
320 }
321 
323 {
324  std::vector<ngl::Colour> colourStack;
325  for(int y = 0; y < m_film->m_height; y++)
326  {
327  for(int x = 0; x < m_film->m_width; x++)
328  {
329  int current_pixel = y*m_film->m_height + x;
330  int total_number_of_pixels = m_film->m_height * m_film->m_width;
331  int r = 90;
332  int w = 45;
333 
334  loadBar(current_pixel, total_number_of_pixels, r, w);
335 
336  ngl::Colour finalColour;
337  if(m_anti_aliasing)
338  {
339  for(int aay = 0; aay < m_anti_aliasing; aay++)
340  {
341  for(int aax = 0; aax < m_anti_aliasing; aax++)
342  {
343  // calculate the primary ray
344  float x_amount = ( (float)x + ((float)aax / (float)m_anti_aliasing) + 0.5f * ((float)aax / (float)m_anti_aliasing) ) / (float)m_film->m_width;
345  float y_amount = ( (float)y + ((float)aay / (float)m_anti_aliasing) + 0.5f * ((float)aay / (float)m_anti_aliasing) ) / (float)m_film->m_width;
346 
347  ngl::Vec3 cam_ray_dir = m_camera->m_dir + (m_camera->m_right * (x_amount - 0.5) + (m_camera->m_down * (y_amount - 0.5)));
348  cam_ray_dir.normalize();
349 
350  // fire the ray and store its colour into a variable
351  ngl::Colour col = trace(m_camera->m_pos, cam_ray_dir, 0);
352 
353  colourStack.push_back(col);
354  }
355  }
356 
357  // AVERAGE COLOURS
358  float cRed = 0.0f;
359  float cGreen = 0.0f;
360  float cBlue = 0.0f;
361 
362  for(int i = 0; i < m_anti_aliasing; i++)
363  {
364  cRed += colourStack.at(i).m_r;
365  cGreen += colourStack.at(i).m_g;
366  cBlue += colourStack.at(i).m_b;
367  }
368 
369  cRed /= (float)m_anti_aliasing;
370  cGreen /= (float)m_anti_aliasing;
371  cBlue /= (float)m_anti_aliasing;
372 
373  finalColour = ngl::Colour(cRed, cGreen, cBlue, 1);
374 
375  // FLUSH VECTOR
376  colourStack.clear();
377  }
378 
379  else // there is anti-aliasing
380  {
381  float x_amount = (x+0.5)/(float)m_film->m_width;
382  float y_amount = ((y) + 0.5)/(float)m_film->m_height;
383 
384  ngl::Vec3 cam_ray_dir = m_camera->m_dir + (m_camera->m_right * (x_amount - 0.5) + (m_camera->m_down * (y_amount - 0.5)));
385  cam_ray_dir.normalize();
386 
387  // fire the ray and store its colour into a variable
388  finalColour = trace(m_camera->m_pos, cam_ray_dir, 0);
389  }
390 
391  // write pixel into the Film object associated to the Render object
392  m_film->writePixel(finalColour);
393  }
394  }
395 
396  // write the file into disk afterwards and display it
397  m_film->writeFile(m_image_name.c_str());
398 
399 }
void render()
Will trigger the class and start doing all the calculations.
Definition: Renderer.cpp:322
void writeFile(const char *_image_name)
Iterates over the pixel vector and writes into a file using basic output stream methods.
Definition: Film.cpp:26
Camera * m_camera
Associated camera.
Definition: Renderer.h:101
Film * m_film
Associated film.
Definition: Renderer.h:93
int m_anti_aliasing
Antialiasing amount.
Definition: Renderer.h:117
ngl::Vec3 m_down
Camera down vector.
Definition: Camera.h:52
ngl::Vec3 m_pos
Camera position vector.
Definition: Camera.h:44
This will be used to return the colour when queried from the Shape derived classes.
int m_height
Height of the image.
Definition: Renderer.h:113
~Renderer()
Destructor, frees any possible memory from the heap.
Definition: Renderer.cpp:47
Renderer()
Constructor for the renderer class.
Definition: Renderer.cpp:45
std::vector< Light * > m_lights
Holds all the Light instances of the scene.
Definition: Scene.h:62
Definition: Ray.h:17
static void loadBar(int x, int n, int r, int w)
Implements a loading bar. This algorithm is taken from another person.
Definition: Renderer.cpp:21
int m_width
Width of the image.
Definition: Renderer.h:109
int m_max_depth
Maximum number of ray stack frames.
Definition: Renderer.h:121
ngl::Vec3 m_dir
Camera aim vector.
Definition: Camera.h:48
ngl::Vec3 m_bg_colour
default background colour.
Definition: Renderer.h:105
Scene * m_scene
Associated scene.
Definition: Renderer.h:97
int getIndexClosest(std::vector< double > _intersections)
Given an intersections vector will return the closest to the camera, the one that it will read colour...
Definition: Renderer.cpp:72
void bind(Scene *_scence, Film *_film, Camera *_camera, int _max_depth, int _anti_aliasing, std::string _image_name)
Grabs all the information and places it into the private interface.
Definition: Renderer.cpp:59
std::string m_image_name
Name of the filename that will be written.
Definition: Renderer.h:89
ngl::Colour trace(ngl::Vec3 _from, ngl::Vec3 _direction, int _depth)
Probably the most important algorithm of this class. This method is recursive. It will fire a ray fro...
Definition: Renderer.cpp:142
bool raycast(ngl::Vec3 _from, int _avoid)
Will fire rays from a position, then iterate over the lights, and if any object is inbetweet will ret...
Definition: Renderer.cpp:107
Holds all the operations regarding to input/output of colour information.
Definition: Film.h:29
int m_width
Width of the Film's image.
Definition: Film.h:64
void writePixel(ngl::Colour _colour)
Will create a pixel and push it to the pixel vector.
Definition: Film.cpp:17
ngl::Vec3 m_right
Camera right vector.
Definition: Camera.h:56
Hold all the objects and lights of the scene.
Definition: Scene.h:20
int m_height
Height of the Film's image.
Definition: Film.h:68
This class is the heart of my raytracer. It is the core, where all the camera rays are calculated and...
Holds camera functions which will be accessed by the Render class.
Definition: Camera.h:17
This class handles the implementation of ray: an object with an origin and a direction.
std::vector< geo::Shape * > m_objects
Holds all the Shape objects in the scene.
Definition: Scene.h:58
ngl::Vec3 getDirection()
Getter method for the direction.
Definition: Ray.cpp:44