SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLRay.cpp
Go to the documentation of this file.
1 /**
2  * \file SLRay.cpp
3  * \date July 2014
4  * \authors Marcus Hudritsch
5  * \copyright http://opensource.org/licenses/GPL-3.0
6  * \remarks Please use clangformat to format the code. See more code style on
7  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
8 */
9 
10 #include <SLRay.h>
11 #include <SLSceneView.h>
12 #include <SLSkybox.h>
13 
14 // init static variables
16 SLfloat SLRay::minContrib = 1.0 / 256.0;
30 
31 //-----------------------------------------------------------------------------
32 /*! Global uniform random number generator for numbers between 0 and 1 that are
33 used in SLRay, SLLightRect and SLPathtracer. So far they work perfectly with
34 CPP11 multithreading.
35 */
36 auto random01 = bind(std::uniform_real_distribution<SLfloat>(0.0, 1.0),
37  std::mt19937((SLuint)time(nullptr)));
38 
39 SLfloat rnd01() { return random01(); }
40 //-----------------------------------------------------------------------------
41 /*!
42 SLRay::SLRay default constructor
43 */
45 {
48  type = PRIMARY;
49  length = FLT_MAX;
50  depth = 1;
51  hitTriangle = -1;
55  hitAO = 1.0f;
56  hitNode = nullptr;
57  hitMesh = nullptr;
58  srcNode = nullptr;
59  srcMesh = nullptr;
60  srcTriangle = -1;
61  x = -1;
62  y = -1;
63  contrib = 1.0f;
64  isOutside = true;
65  isInsideVolume = false;
66  sv = sceneView;
67 }
68 //-----------------------------------------------------------------------------
69 /*!
70 SLRay::SLRay constructor for primary rays
71 */
72 SLRay::SLRay(const SLVec3f& Origin,
73  const SLVec3f& Dir,
74  SLfloat X,
75  SLfloat Y,
76  const SLCol4f& backColor,
77  SLSceneView* sceneView)
78 {
79  origin = Origin;
80  setDir(Dir);
81  type = PRIMARY;
82  length = FLT_MAX;
83  depth = 1;
84  hitTriangle = -1;
88  hitAO = 1.0f;
89  hitNode = nullptr;
90  hitMesh = nullptr;
91  srcNode = nullptr;
92  srcMesh = nullptr;
93  srcTriangle = -1;
94  x = (SLfloat)X;
95  y = (SLfloat)Y;
96  contrib = 1.0f;
97  isOutside = true;
98  isInsideVolume = false;
99  backgroundColor = backColor;
100  sv = sceneView;
101 }
102 //-----------------------------------------------------------------------------
103 /*!
104 SLRay::SLRay constructor for shadow rays
105 */
106 SLRay::SLRay(SLfloat distToLight,
107  const SLVec3f& dirToLight,
108  SLRay* rayFromHitPoint)
109 {
110  origin = rayFromHitPoint->hitPoint;
111  setDir(dirToLight);
112  type = SHADOW;
113  length = distToLight;
114  lightDist = distToLight;
115  depth = rayFromHitPoint->depth;
119  hitAO = 1.0f;
120  hitTriangle = -1;
121  hitNode = nullptr;
122  hitMesh = nullptr;
123  srcNode = rayFromHitPoint->hitNode;
124  srcMesh = rayFromHitPoint->hitMesh;
125  srcTriangle = rayFromHitPoint->hitTriangle;
126  x = rayFromHitPoint->x;
127  y = rayFromHitPoint->y;
128  backgroundColor = rayFromHitPoint->backgroundColor;
129  sv = rayFromHitPoint->sv;
130  contrib = 0.0f;
131  isOutside = rayFromHitPoint->isOutside;
132  shadowRays++;
133 }
134 //-----------------------------------------------------------------------------
135 /*!
136 SLRay::prints prints the rays origin (O), direction (D) and the length to the
137 intersection (L)
138 */
139 void SLRay::print() const
140 {
141  SL_LOG("Ray: O(%.2f, %.2f, %.2f), D(%.2f, %.2f, %.2f), L: %.2f",
142  origin.x,
143  origin.y,
144  origin.z,
145  dir.x,
146  dir.y,
147  dir.z,
148  length);
149 }
150 //-----------------------------------------------------------------------------
151 /*!
152 SLRay::reflect calculates a secondary ray reflected at the normal, starting at
153 the intersection point. All vectors must be normalized vectors.
154 R = 2(-I*N) N + I
155 */
156 void SLRay::reflect(SLRay* reflected) const
157 {
158 #ifdef DEBUG_RAY
159  for (SLint i = 0; i < depth; ++i)
160  cout << " ";
161  cout << "Reflect: " << hitMesh->name() << endl;
162 #endif
163 
164  SLVec3f R(dir - 2.0f * (dir * hitNormal) * hitNormal);
165 
166  reflected->setDir(R);
167  reflected->origin.set(hitPoint);
168  reflected->depth = depth + 1;
169  reflected->length = FLT_MAX;
170  reflected->contrib = contrib * hitMesh->mat()->kr();
171  reflected->srcNode = hitNode;
172  reflected->srcMesh = hitMesh;
173  reflected->srcTriangle = hitTriangle;
174  reflected->type = REFLECTED;
175  reflected->isOutside = isOutside;
176  reflected->x = x;
177  reflected->y = y;
178  reflected->sv = sv;
179  if (sv->s()->skybox())
180  reflected->backgroundColor = sv->s()->skybox()->colorAtDir(reflected->dir);
181  else
182  reflected->backgroundColor = backgroundColor;
183 
184  depthReached = reflected->depth;
185  ++reflectedRays;
186 }
187 //-----------------------------------------------------------------------------
188 /*!
189 SLRay::refract calculates a secondary refracted ray, starting at the
190 intersection point. All vectors must be normalized vectors, so the refracted
191 vector T will be a unit vector too. If total internal refraction occurs a
192 reflected ray is calculated instead.
193 Index of refraction eta = Kn_Source/Kn_Destination (Kn_Air = 1.0)
194 We are using a formula by Xavier Bec that is a little faster:
195 http://www.realtimerendering.com/resources/RTNews/html/rtnv10n1.html#art3
196 */
197 void SLRay::refract(SLRay* refracted)
198 {
199  assert(hitMesh && "hitMesh is null");
200 
201  SLVec3f T; // refracted direction
202  SLfloat eta; // refraction coefficient
203 
204  SLfloat c1 = hitNormal.dot(-dir);
205  SLbool hitFrontSide = c1 > 0.0f;
206 
207  SLMaterial* srcMat = srcMesh ? srcMesh->mat() : nullptr;
208  SLMaterial* hitMat = hitMesh ? hitMesh->mat() : nullptr;
209  SLMaterial* hitMatOut = hitMesh ? hitMesh->matOut() : nullptr;
210 
211 #ifdef DEBUG_RAY
212  for (SLint i = 0; i < depth; ++i)
213  cout << " ";
214  cout << "Refract: ";
215 #endif
216 
217  // Calculate index of refraction eta = Kn_Source/Kn_Destination
218  // Case 1: From air into a mesh
219  if (isOutside)
220  {
221  eta = 1.0f / hitMat->kn();
222  }
223  else
224  { // Case 2: From inside the same mesh
225  if (hitMesh == srcMesh)
226  {
227  if (hitMatOut) // Case 2a: into another material
228  eta = hitMat->kn() / hitMatOut->kn();
229  else // Case 2b: into air
230  eta = hitMat->kn(); // = hitMat / 1.0
231  }
232  else
233  { // Case 3: We hit inside another material from the front
234  if (hitFrontSide)
235  {
236  if (hitMatOut)
237  eta = hitMatOut->kn() / hitMat->kn();
238  else
239  { // Mesh hit without outside material before leaving another mesh.
240  // This should not happen, but can due to float inaccuracies
241  eta = srcMat->kn() / hitMat->kn();
242  }
243  }
244  else // Case 4: We hit inside another material from behind
245  {
246  if (hitMatOut) // Case 4a: into another material
247  eta = hitMat->kn() / hitMatOut->kn();
248  else // Case 4b: into air
249  eta = hitMat->kn(); // = hitMat / 1.0
250  }
251  }
252  }
253 
254  // Invert the hit normal if ray hit backside for correct refraction
255  if (!hitFrontSide)
256  {
257  c1 *= -1.0f;
258  hitNormal *= -1.0f;
259  }
260 
261  SLfloat w = eta * c1;
262  SLfloat c2 = 1.0f + (w - eta) * (w + eta);
263 
264  if (c2 >= 0.0f)
265  {
266  T = eta * dir + (w - sqrt(c2)) * hitNormal;
267  refracted->contrib = contrib * hitMat->kt();
268  refracted->type = REFRACTED;
269 
270  if (isOutside)
271  refracted->isOutside = false;
272  else // inside
273  {
274  if (srcMesh == hitMesh)
275  refracted->isOutside = !hitMatOut;
276  else
277  refracted->isOutside = !hitFrontSide;
278  }
279 
280  ++refractedRays;
281  }
282  else // total internal refraction results in a internal reflected ray
283  {
284  T = 2.0f * (-dir * hitNormal) * hitNormal + dir;
285  refracted->contrib = 1.0f;
286  refracted->type = REFLECTED;
287  refracted->isOutside = isOutside; // remain inside
288  ++tirRays;
289  }
290 
291  refracted->setDir(T);
292  refracted->origin.set(hitPoint);
293  refracted->length = FLT_MAX;
294  refracted->srcNode = hitNode;
295  refracted->srcMesh = hitMesh;
296  refracted->srcTriangle = hitTriangle;
297  refracted->depth = depth + 1;
298  refracted->x = x;
299  refracted->y = y;
300  refracted->sv = sv;
301  if (sv->s()->skybox())
302  refracted->backgroundColor = sv->s()->skybox()->colorAtDir(refracted->dir);
303  else
304  refracted->backgroundColor = backgroundColor;
305  depthReached = refracted->depth;
306 
307 #ifdef DEBUG_RAY
308  cout << hitMesh->name();
309  if (isOutside)
310  cout << ",out";
311  else
312  cout << ",in";
313  if (refracted->isOutside)
314  cout << ">out";
315  else
316  cout << ">in";
317  cout << ", dir: " << refracted->dir.toString();
318  cout << ", contrib: " << Utils::toString(refracted->contrib, 2);
319  cout << endl;
320 #endif
321 }
322 //-----------------------------------------------------------------------------
323 /*!
324 SLRay::reflectMC scatters a ray around perfect specular direction according to
325 shininess (for higher shininess the ray is less scattered). This is used for
326 path tracing and distributed ray tracing as well as for photon scattering.
327 The direction is calculated according to MCCABE. The created direction is
328 along z-axis and then transformed to lie along specular direction with
329 rotationMatrix rotMat. The rotation matrix must be precalculated (stays the
330 same for each ray sample, needs to be be calculated only once)
331 */
332 bool SLRay::reflectMC(SLRay* reflected, const SLMat3f& rotMat) const
333 {
334  SLfloat eta1, eta2;
335  SLVec3f randVec;
336  SLfloat shininess = hitMesh->mat()->shininess();
337 
338  // scatter within specular lobe
339  eta1 = rnd01();
340  eta2 = Utils::TWOPI * rnd01();
341  SLfloat f1 = sqrt(1.0f - pow(eta1, 2.0f / (shininess + 1.0f)));
342 
343  // tranform to cartesian
344  randVec.set(f1 * cos(eta2),
345  f1 * sin(eta2),
346  pow(eta1, 1.0f / (shininess + 1.0f)));
347 
348  // ray needs to be reset if already hit a scene node
349  if (reflected->hitNode)
350  {
351  reflected->length = FLT_MAX;
352  reflected->hitNode = nullptr;
353  reflected->hitMesh = nullptr;
354  reflected->hitPoint = SLVec3f::ZERO;
355  reflected->hitNormal = SLVec3f::ZERO;
356  }
357 
358  // apply rotation
359  reflected->setDir(rotMat * randVec);
360 
361  // Set pixel and background
362  reflected->x = x;
363  reflected->y = y;
364  reflected->sv = sv;
365  if (sv->s()->skybox())
366  reflected->backgroundColor = sv->s()->skybox()->colorAtDir(reflected->dir);
367  else
368  reflected->backgroundColor = backgroundColor;
369 
370  // true if in direction of normal
371  return (hitNormal * reflected->dir >= 0.0f);
372 }
373 //-----------------------------------------------------------------------------
374 /*!
375 SLRay::refractMC scatters a ray around perfect transmissive direction according
376 to translucency (for higher translucency the ray is less scattered).
377 This is used for path tracing and distributed ray tracing as well as for photon
378 scattering. The direction is calculated the same as with specular scattering
379 (see reflectMC). The created direction is along z-axis and then transformed to
380 lie along transmissive direction with rotationMatrix rotMat. The rotation
381 matrix must be precalculated (stays the same for each ray sample, needs to be
382 be calculated only once)
383 */
384 void SLRay::refractMC(SLRay* refracted, const SLMat3f& rotMat) const
385 {
386  SLfloat eta1, eta2;
387  SLVec3f randVec;
388  SLfloat translucency = hitMesh->mat()->translucency();
389 
390  // scatter within transmissive lobe
391  eta1 = rnd01();
392  eta2 = Utils::TWOPI * rnd01();
393  SLfloat f1 = sqrt(1.0f - pow(eta1, 2.0f / (translucency + 1.0f)));
394 
395  // transform to cartesian
396  randVec.set(f1 * cos(eta2),
397  f1 * sin(eta2),
398  pow(eta1, 1.0f / (translucency + 1.0f)));
399 
400  // ray needs to be reset if already hit a scene node
401  if (refracted->hitNode)
402  {
403  refracted->length = FLT_MAX;
404  refracted->hitNode = nullptr;
405  refracted->hitMesh = nullptr;
406  refracted->hitPoint = SLVec3f::ZERO;
407  refracted->hitNormal = SLVec3f::ZERO;
408  }
409 
410  // Apply rotation
411  refracted->setDir(rotMat * randVec);
412 
413  // Set pixel and background
414  refracted->x = x;
415  refracted->y = y;
416  refracted->sv = sv;
417  if (sv->s()->skybox())
418  refracted->backgroundColor = sv->s()->skybox()->colorAtDir(refracted->dir);
419  else
420  refracted->backgroundColor = backgroundColor;
421 }
422 //-----------------------------------------------------------------------------
423 /*!
424 SLRay::diffuseMC scatters a ray around hit normal (cosine distribution).
425 This is only used for photonmapping(russian roulette).
426 The random direction lies around z-Axis and is then transformed by a rotation
427 matrix to lie along the normal. The direction is calculated according to MCCABE
428 */
429 void SLRay::diffuseMC(SLRay* scattered) const
430 {
431  SLVec3f randVec;
432  SLfloat eta1, eta2, eta1sqrt;
433 
434  scattered->setDir(hitNormal);
435  scattered->origin = hitPoint;
436  scattered->depth = depth + 1;
437  depthReached = scattered->depth;
438 
439  // for reflectance the start material stays the same
440  scattered->srcNode = hitNode;
441  scattered->srcMesh = hitMesh;
442  scattered->type = REFLECTED;
443 
444  // calculate rotation matrix
445  SLMat3f rotMat;
446  SLVec3f rotAxis((SLVec3f(0.0, 0.0, 1.0) ^ scattered->dir).normalize());
447  SLfloat rotAngle = acos(scattered->dir.z); // z*scattered.dir()
448  rotMat.rotation(rotAngle * 180.0f * Utils::ONEOVERPI, rotAxis);
449 
450  // cosine distribution
451  eta1 = rnd01();
452  eta2 = Utils::TWOPI * rnd01();
453  eta1sqrt = sqrt(1 - eta1);
454 
455  // transform to cartesian
456  randVec.set(eta1sqrt * cos(eta2),
457  eta1sqrt * sin(eta2),
458  sqrt(eta1));
459 
460  // Apply rotation
461  scattered->setDir(rotMat * randVec);
462 
463  // Set pixel and background
464  scattered->x = x;
465  scattered->y = y;
466  scattered->sv = sv;
467  if (sv->s()->skybox())
468  scattered->backgroundColor = sv->s()->skybox()->colorAtDir(scattered->dir);
469  else
470  scattered->backgroundColor = backgroundColor;
471 }
472 //-----------------------------------------------------------------------------
float SLfloat
Definition: SL.h:173
#define SL_LOG(...)
Definition: SL.h:233
unsigned int SLuint
Definition: SL.h:171
bool SLbool
Definition: SL.h:175
int SLint
Definition: SL.h:170
auto random01
Definition: SLRay.cpp:36
SLfloat rnd01()
Definition: SLRay.cpp:39
@ SHADOW
Definition: SLRay.h:26
@ PRIMARY
Definition: SLRay.h:23
@ REFLECTED
Definition: SLRay.h:24
@ REFRACTED
Definition: SLRay.h:25
SLVec3< SLfloat > SLVec3f
Definition: SLVec3.h:318
void rotation(const T angleDEG, const SLVec3< T > &axis)
Sets the rotation components
Definition: SLMat3.h:392
Defines a standard CG material with textures and a shader program.
Definition: SLMaterial.h:56
void translucency(SLfloat transl)
Definition: SLMaterial.h:176
void kt(SLfloat kt)
Definition: SLMaterial.h:190
void shininess(SLfloat shin)
Definition: SLMaterial.h:177
void kr(SLfloat kr)
Definition: SLMaterial.h:184
void kn(SLfloat kn)
Definition: SLMaterial.h:199
SLMaterial * matOut() const
Definition: SLMesh.h:178
SLMaterial * mat() const
Definition: SLMesh.h:177
void name(const SLstring &Name)
Definition: SLObject.h:34
Ray class with ray and intersection properties.
Definition: SLRay.h:40
SLint hitTriangle
Points to the intersected triangle.
Definition: SLRay.h:107
SLVec3f origin
Vector to the origin of ray in WS.
Definition: SLRay.h:75
SLRay()
default ctor
Definition: SLRayMC.cpp:44
static SLint depthReached
depth reached for a primary ray
Definition: SLRay.h:134
static SLuint intersections
NO. of intersection.
Definition: SLRay.h:133
static SLuint primaryRays
NO. of primary rays shot.
Definition: SLRay.h:126
bool reflectMC(SLRay *reflected, const SLMat3f &rotMat) const
Definition: SLRay.cpp:332
SLfloat lightDist
Distance to light for shadow rays.
Definition: SLRay.h:93
static SLuint tirRays
NO. of TIR refraction rays.
Definition: SLRay.h:131
SLRayType type
PRIMARY, REFLECTED, REFRACTED, SHADOW.
Definition: SLRay.h:92
SLint srcTriangle
Points to the triangle at ray origin.
Definition: SLRay.h:99
SLCol4f backgroundColor
Background color at pixel x,y.
Definition: SLRay.h:100
static SLuint tests
NO. of intersection tests.
Definition: SLRay.h:132
SLMesh * hitMesh
Points to the intersected mesh.
Definition: SLRay.h:106
SLbool isOutside
Flag if ray is inside of a material.
Definition: SLRay.h:95
static SLuint reflectedRays
NO. of reflected rays.
Definition: SLRay.h:127
SLint depth
Recursion depth for ray tracing.
Definition: SLRay.h:78
SLfloat hitAO
Ambient occlusion factor at intersection point.
Definition: SLRay.h:113
SLMaterial * hitMat
Points to material of intersected node.
Definition: SLRayMC.h:116
SLVec3f dir
Direction vector of ray in WS.
Definition: SLRay.h:76
SLMesh * srcMesh
Points to the mesh at ray origin.
Definition: SLRay.h:98
void refract(SLRay *refracted)
Definition: SLRay.cpp:197
static SLint maxDepthReached
max. depth reached for all rays
Definition: SLRay.h:135
static SLuint refractedRays
NO. of refracted rays.
Definition: SLRay.h:128
SLfloat length
length from origin to an intersection
Definition: SLRay.h:77
void print() const
Definition: SLRay.cpp:139
SLNode * hitNode
Points to the intersected node.
Definition: SLRay.h:105
static SLuint ignoredRays
NO. of ignore refraction rays.
Definition: SLRay.h:129
SLfloat contrib
Current contribution of ray to color.
Definition: SLRay.h:79
static SLuint subsampledPixels
NO. of of subsampled pixels.
Definition: SLRay.h:138
SLbool isInsideVolume
Flag if ray is in Volume.
Definition: SLRay.h:96
SLVec3f hitPoint
Point of intersection.
Definition: SLRay.h:110
void diffuseMC(SLRay *scattered) const
Definition: SLRay.cpp:429
SLNode * srcNode
Points to the node at ray origin.
Definition: SLRay.h:97
void setDir(const SLVec3f &Dir)
Setter for the rays direction in world space also setting the inverse direction.
Definition: SLRay.h:146
static SLfloat avgDepth
average depth reached
Definition: SLRay.h:136
static SLfloat minContrib
Min. contibution to color (1/256)
Definition: SLRay.h:125
void reflect(SLRay *reflected) const
Definition: SLRay.cpp:156
static SLuint subsampledRays
NO. of of subsampled rays.
Definition: SLRay.h:137
SLCol4f hitTexColor
Color at intersection for texture or color attributes.
Definition: SLRay.h:112
SLfloat y
Pixel position for primary rays.
Definition: SLRay.h:94
SLfloat x
Definition: SLRay.h:94
void refractMC(SLRay *refracted, const SLMat3f &rotMat) const
Definition: SLRay.cpp:384
static SLuint shadowRays
NO. of shadow rays.
Definition: SLRay.h:130
SLVec3f hitNormal
Surface normal at intersection point.
Definition: SLRay.h:111
SLSceneView * sv
Pointer to the sceneview.
Definition: SLRay.h:101
static SLint maxDepth
Max. recursion depth.
Definition: SLRay.h:124
void skybox(SLSkybox *skybox)
Definition: SLScene.h:91
SceneView class represents a dynamic real time 3D view onto the scene.
Definition: SLSceneView.h:69
SLScene * s()
Definition: SLSceneView.h:167
SLstring toString(SLstring delimiter=", ", int decimals=2)
Conversion to string.
Definition: SLVec3.h:199
T y
Definition: SLVec3.h:43
T x
Definition: SLVec3.h:43
void set(const T X, const T Y, const T Z)
Definition: SLVec3.h:59
T dot(const SLVec3 &v) const
Definition: SLVec3.h:117
T z
Definition: SLVec3.h:43
static SLVec3 ZERO
Definition: SLVec3.h:285
static SLVec4 WHITE
Definition: SLVec4.h:215
static const float ONEOVERPI
Definition: Utils.h:241
static const float TWOPI
Definition: Utils.h:240
string toString(float f, int roundedDecimals)
Returns a string from a float with max. one trailing zero.
Definition: Utils.cpp:92