ef7278248ba73a8c17f2617b164dfcc1e9fb384c
[blender-staging.git] / intern / elbeem / intern / ntl_ray.cpp
1 /******************************************************************************
2  *
3  * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
4  * Copyright 2003,2004 Nils Thuerey
5  *
6  * main renderer class
7  *
8  *****************************************************************************/
9
10
11 #include "ntl_ray.h"
12 #include "ntl_scene.h"
13
14
15 /* Minimum value for refl/refr to be traced */
16 #define RAY_THRESHOLD 0.001
17
18 #if GFX_PRECISION==1
19 // float values
20 //! Minimal contribution for rays to be traced on
21 #define RAY_MINCONTRIB (1e-04)
22
23 #else 
24 // double values
25 //! Minimal contribution for rays to be traced on
26 #define RAY_MINCONTRIB (1e-05)
27
28 #endif 
29
30
31
32
33
34 /******************************************************************************
35  * Constructor
36  *****************************************************************************/
37 ntlRay::ntlRay( void )
38   : mOrigin(0.0)
39   , mDirection(0.0)
40   , mvNormal(0.0)
41   , mDepth(0)
42   , mpGlob(NULL)
43   , mIsRefracted(0)
44 {
45   errFatal("ntlRay::ntlRay()","Don't use uninitialized rays !", SIMWORLD_GENERICERROR);
46         return;
47 }
48
49
50 /******************************************************************************
51  * Copy - Constructor
52  *****************************************************************************/
53 ntlRay::ntlRay( const ntlRay &r )
54 {
55   // copy it! initialization is not enough!
56   mOrigin    = r.mOrigin;
57   mDirection = r.mDirection;
58   mvNormal   = r.mvNormal;
59   mDepth     = r.mDepth;
60   mIsRefracted  = r.mIsRefracted;
61   mIsReflected  = r.mIsReflected;
62         mContribution = r.mContribution;
63   mpGlob        = r.mpGlob;
64
65         // get new ID
66         if(mpGlob) {
67                 mID           = mpGlob->getCounterRays()+1;
68                 mpGlob->setCounterRays( mpGlob->getCounterRays()+1 );
69         } else {
70                 mID = 0;
71         }
72 }
73
74
75 /******************************************************************************
76  * Constructor with explicit parameters and global render object
77  *****************************************************************************/
78 ntlRay::ntlRay(const ntlVec3Gfx &o, const ntlVec3Gfx &d, unsigned int i, gfxReal contrib, ntlRenderGlobals *glob)
79   : mOrigin( o )
80   , mDirection( d )
81   , mvNormal(0.0)
82   , mDepth( i )
83         , mContribution( contrib )
84   , mpGlob( glob )
85   , mIsRefracted( 0 )
86         , mIsReflected( 0 )
87 {
88         // get new ID
89         if(mpGlob) {
90                 mID           = mpGlob->getCounterRays()+1;
91                 mpGlob->setCounterRays( mpGlob->getCounterRays()+1 );
92         } else {
93                 mID = 0;
94         }
95 }
96
97
98
99 /******************************************************************************
100  * Destructor
101  *****************************************************************************/
102 ntlRay::~ntlRay()
103 {
104   /* nothing to do... */
105 }
106
107
108
109 /******************************************************************************
110  * AABB
111  *****************************************************************************/
112 /* for AABB intersect */
113 #define NUMDIM 3
114 #define RIGHT  0
115 #define LEFT   1
116 #define MIDDLE 2
117
118 //! intersect ray with AABB
119 void ntlRay::intersectFrontAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &t, ntlVec3Gfx &retnormal,ntlVec3Gfx &retcoord) const
120 {
121   char   inside = true;   /* inside box? */
122   char   hit    = false;  /* ray hits box? */
123   int    whichPlane;      /* intersection plane */
124   gfxReal candPlane[NUMDIM];  /* candidate plane */
125   gfxReal quadrant[NUMDIM];   /* quadrants */
126   gfxReal maxT[NUMDIM];       /* max intersection T for planes */
127   ntlVec3Gfx  coord;           /* intersection point */
128   ntlVec3Gfx  dir = mDirection;
129   ntlVec3Gfx  origin = mOrigin;
130   ntlVec3Gfx  normal(0.0, 0.0, 0.0);
131
132   t = GFX_REAL_MAX;
133
134   /* check intersection planes for AABB */
135   for(int i=0;i<NUMDIM;i++) {
136     if(origin[i] < mStart[i]) {
137       quadrant[i] = LEFT;
138       candPlane [i] = mStart[i];
139       inside = false;
140     } else if(origin[i] > mEnd[i]) {
141       quadrant[i] = RIGHT;
142       candPlane[i] = mEnd[i];
143       inside = false;
144     } else {
145       quadrant[i] = MIDDLE;
146     }
147   }
148
149   /* inside AABB? */
150   if(!inside) {
151     /* get t distances to planes */
152     /* treat too small direction components as paralell */
153     for(int i=0;i<NUMDIM;i++) {
154       if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
155                                 maxT[i] = (candPlane[i] - origin[i]) / dir[i];
156       } else {
157                                 maxT[i] = -1;
158       }
159     }
160     
161     /* largest max t */
162     whichPlane = 0;
163     for(int i=1;i<NUMDIM;i++) {
164       if(maxT[whichPlane] < maxT[i]) whichPlane = i;
165     }
166     
167     /* check final candidate */
168     hit  = true;
169     if(maxT[whichPlane] >= 0.0) {
170
171       for(int i=0;i<NUMDIM;i++) {
172                                 if(whichPlane != i) {
173                                         coord[i] = origin[i] + maxT[whichPlane] * dir[i];
174                                         if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
175                                                         (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
176                                                 /* no hit... */
177                                                 hit  = false;
178                                         } 
179                                 }
180                                 else {
181                                         coord[i] = candPlane[i];
182                                 }      
183       }
184
185       /* AABB hit... */
186       if( hit ) {
187                                 t = maxT[whichPlane];   
188                                 if(quadrant[whichPlane]==RIGHT) normal[whichPlane] = 1.0;
189                                 else normal[whichPlane] = -1.0;
190       }
191     }
192     
193
194   } else {
195     /* inside AABB... */
196     t  = 0.0;
197     coord = origin;
198     return;
199   }
200
201   if(t == GFX_REAL_MAX) t = -1.0;
202   retnormal = normal;
203   retcoord = coord;
204 }
205
206
207
208 //! intersect ray with AABB
209 void ntlRay::intersectBackAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &t, ntlVec3Gfx &retnormal,ntlVec3Gfx &retcoord) const
210 {
211   char   hit    = false;  /* ray hits box? */
212   int    whichPlane;      /* intersection plane */
213   gfxReal candPlane[NUMDIM]; /* candidate plane */
214   gfxReal quadrant[NUMDIM];  /* quadrants */
215   gfxReal maxT[NUMDIM];    /* max intersection T for planes */
216   ntlVec3Gfx  coord;           /* intersection point */
217   ntlVec3Gfx  dir = mDirection;
218   ntlVec3Gfx  origin = mOrigin;
219   ntlVec3Gfx  normal(0.0, 0.0, 0.0);
220
221   t = GFX_REAL_MAX;
222   for(int i=0;i<NUMDIM;i++) {
223     if(origin[i] < mStart[i]) {
224       quadrant[i] = LEFT;
225       candPlane [i] = mEnd[i];
226     } else if(origin[i] > mEnd[i]) {
227       quadrant[i] = RIGHT;
228       candPlane[i] = mStart[i];
229     } else {
230       if(dir[i] > 0) {
231                                 quadrant[i] = LEFT;
232                                 candPlane [i] = mEnd[i];
233       } else 
234                                 if(dir[i] < 0) {
235                                         quadrant[i] = RIGHT;
236                                         candPlane[i] = mStart[i];
237                                 } else {
238                                         quadrant[i] = MIDDLE;
239                                 }
240     }
241   }
242
243
244         /* get t distances to planes */
245         /* treat too small direction components as paralell */
246         for(int i=0;i<NUMDIM;i++) {
247                 if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
248                         maxT[i] = (candPlane[i] - origin[i]) / dir[i];
249                 } else {
250                         maxT[i] = GFX_REAL_MAX;
251                 }
252         }
253     
254         /* largest max t */
255         whichPlane = 0;
256         for(int i=1;i<NUMDIM;i++) {
257                 if(maxT[whichPlane] > maxT[i]) whichPlane = i;
258         }
259     
260         /* check final candidate */
261         hit  = true;
262         if(maxT[whichPlane] != GFX_REAL_MAX) {
263
264                 for(int i=0;i<NUMDIM;i++) {
265                         if(whichPlane != i) {
266                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
267                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
268                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
269                                         /* no hit... */
270                                         hit  = false;
271                                 } 
272                         }
273                         else {
274                                 coord[i] = candPlane[i];
275                         }      
276                 }
277
278                 /* AABB hit... */
279                 if( hit ) {
280                         t = maxT[whichPlane];
281         
282                         if(quadrant[whichPlane]==RIGHT) normal[whichPlane] = 1.0;
283                         else normal[whichPlane] = -1.0;
284                 }
285         }
286     
287
288   if(t == GFX_REAL_MAX) t = -1.0;
289   retnormal = normal;
290   retcoord = coord;
291 }
292
293
294
295
296
297 //! intersect ray with AABB
298 void ntlRay::intersectCompleteAABB(ntlVec3Gfx mStart, ntlVec3Gfx mEnd, gfxReal &tmin, gfxReal &tmax) const
299 {
300   char   inside = true;   /* inside box? */
301   char   hit    = false;  /* ray hits box? */
302   int    whichPlane;      /* intersection plane */
303   gfxReal candPlane[NUMDIM]; /* candidate plane */
304   gfxReal quadrant[NUMDIM];  /* quadrants */
305   gfxReal maxT[NUMDIM];    /* max intersection T for planes */
306   ntlVec3Gfx  coord;           /* intersection point */
307   ntlVec3Gfx  dir = mDirection;
308   ntlVec3Gfx  origin = mOrigin;
309   gfxReal t = GFX_REAL_MAX;
310
311   /* check intersection planes for AABB */
312   for(int i=0;i<NUMDIM;i++) {
313     if(origin[i] < mStart[i]) {
314       quadrant[i] = LEFT;
315       candPlane [i] = mStart[i];
316       inside = false;
317     } else if(origin[i] > mEnd[i]) {
318       quadrant[i] = RIGHT;
319       candPlane[i] = mEnd[i];
320       inside = false;
321     } else {
322       /* intersect with backside */
323       if(dir[i] > 0) {
324                                 quadrant[i] = LEFT;
325                                 candPlane [i] = mStart[i];
326       } else 
327                                 if(dir[i] < 0) {
328                                         quadrant[i] = RIGHT;
329                                         candPlane[i] = mEnd[i];
330                                 } else {
331                                         quadrant[i] = MIDDLE;
332                                 }
333     }
334   }
335
336   /* get t distances to planes */
337   for(int i=0;i<NUMDIM;i++) {
338     if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
339       maxT[i] = (candPlane[i] - origin[i]) / dir[i];
340     } else {
341       maxT[i] = GFX_REAL_MAX;
342     }
343   }
344  
345   /* largest max t */
346   whichPlane = 0;
347   for(int i=1;i<NUMDIM;i++) {
348     if( ((maxT[whichPlane] < maxT[i])&&(maxT[i]!=GFX_REAL_MAX)) ||
349                                 (maxT[whichPlane]==GFX_REAL_MAX) )
350       whichPlane = i;
351   }
352     
353   /* check final candidate */
354   hit  = true;
355   if(maxT[whichPlane]<GFX_REAL_MAX) {
356     for(int i=0;i<NUMDIM;i++) {
357       if(whichPlane != i) {
358                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
359                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
360                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
361                                         /* no hit... */
362                                         hit  = false;
363                                 } 
364       }
365       else { coord[i] = candPlane[i];   }      
366     }
367
368     /* AABB hit... */
369     if( hit ) {
370       t = maxT[whichPlane];     
371     }
372   }
373   tmin = t;
374
375   /* now the backside */
376   t = GFX_REAL_MAX;
377   for(int i=0;i<NUMDIM;i++) {
378     if(origin[i] < mStart[i]) {
379       quadrant[i] = LEFT;
380       candPlane [i] = mEnd[i];
381     } else if(origin[i] > mEnd[i]) {
382       quadrant[i] = RIGHT;
383       candPlane[i] = mStart[i];
384     } else {
385       if(dir[i] > 0) {
386                                 quadrant[i] = LEFT;
387                                 candPlane [i] = mEnd[i];
388       } else 
389                                 if(dir[i] < 0) {
390                                         quadrant[i] = RIGHT;
391                                         candPlane[i] = mStart[i];
392                                 } else {
393                                         quadrant[i] = MIDDLE;
394                                 }
395     }
396   }
397
398
399   /* get t distances to planes */
400   for(int i=0;i<NUMDIM;i++) {
401     if((quadrant[i] != MIDDLE) && (fabs(dir[i]) > getVecEpsilon()) ) {
402       maxT[i] = (candPlane[i] - origin[i]) / dir[i];
403     } else {
404       maxT[i] = GFX_REAL_MAX;
405     }
406   }
407     
408   /* smallest max t */
409   whichPlane = 0;
410   for(int i=1;i<NUMDIM;i++) {
411     if(maxT[whichPlane] > maxT[i]) whichPlane = i;
412   }
413     
414   /* check final candidate */
415   hit  = true;
416   if(maxT[whichPlane] != GFX_REAL_MAX) {
417
418     for(int i=0;i<NUMDIM;i++) {
419       if(whichPlane != i) {
420                                 coord[i] = origin[i] + maxT[whichPlane] * dir[i];
421                                 if( (coord[i] < mStart[i]-getVecEpsilon() ) || 
422                                                 (coord[i] > mEnd[i]  +getVecEpsilon() )  ) {
423                                         /* no hit... */
424                                         hit  = false;
425                                 } 
426       }
427       else {
428                                 coord[i] = candPlane[i];
429       }      
430     }
431
432     /* AABB hit... */
433     if( hit ) {
434       t = maxT[whichPlane];
435     }
436   }
437    
438   tmax = t;
439 }
440
441
442
443 /******************************************************************************
444  * Determine color of this ray by tracing through the scene
445  *****************************************************************************/
446 const ntlColor ntlRay::shade() //const
447 {
448   ntlGeometryObject           *closest = NULL;
449   gfxReal                      minT = GFX_REAL_MAX;
450   vector<ntlLightObject*>     *lightlist = mpGlob->getLightList();
451   mpGlob->setCounterShades( mpGlob->getCounterShades()+1 );
452         bool intersectionInside = 0;
453         if(mpGlob->getDebugOut() > 5) errorOut(std::endl<<"New Ray: depth "<<mDepth<<", org "<<mOrigin<<", dir "<<mDirection );
454
455         /* check if this ray contributes enough */
456         if(mContribution <= RAY_MINCONTRIB) {
457                 //return ntlColor(0.0);
458         }
459
460         
461   /* find closes object that intersects */
462         ntlTriangle *tri = NULL;
463         ntlVec3Gfx normal;
464         mpGlob->getScene()->intersectScene(*this, minT, normal, tri, 0);
465         if(minT>0) {
466                 closest = mpGlob->getScene()->getObject( tri->getObjectId() );
467         }
468
469   /* object hit... */
470   if (closest != NULL) {
471                 //return( ntlColor(1.0) );
472                 //normal = tri->getNormal(); // no normal smoothing
473
474                 ntlVec3Gfx triangleNormal = tri->getNormal();
475                 if( equal(triangleNormal, ntlVec3Gfx(0.0)) ) errorOut("ntlRaytracer warning: trinagle normal= 0 "); // DEBUG
476                 /* intersection on inside faces? if yes invert normal afterwards */
477                 gfxReal valDN; // = mDirection | normal;
478                 valDN = dot(mDirection, triangleNormal);
479                 if( valDN > 0.0) {
480                         intersectionInside = 1;
481                         normal = normal * -1.0;
482                         triangleNormal = triangleNormal * -1.0;
483                 } 
484
485     /* ... -> do reflection */
486     ntlVec3Gfx intersectionPosition(mOrigin + (mDirection * (minT)) );
487     ntlMaterial *clossurf = closest->getMaterial();
488                 if(mpGlob->getDebugOut() > 5) {
489                         errorOut("Ray hit: at "<<intersectionPosition<<" n:"<<normal<<"    dn:"<<valDN<<" ins:"<<intersectionInside<<"  cl:"<<((unsigned int)closest) ); 
490                         errorOut(" t1:"<<mpGlob->getScene()->getVertex(tri->getPoints()[0])<<" t2:"<<mpGlob->getScene()->getVertex(tri->getPoints()[1])<<" t3:"<<mpGlob->getScene()->getVertex(tri->getPoints()[2]) ); 
491                         errorOut(" trin:"<<tri->getNormal() );
492                 }
493
494                 /* current transparence and reflectivity */
495                 gfxReal currTrans = clossurf->getTransparence();
496                 gfxReal currRefl = clossurf->getMirror();
497
498     /* correct intersectopm position */
499     intersectionPosition += ( triangleNormal*getVecEpsilon() );
500                 /* reflection at normal */
501                 ntlVec3Gfx reflectedDir = getNormalized( reflectVector(mDirection, normal) );
502                 int badRefl = 0;
503                 if(dot(reflectedDir, triangleNormal)<0.0 ) {
504                         badRefl = 1;
505                         if(mpGlob->getDebugOut() > 5) { errorOut("Ray Bad reflection...!"); }
506                 }
507
508                 /* refraction direction, depending on in/outside hit */
509                 ntlVec3Gfx refractedDir;
510                 int refRefl = 0;
511                 /* refraction at normal is handled by inverting normal before */
512                 gfxReal myRefIndex = 1.0;
513                 if((currTrans>RAY_THRESHOLD)||(clossurf->getFresnel())) {
514                         if(intersectionInside) {
515                                 myRefIndex = 1.0/clossurf->getRefracIndex();
516                         } else {
517                                 myRefIndex = clossurf->getRefracIndex();
518                         }
519
520                         refractedDir = refractVector(mDirection, normal, myRefIndex , (gfxReal)(1.0) /* global ref index */, refRefl);
521                 }
522
523                 /* calculate fresnel? */
524                 if(clossurf->getFresnel()) {
525                         // for total reflection, just set trans to 0
526                         if(refRefl) {
527                                 currRefl = 1.0; currTrans = 0.0;
528                         } else {
529                                 // calculate fresnel coefficients
530                                 clossurf->calculateFresnel( mDirection, normal, myRefIndex, currRefl,currTrans );
531                         }
532                 }
533
534     ntlRay reflectedRay(intersectionPosition, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
535                 reflectedRay.setNormal( normal );
536     ntlColor currentColor(0.0);
537     ntlColor highlightColor(0.0);
538
539     /* first add reflected ambient color */
540     currentColor += (clossurf->getAmbientRefl() * mpGlob->getAmbientLight() );
541
542     /* calculate lighting, not on the insides of objects... */
543                 if(!intersectionInside) {
544                         for (vector<ntlLightObject*>::iterator iter = lightlist->begin();
545                                          iter != lightlist->end();
546                                          iter++) {
547                                 
548                                 /* let light illuminate point */
549                                 currentColor += (*iter)->illuminatePoint( reflectedRay, closest, highlightColor );
550                                 
551                         } // for all lights
552                 }
553
554     // recurse ?
555     if ((mDepth < mpGlob->getRayMaxDepth() )&&(currRefl>RAY_THRESHOLD)) {
556
557                                 if(badRefl) {
558                                         ntlVec3Gfx intersectionPosition2;
559                                         ntlGeometryObject           *closest2 = NULL;
560                                         gfxReal                      minT2 = GFX_REAL_MAX;
561                                         ntlTriangle *tri2 = NULL;
562                                         ntlVec3Gfx normal2;
563
564                                         ntlVec3Gfx refractionPosition2(mOrigin + (mDirection * minT) );
565                                         refractionPosition2 -= (triangleNormal*getVecEpsilon() );
566
567                                         ntlRay reflectedRay2 = ntlRay(refractionPosition2, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
568                                         mpGlob->getScene()->intersectScene(reflectedRay2, minT2, normal2, tri2, 0);
569                                         if(minT2>0) {
570                                                 closest2 = mpGlob->getScene()->getObject( tri2->getObjectId() );
571                                         }
572
573                                         /* object hit... */
574                                         if (closest2 != NULL) {
575                                                 ntlVec3Gfx triangleNormal2 = tri2->getNormal();
576                                                 gfxReal valDN2; 
577                                                 valDN2 = dot(reflectedDir, triangleNormal2);
578                                                 if( valDN2 > 0.0) {
579                                                         triangleNormal2 = triangleNormal2 * -1.0;
580                                                         intersectionPosition2 = ntlVec3Gfx(intersectionPosition + (reflectedDir * (minT2)) );
581                                                         /* correct intersection position and create new reflected ray */
582                                                         intersectionPosition2 += ( triangleNormal2*getVecEpsilon() );
583                                                         reflectedRay = ntlRay(intersectionPosition2, reflectedDir, mDepth+1, mContribution*currRefl, mpGlob);
584                                                 } else { 
585                                                         // ray seems to work, continue normally ?
586                                                 }
587
588                                         }
589
590                                 }
591
592       // add mirror color multiplied by mirror factor of surface
593                         if(mpGlob->getDebugOut() > 5) errorOut("Reflected ray from depth "<<mDepth<<", dir "<<reflectedDir );
594                         ntlColor reflectedColor = reflectedRay.shade() * currRefl;
595                         currentColor += reflectedColor;
596     }
597
598     /* Trace another ray on for transparent objects */
599     if(currTrans > RAY_THRESHOLD) {
600
601       //gfxReal refrac_dn = mDirection | normal;
602       /* position at the other side of the surface, along ray */
603       ntlVec3Gfx refraction_position(mOrigin + (mDirection * minT) );
604       refraction_position += (mDirection * getVecEpsilon());
605                         refraction_position -= (triangleNormal*getVecEpsilon() );
606                         
607       ntlColor refracCol(0.0);         /* refracted color */
608                         
609
610       /* trace refracted ray */
611       ntlRay transRay(refraction_position, refractedDir, mDepth+1, mContribution*currTrans, mpGlob);
612       transRay.setRefracted(1);
613                         transRay.setNormal( normal );
614       if(mDepth < mpGlob->getRayMaxDepth() ) {
615                                 if(!refRefl) {
616                                         if(mpGlob->getDebugOut() > 5) errorOut("Refracted ray from depth "<<mDepth<<", dir "<<refractedDir );
617                                         refracCol = transRay.shade();
618                                 } else { 
619                                         //we shouldnt reach this!
620                                         if(mpGlob->getDebugOut() > 5) errorOut("Fully reflected ray from depth "<<mDepth<<", dir "<<reflectedDir );
621                                         refracCol = reflectedRay.shade();
622                                 }
623       }
624
625       /* calculate color */
626       /* additive transparency "light amplification" */
627       ntlColor add_col = currentColor + refracCol * currTrans;
628       /* subtractive transparency, more realistic */
629       ntlColor sub_col = (refracCol * currTrans) + 
630         ( currentColor * (1.0-currTrans) );
631
632       /* mix additive and subtractive */
633                         add_col += sub_col;
634                         currentColor += (refracCol * currTrans);
635
636     }
637
638
639                 /* add highlights (should not be affected by transparence as the diffuse reflections */
640                 currentColor += highlightColor;
641
642                 /* attentuate as a last step*/
643     //if(currTrans > RAY_THRESHOLD) {
644                 /* check if we're on the inside or outside */
645                 if(intersectionInside) {
646                         gfxReal kr,kg,kb;    /* attentuation */
647                         /* calculate attentuation */
648                         ntlColor attCol = clossurf->getTransAttCol();
649                         kr = exp( attCol[0] * minT );
650                         kg = exp( attCol[1] * minT );
651                         kb = exp( attCol[2] * minT );
652                         currentColor = currentColor * ntlColor(kr,kg,kb);
653                 }
654
655     /* done... */
656                 if(mpGlob->getDebugOut() > 5) { errorOut("Ray "<<mDepth<<" color "<<currentColor ); }
657     return ntlColor(currentColor);
658   }
659
660   /* no object hit -> ray goes to infinity */
661   return mpGlob->getBackgroundCol(); 
662 }
663
664
665
666
667
668