0024887: Visualization - revise and extend Raytracing controls
[occt.git] / src / Shaders / RaytraceBase.fs
1 //! Normalized pixel coordinates.
2 in vec2 vPixel;
3
4 //! Origin of viewing ray in left-top corner.
5 uniform vec3 uOriginLT;
6 //! Origin of viewing ray in left-bottom corner.
7 uniform vec3 uOriginLB;
8 //! Origin of viewing ray in right-top corner.
9 uniform vec3 uOriginRT;
10 //! Origin of viewing ray in right-bottom corner.
11 uniform vec3 uOriginRB;
12
13 //! Direction of viewing ray in left-top corner.
14 uniform vec3 uDirectLT;
15 //! Direction of viewing ray in left-bottom corner.
16 uniform vec3 uDirectLB;
17 //! Direction of viewing ray in right-top corner.
18 uniform vec3 uDirectRT;
19 //! Direction of viewing ray in right-bottom corner.
20 uniform vec3 uDirectRB;
21
22 //! Texture buffer of data records of high-level BVH nodes.
23 uniform isamplerBuffer uSceneNodeInfoTexture;
24 //! Texture buffer of minimum points of high-level BVH nodes.
25 uniform samplerBuffer uSceneMinPointTexture;
26 //! Texture buffer of maximum points of high-level BVH nodes.
27 uniform samplerBuffer uSceneMaxPointTexture;
28 //! Texture buffer of transformations of high-level BVH nodes.
29 uniform samplerBuffer uSceneTransformTexture;
30
31 //! Texture buffer of data records of bottom-level BVH nodes.
32 uniform isamplerBuffer uObjectNodeInfoTexture;
33 //! Texture buffer of minimum points of bottom-level BVH nodes.
34 uniform samplerBuffer uObjectMinPointTexture;
35 //! Texture buffer of maximum points of bottom-level BVH nodes.
36 uniform samplerBuffer uObjectMaxPointTexture;
37
38 //! Texture buffer of vertex coords.
39 uniform samplerBuffer uGeometryVertexTexture;
40 //! Texture buffer of vertex normals.
41 uniform samplerBuffer uGeometryNormalTexture;
42 //! Texture buffer of triangle indices.
43 uniform isamplerBuffer uGeometryTriangTexture;
44
45 //! Texture buffer of material properties.
46 uniform samplerBuffer uRaytraceMaterialTexture;
47 //! Texture buffer of light source properties.
48 uniform samplerBuffer uRaytraceLightSrcTexture;
49 //! Environment map texture.
50 uniform sampler2D uEnvironmentMapTexture;
51
52 //! Total number of light sources.
53 uniform int uLightCount;
54 //! Intensity of global ambient light.
55 uniform vec4 uGlobalAmbient;
56
57 //! Enables/disables environment map.
58 uniform int uEnvironmentEnable;
59 //! Enables/disables computation of shadows.
60 uniform int uShadowsEnable;
61 //! Enables/disables computation of reflections.
62 uniform int uReflectionsEnable;
63
64 //! Radius of bounding sphere of the scene.
65 uniform float uSceneRadius;
66 //! Scene epsilon to prevent self-intersections.
67 uniform float uSceneEpsilon;
68
69 /////////////////////////////////////////////////////////////////////////////////////////
70 // Specific data types
71   
72 //! Stores ray parameters.
73 struct SRay
74 {
75   vec3 Origin;
76   
77   vec3 Direct;
78 };
79
80 //! Stores intersection parameters.
81 struct SIntersect
82 {
83   float Time;
84   
85   vec2 UV;
86   
87   vec3 Normal;
88 };
89
90 /////////////////////////////////////////////////////////////////////////////////////////
91 // Some useful constants
92
93 #define MAXFLOAT 1e15f
94
95 #define SMALL vec3 (exp2 (-80.0f))
96
97 #define ZERO vec3 (0.0f, 0.0f, 0.0f)
98 #define UNIT vec3 (1.0f, 1.0f, 1.0f)
99
100 #define AXIS_X vec3 (1.0f, 0.0f, 0.0f)
101 #define AXIS_Y vec3 (0.0f, 1.0f, 0.0f)
102 #define AXIS_Z vec3 (0.0f, 0.0f, 1.0f)
103
104
105 // =======================================================================
106 // function : MatrixRowMultiplyDir
107 // purpose  : Multiplies a vector by matrix
108 // =======================================================================
109 vec3 MatrixRowMultiplyDir (in vec3 v,
110                            in vec4 m0,
111                            in vec4 m1,
112                            in vec4 m2)
113 {
114   return vec3 (dot (m0.xyz, v),
115                dot (m1.xyz, v),
116                dot (m2.xyz, v));
117 }
118
119 // =======================================================================
120 // function : MatrixColMultiplyPnt
121 // purpose  : Multiplies a vector by matrix
122 // =======================================================================
123 vec3 MatrixColMultiplyPnt (in vec3 v,
124                            in vec4 m0,
125                            in vec4 m1,
126                            in vec4 m2,
127                            in vec4 m3)
128 {
129   return vec3 (m0[0] * v.x + m1[0] * v.y + m2[0] * v.z + m3[0],
130                m0[1] * v.x + m1[1] * v.y + m2[1] * v.z + m3[1],
131                m0[2] * v.x + m1[2] * v.y + m2[2] * v.z + m3[2]);
132 }
133
134 // =======================================================================
135 // function : MatrixColMultiplyDir
136 // purpose  : Multiplies a vector by matrix
137 // =======================================================================
138 vec3 MatrixColMultiplyDir (in vec3 v,
139                            in vec4 m0,
140                            in vec4 m1,
141                            in vec4 m2,
142                            in vec4 m3)
143 {
144   return vec3 (m0[0] * v.x + m1[0] * v.y + m2[0] * v.z,
145                m0[1] * v.x + m1[1] * v.y + m2[1] * v.z,
146                m0[2] * v.x + m1[2] * v.y + m2[2] * v.z);
147 }
148
149 /////////////////////////////////////////////////////////////////////////////////////////
150 // Functions for compute ray-object intersection
151
152 // =======================================================================
153 // function : GenerateRay
154 // purpose  :
155 // =======================================================================
156 SRay GenerateRay (in vec2 thePixel)
157 {
158   vec3 aP0 = mix (uOriginLB, uOriginRB, thePixel.x);
159   vec3 aP1 = mix (uOriginLT, uOriginRT, thePixel.x);
160
161   vec3 aD0 = mix (uDirectLB, uDirectRB, thePixel.x);
162   vec3 aD1 = mix (uDirectLT, uDirectRT, thePixel.x);
163   
164   return SRay (mix (aP0, aP1, thePixel.y),
165                mix (aD0, aD1, thePixel.y));
166 }
167
168 // =======================================================================
169 // function : IntersectSphere
170 // purpose  : Computes ray-sphere intersection
171 // =======================================================================
172 float IntersectSphere (in SRay theRay, in float theRadius)
173 {
174   float aDdotD = dot (theRay.Direct, theRay.Direct);
175   float aDdotO = dot (theRay.Direct, theRay.Origin);
176   float aOdotO = dot (theRay.Origin, theRay.Origin);
177   
178   float aD = aDdotO * aDdotO - aDdotD * (aOdotO - theRadius * theRadius);
179   
180   if (aD > 0.0f)
181   {
182     float aTime = (sqrt (aD) - aDdotO) * (1.0f / aDdotD);
183     
184     return aTime > 0.0f ? aTime : MAXFLOAT;
185   }
186   
187   return MAXFLOAT;
188 }
189
190 // =======================================================================
191 // function : IntersectTriangle
192 // purpose  : Computes ray-triangle intersection (branchless version)
193 // =======================================================================
194 float IntersectTriangle (in SRay theRay,
195                          in vec3 thePnt0,
196                          in vec3 thePnt1,
197                          in vec3 thePnt2,
198                          out vec2 theUV,
199                          out vec3 theNorm)
200 {
201   vec3 aEdge0 = thePnt1 - thePnt0;
202   vec3 aEdge1 = thePnt0 - thePnt2;
203   
204   theNorm = cross (aEdge1, aEdge0);
205
206   vec3 aEdge2 = (1.0f / dot (theNorm, theRay.Direct)) * (thePnt0 - theRay.Origin);
207   
208   float aTime = dot (theNorm, aEdge2);
209
210   vec3 theVec = cross (theRay.Direct, aEdge2);
211   
212   theUV.x = dot (theVec, aEdge1);
213   theUV.y = dot (theVec, aEdge0);
214   
215   return bool (int(aTime >= 0.0f) &
216                int(theUV.x >= 0.0f) &
217                int(theUV.y >= 0.0f) &
218                int(theUV.x + theUV.y <= 1.0f)) ? aTime : MAXFLOAT;
219 }
220
221 //! Identifies the absence of intersection.
222 #define INALID_HIT ivec4 (-1)
223
224 //! Global stack shared between traversal functions.
225 int Stack[STACK_SIZE];
226
227 // =======================================================================
228 // function : ObjectNearestHit
229 // purpose  : Finds intersection with nearest object triangle
230 // =======================================================================
231 ivec4 ObjectNearestHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffset,
232   in SRay theRay, in vec3 theInverse, inout SIntersect theHit, in int theSentinel)
233 {
234   int aHead = theSentinel;  // stack pointer
235   int aNode = theBVHOffset; // node to visit
236
237   ivec4 aTriIndex = INALID_HIT;
238
239   while (true)
240   {
241     ivec3 aData = texelFetch (uObjectNodeInfoTexture, aNode).xyz;
242
243     if (aData.x == 0) // if inner node
244     {
245       float aTimeOut;
246       float aTimeLft;
247       float aTimeRgh;
248       
249       aData.y += theBVHOffset;
250       aData.z += theBVHOffset;
251   
252       vec3 aNodeMinLft = texelFetch (uObjectMinPointTexture, aData.y).xyz;
253       vec3 aNodeMaxLft = texelFetch (uObjectMaxPointTexture, aData.y).xyz;
254       vec3 aNodeMinRgh = texelFetch (uObjectMinPointTexture, aData.z).xyz;
255       vec3 aNodeMaxRgh = texelFetch (uObjectMaxPointTexture, aData.z).xyz;
256
257       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
258       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
259       
260       vec3 aTimeMax = max (aTime0, aTime1);
261       vec3 aTimeMin = min (aTime0, aTime1);
262
263       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
264       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
265       
266       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
267       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
268
269       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeLft <= theHit.Time);
270
271       aTimeMax = max (aTime0, aTime1);
272       aTimeMin = min (aTime0, aTime1);
273
274       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
275       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
276
277       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theHit.Time);
278
279       if (bool(aHitLft & aHitRgh))
280       {
281         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
282         
283         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
284       }
285       else
286       {
287         if (bool(aHitLft | aHitRgh))
288         {
289           aNode = bool(aHitLft) ? aData.y : aData.z;
290         }
291         else
292         {
293           if (aHead == theSentinel)
294             return aTriIndex;
295             
296           aNode = Stack[aHead--];
297         }
298       }
299     }
300     else // if leaf node
301     {
302       vec3 aNormal;
303       vec2 aParams;
304
305       for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)
306       {
307         ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + theTrgOffset);
308
309         vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x += theVrtOffset).xyz;
310         vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y += theVrtOffset).xyz;
311         vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z += theVrtOffset).xyz;
312
313         float aTime = IntersectTriangle (theRay,
314                                          aPoint0,
315                                          aPoint1,
316                                          aPoint2,
317                                          aParams,
318                                          aNormal);
319                                          
320         if (aTime < theHit.Time)
321         {
322           aTriIndex = aTriangle;
323           
324           theHit = SIntersect (aTime, aParams, aNormal);
325         }
326       }
327       
328       if (aHead == theSentinel)
329         return aTriIndex;
330
331       aNode = Stack[aHead--];
332     }
333   }
334
335   return aTriIndex;
336 }
337
338 #define MATERIAL_AMBN(index) (7 * index + 0)
339 #define MATERIAL_DIFF(index) (7 * index + 1)
340 #define MATERIAL_SPEC(index) (7 * index + 2)
341 #define MATERIAL_EMIS(index) (7 * index + 3)
342 #define MATERIAL_REFL(index) (7 * index + 4)
343 #define MATERIAL_REFR(index) (7 * index + 5)
344 #define MATERIAL_TRAN(index) (7 * index + 6)
345
346 // =======================================================================
347 // function : ObjectAnyHit
348 // purpose  : Finds intersection with any object triangle
349 // =======================================================================
350 float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffset,
351   in SRay theRay, in vec3 theInverse, in float theDistance, in int theSentinel)
352 {
353   int aHead = theSentinel;  // stack pointer
354   int aNode = theBVHOffset; // node to visit
355
356 #ifdef TRANSPARENT_SHADOWS
357   float aFactor = 1.0f;
358 #endif
359
360   while (true)
361   {
362     ivec4 aData = texelFetch (uObjectNodeInfoTexture, aNode);
363
364     if (aData.x == 0) // if inner node
365     {
366       float aTimeOut;
367       float aTimeLft;
368       float aTimeRgh;
369       
370       aData.y += theBVHOffset;
371       aData.z += theBVHOffset;
372   
373       vec3 aNodeMinLft = texelFetch (uObjectMinPointTexture, aData.y).xyz;
374       vec3 aNodeMaxLft = texelFetch (uObjectMaxPointTexture, aData.y).xyz;
375       vec3 aNodeMinRgh = texelFetch (uObjectMinPointTexture, aData.z).xyz;
376       vec3 aNodeMaxRgh = texelFetch (uObjectMaxPointTexture, aData.z).xyz;
377
378       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
379       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
380
381       vec3 aTimeMax = max (aTime0, aTime1);
382       vec3 aTimeMin = min (aTime0, aTime1);
383
384       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
385       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
386       
387       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
388       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
389
390       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeLft <= theDistance);
391
392       aTimeMax = max (aTime0, aTime1);
393       aTimeMin = min (aTime0, aTime1);
394
395       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
396       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
397
398       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theDistance);
399
400       if (bool(aHitLft & aHitRgh))
401       {
402         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
403
404         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
405       }
406       else
407       {
408         if (bool(aHitLft | aHitRgh))
409         {
410           aNode = bool(aHitLft) ? aData.y : aData.z;
411         }
412         else
413         {
414 #ifdef TRANSPARENT_SHADOWS
415           if (aHead == theSentinel)
416             return aFactor;
417 #else
418           if (aHead == theSentinel)
419             return 1.0f;
420 #endif
421
422           aNode = Stack[aHead--];
423         }
424       }
425     }
426     else // if leaf node
427     {
428       vec3 aNormal;
429       vec2 aParams;
430       
431       for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)
432       {
433         ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + theTrgOffset);
434
435         vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x + theVrtOffset).xyz;
436         vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y + theVrtOffset).xyz;
437         vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z + theVrtOffset).xyz;
438
439         float aTime = IntersectTriangle (theRay,
440                                          aPoint0,
441                                          aPoint1,
442                                          aPoint2,
443                                          aParams,
444                                          aNormal);
445
446 #ifdef TRANSPARENT_SHADOWS
447         if (aTime < theDistance)
448         {
449           aFactor *= 1.0f - texelFetch (uRaytraceMaterialTexture, MATERIAL_TRAN (aTriangle.w)).x;
450         }
451 #else
452         if (aTime < theDistance)
453           return 0.0f;
454 #endif
455       }
456       
457 #ifdef TRANSPARENT_SHADOWS
458       if (aHead == theSentinel || aFactor < 0.1f)
459         return aFactor;
460 #else
461       if (aHead == theSentinel)
462         return 1.0f;
463 #endif
464
465       aNode = Stack[aHead--];
466     }
467   }
468
469 #ifdef TRANSPARENT_SHADOWS
470   return aFactor;
471 #else
472   return 1.0f;
473 #endif
474 }
475
476 // =======================================================================
477 // function : SceneNearestHit
478 // purpose  : Finds intersection with nearest scene triangle
479 // =======================================================================
480 ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theObjectId)
481 {
482   int aHead = -1; // stack pointer
483   int aNode =  0; // node to visit
484
485   ivec4 aHitObject = INALID_HIT;
486
487   while (true)
488   {
489     ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
490
491     if (aData.x != 0) // if leaf node
492     {
493       vec3 aNodeMin = texelFetch (uSceneMinPointTexture, aNode).xyz;
494       vec3 aNodeMax = texelFetch (uSceneMaxPointTexture, aNode).xyz;
495       
496       vec3 aTime0 = (aNodeMin - theRay.Origin) * theInverse;
497       vec3 aTime1 = (aNodeMax - theRay.Origin) * theInverse;
498       
499       vec3 aTimes = min (aTime0, aTime1);
500       
501       if (max (aTimes.x, max (aTimes.y, aTimes.z)) < theHit.Time)
502       {
503         // fetch object transformation
504         int anObjectId = aData.x - 1;
505
506         vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0);
507         vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1);
508         vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2);
509         vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 3);
510
511         SRay aTrsfRay = SRay (
512           MatrixColMultiplyPnt (theRay.Origin, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3),
513           MatrixColMultiplyDir (theRay.Direct, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3));
514
515         vec3 aTrsfInverse = 1.0f / max (abs (aTrsfRay.Direct), SMALL);
516
517         aTrsfInverse.x = aTrsfRay.Direct.x < 0.f ? -aTrsfInverse.x : aTrsfInverse.x;
518         aTrsfInverse.y = aTrsfRay.Direct.y < 0.f ? -aTrsfInverse.y : aTrsfInverse.y;
519         aTrsfInverse.z = aTrsfRay.Direct.z < 0.f ? -aTrsfInverse.z : aTrsfInverse.z;
520
521         ivec4 aTriIndex = ObjectNearestHit (
522           aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theHit, aHead);
523
524         if (aTriIndex.x != -1)
525         {
526           aHitObject = ivec4 (aTriIndex.x,  // vertex 0
527                               aTriIndex.y,  // vertex 1
528                               aTriIndex.z,  // vertex 2
529                               aTriIndex.w); // material
530
531           theObjectId = anObjectId;
532         }
533       }
534       
535       if (aHead < 0)
536         return aHitObject;
537             
538       aNode = Stack[aHead--];
539     }
540     else // if inner node
541     {
542       float aTimeOut;
543       float aTimeLft;
544       float aTimeRgh;
545
546       vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
547       vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
548       vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
549       vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
550
551       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
552       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
553
554       vec3 aTimeMax = max (aTime0, aTime1);
555       vec3 aTimeMin = min (aTime0, aTime1);
556
557       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
558       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
559
560       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeLft <= theHit.Time);
561       
562       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
563       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
564
565       aTimeMax = max (aTime0, aTime1);
566       aTimeMin = min (aTime0, aTime1);
567
568       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
569       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
570       
571       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theHit.Time);
572
573       if (bool(aHitLft & aHitRgh))
574       {
575         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
576
577         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
578       }
579       else
580       {
581         if (bool(aHitLft | aHitRgh))
582         {
583           aNode = bool(aHitLft) ? aData.y : aData.z;
584         }
585         else
586         {
587           if (aHead < 0)
588             return aHitObject;
589
590           aNode = Stack[aHead--];
591         }
592       }
593     }
594   }
595   
596   return aHitObject;
597 }
598
599 // =======================================================================
600 // function : SceneAnyHit
601 // purpose  : Finds intersection with any scene triangle
602 // =======================================================================
603 float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance)
604 {
605   int aHead = -1; // stack pointer
606   int aNode =  0; // node to visit
607
608 #ifdef TRANSPARENT_SHADOWS
609   float aFactor = 1.0f;
610 #endif
611
612   while (true)
613   {
614     ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
615
616     if (aData.x != 0) // if leaf node
617     {
618       // fetch object transformation
619       int anObjectId = aData.x - 1;
620
621       vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0);
622       vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1);
623       vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2);
624       vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 3);
625
626       SRay aTrsfRay = SRay (
627         MatrixColMultiplyPnt (theRay.Origin, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3),
628         MatrixColMultiplyDir (theRay.Direct, aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3));
629
630       vec3 aTrsfInverse = 1.0f / max (abs (aTrsfRay.Direct), SMALL);
631
632       aTrsfInverse.x = aTrsfRay.Direct.x < 0.0f ? -aTrsfInverse.x : aTrsfInverse.x;
633       aTrsfInverse.y = aTrsfRay.Direct.y < 0.0f ? -aTrsfInverse.y : aTrsfInverse.y;
634       aTrsfInverse.z = aTrsfRay.Direct.z < 0.0f ? -aTrsfInverse.z : aTrsfInverse.z;
635
636 #ifdef TRANSPARENT_SHADOWS
637       aFactor *= ObjectAnyHit (
638         aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theDistance, aHead);
639
640       if (aHead < 0 || aFactor < 0.1f)
641         return aFactor;
642 #else
643       bool isShadow = 0.0f == ObjectAnyHit (
644         aData.y, aData.z, aData.w, aTrsfRay, aTrsfInverse, theDistance, aHead);
645         
646       if (aHead < 0 || isShadow)
647         return isShadow ? 0.0f : 1.0f;
648 #endif
649             
650       aNode = Stack[aHead--];
651     }
652     else // if inner node
653     {
654       float aTimeOut;
655       float aTimeLft;
656       float aTimeRgh;
657
658       vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
659       vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
660       vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
661       vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
662       
663       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
664       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
665
666       vec3 aTimeMax = max (aTime0, aTime1);
667       vec3 aTimeMin = min (aTime0, aTime1);
668
669       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
670       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
671
672       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeLft <= theDistance);
673       
674       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
675       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
676
677       aTimeMax = max (aTime0, aTime1);
678       aTimeMin = min (aTime0, aTime1);
679
680       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
681       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
682       
683       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.0f) & int(aTimeRgh <= theDistance);
684
685       if (bool(aHitLft & aHitRgh))
686       {
687         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
688
689         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
690       }
691       else
692       {
693         if (bool(aHitLft | aHitRgh))
694         {
695           aNode = bool(aHitLft) ? aData.y : aData.z;
696         }
697         else
698         {
699 #ifdef TRANSPARENT_SHADOWS
700           if (aHead < 0)
701             return aFactor;
702 #else
703           if (aHead < 0)
704             return 1.0f;
705 #endif
706
707           aNode = Stack[aHead--];
708         }
709       }
710     }
711   }
712   
713 #ifdef TRANSPARENT_SHADOWS
714   return aFactor;
715 #else
716   return 1.0f;
717 #endif
718 }
719
720 #define PI 3.1415926f
721
722 // =======================================================================
723 // function : Latlong
724 // purpose  : Converts world direction to environment texture coordinates
725 // =======================================================================
726 vec2 Latlong (in vec3 thePoint, in float theRadius)
727 {
728   float aPsi = acos (-thePoint.z / theRadius);
729   
730   float aPhi = atan (thePoint.y, thePoint.x) + PI;
731   
732   return vec2 (aPhi * 0.1591549f,
733                aPsi * 0.3183098f);
734 }
735
736 // =======================================================================
737 // function : SmoothNormal
738 // purpose  : Interpolates normal across the triangle
739 // =======================================================================
740 vec3 SmoothNormal (in vec2 theUV, in ivec4 theTriangle)
741 {
742   vec3 aNormal0 = texelFetch (uGeometryNormalTexture, theTriangle.x).xyz;
743   vec3 aNormal1 = texelFetch (uGeometryNormalTexture, theTriangle.y).xyz;
744   vec3 aNormal2 = texelFetch (uGeometryNormalTexture, theTriangle.z).xyz;
745   
746   return normalize (aNormal1 * theUV.x +
747                     aNormal2 * theUV.y +
748                     aNormal0 * (1.0f - theUV.x - theUV.y));
749 }
750
751 // =======================================================================
752 // function : Refract
753 // purpose  : Computes refraction ray (also handles TIR)
754 // =======================================================================
755 vec3 Refract (in vec3 theInput,
756               in vec3 theNormal,
757               in float theRefractIndex,
758               in float theInvRefractIndex)
759 {
760   float aNdotI  = dot (theInput, theNormal);
761   
762   float anIndex = aNdotI < 0.0f
763                 ? theInvRefractIndex
764                 : theRefractIndex;
765                 
766   float aSquare = anIndex * anIndex * (1.0f - aNdotI * aNdotI);
767   
768   if (aSquare > 1.0f)
769   {
770     return reflect (theInput, theNormal);
771   }
772   
773   float aNdotT = sqrt (1.0f - aSquare);
774   
775   return normalize (anIndex * theInput -
776     (anIndex * aNdotI + (aNdotI < 0.0f ? aNdotT : -aNdotT)) * theNormal);
777 }
778
779 #define THRESHOLD vec3 (0.1f)
780
781 #define LIGHT_POS(index) (2 * index + 1)
782 #define LIGHT_PWR(index) (2 * index + 0)
783
784 // =======================================================================
785 // function : Radiance
786 // purpose  : Computes color of specified ray
787 // =======================================================================
788 vec4 Radiance (in SRay theRay, in vec3 theInverse)
789 {
790   vec3 aResult = vec3 (0.0f);
791   vec4 aWeight = vec4 (1.0f);
792
793   int anObjectId;
794   
795   for (int aDepth = 0; aDepth < TRACE_DEPTH; ++aDepth)
796   {
797     SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO);
798     
799     ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, anObjectId);
800
801     if (aTriIndex.x == -1)
802     {
803       if (aWeight.w != 0.0f)
804       {
805         return vec4 (aResult.x,
806                      aResult.y,
807                      aResult.z,
808                      aWeight.w);
809       }
810
811       if (bool(uEnvironmentEnable))
812       {
813         float aTime = IntersectSphere (theRay, uSceneRadius);
814         
815         aResult.xyz += aWeight.xyz * textureLod (uEnvironmentMapTexture,
816           Latlong (theRay.Direct * aTime + theRay.Origin, uSceneRadius), 0.0f).xyz;
817       }
818       
819       return vec4 (aResult.x,
820                    aResult.y,
821                    aResult.z,
822                    aWeight.w);
823     }
824     
825     vec3 aPoint = theRay.Direct * aHit.Time + theRay.Origin;
826     
827     vec3 aAmbient  = texelFetch (
828       uRaytraceMaterialTexture, MATERIAL_AMBN (aTriIndex.w)).rgb;
829     vec3 aDiffuse  = texelFetch (
830       uRaytraceMaterialTexture, MATERIAL_DIFF (aTriIndex.w)).rgb;
831     vec4 aSpecular = texelFetch (
832       uRaytraceMaterialTexture, MATERIAL_SPEC (aTriIndex.w));
833     vec4 aOpacity  = texelFetch (
834       uRaytraceMaterialTexture, MATERIAL_TRAN (aTriIndex.w));
835       
836     vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);
837
838     vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0);
839     vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1);
840     vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2);
841
842     aNormal = normalize (MatrixRowMultiplyDir (
843       aNormal, aInvTransf0, aInvTransf1, aInvTransf2));
844     
845     aHit.Normal = normalize (aHit.Normal);
846     
847     for (int aLightIdx = 0; aLightIdx < uLightCount; ++aLightIdx)
848     {
849       vec4 aLight = texelFetch (
850         uRaytraceLightSrcTexture, LIGHT_POS (aLightIdx));
851       
852       float aDistance = MAXFLOAT;
853       
854       if (aLight.w != 0.0f) // point light source
855       {
856         aDistance = length (aLight.xyz -= aPoint);
857         
858         aLight.xyz *= 1.0f / aDistance;
859       }
860
861       SRay aShadow = SRay (aPoint + aLight.xyz * uSceneEpsilon, aLight.xyz);
862       
863       aShadow.Origin += aHit.Normal * uSceneEpsilon *
864         (dot (aHit.Normal, aLight.xyz) >= 0.0f ? 1.0f : -1.0f);
865       
866       float aVisibility = 1.0f;
867      
868       if (bool(uShadowsEnable))
869       {
870         vec3 aInverse = 1.0f / max (abs (aLight.xyz), SMALL);
871         
872         aInverse.x = aLight.x < 0.0f ? -aInverse.x : aInverse.x;
873         aInverse.y = aLight.y < 0.0f ? -aInverse.y : aInverse.y;
874         aInverse.z = aLight.z < 0.0f ? -aInverse.z : aInverse.z;
875         
876         aVisibility = SceneAnyHit (aShadow, aInverse, aDistance);
877       }
878       
879       if (aVisibility > 0.0f)
880       {
881         vec3 aIntensity = vec3 (texelFetch (
882           uRaytraceLightSrcTexture, LIGHT_PWR (aLightIdx)));
883  
884         float aLdotN = dot (aShadow.Direct, aNormal);
885         
886         if (aOpacity.y > 0.0f)   // force two-sided lighting
887           aLdotN = abs (aLdotN); // for transparent surfaces
888           
889         if (aLdotN > 0.0f)
890         {
891           float aRdotV = dot (reflect (aShadow.Direct, aNormal), theRay.Direct);
892           
893           aResult.xyz += aWeight.xyz * (aOpacity.x * aVisibility) * aIntensity *
894             (aDiffuse * aLdotN + aSpecular.xyz * pow (max (0.0f, aRdotV), aSpecular.w));
895         }
896       }
897     }
898     
899     aResult.xyz += aWeight.xyz * uGlobalAmbient.xyz *
900       aAmbient * aOpacity.x * max (abs (dot (aNormal, theRay.Direct)), 0.5f);
901     
902     if (aOpacity.x != 1.0f)
903     {
904       aWeight *= aOpacity.y;
905       
906       if (aOpacity.z != 1.0f)
907       {
908         theRay.Direct = Refract (theRay.Direct, aNormal, aOpacity.z, aOpacity.w);
909
910         theInverse = 1.0f / max (abs (theRay.Direct), SMALL);
911         
912         theInverse.x = theRay.Direct.x < 0.0f ? -theInverse.x : theInverse.x;
913         theInverse.y = theRay.Direct.y < 0.0f ? -theInverse.y : theInverse.y;
914         theInverse.z = theRay.Direct.z < 0.0f ? -theInverse.z : theInverse.z;
915
916         aPoint += aHit.Normal * (dot (aHit.Normal, theRay.Direct) >= 0.0f ? uSceneEpsilon : -uSceneEpsilon);     
917       }
918     }
919     else
920     {
921       aWeight *= bool(uReflectionsEnable) ?
922         texelFetch (uRaytraceMaterialTexture, MATERIAL_REFL (aTriIndex.w)) : vec4 (0.0f);
923       
924       theRay.Direct = reflect (theRay.Direct, aNormal);
925       
926       if (dot (theRay.Direct, aHit.Normal) < 0.0f)
927       {
928         theRay.Direct = reflect (theRay.Direct, aHit.Normal);      
929       }
930
931       theInverse = 1.0f / max (abs (theRay.Direct), SMALL);
932       
933       theInverse.x = theRay.Direct.x < 0.0f ? -theInverse.x : theInverse.x;
934       theInverse.y = theRay.Direct.y < 0.0f ? -theInverse.y : theInverse.y;
935       theInverse.z = theRay.Direct.z < 0.0f ? -theInverse.z : theInverse.z;
936       
937       aPoint += aHit.Normal * (dot (aHit.Normal, theRay.Direct) >= 0.0f ? uSceneEpsilon : -uSceneEpsilon);
938     }
939     
940     if (all (lessThanEqual (aWeight.xyz, THRESHOLD)))
941     {
942       return vec4 (aResult.x,
943                    aResult.y,
944                    aResult.z,
945                    aWeight.w);
946     }
947     
948     theRay.Origin = theRay.Direct * uSceneEpsilon + aPoint;
949   }
950
951   return vec4 (aResult.x,
952                aResult.y,
953                aResult.z,
954                aWeight.w);
955 }