1050478d539174f5b617846c8f249a8becf83024
[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.f))
96
97 #define ZERO vec3 (0.f, 0.f, 0.f)
98 #define UNIT vec3 (1.f, 1.f, 1.f)
99
100 #define AXIS_X vec3 (1.f, 0.f, 0.f)
101 #define AXIS_Y vec3 (0.f, 1.f, 0.f)
102 #define AXIS_Z vec3 (0.f, 0.f, 1.f)
103
104
105 // =======================================================================
106 // function : MatrixRowMultiply
107 // purpose  : Multiplies a vector by matrix
108 // =======================================================================
109 vec3 MatrixRowMultiply (in vec4 v,
110                         in vec4 m0,
111                         in vec4 m1,
112                         in vec4 m2,
113                         in vec4 m3)
114 {
115   return vec3 (dot (m0, v),
116                dot (m1, v),
117                dot (m2, v));
118 }
119
120
121 // =======================================================================
122 // function : MatrixColMultiply
123 // purpose  : Multiplies a vector by matrix
124 // =======================================================================
125 vec3 MatrixColMultiply (in vec4 v,
126                         in vec4 m0,
127                         in vec4 m1,
128                         in vec4 m2,
129                         in vec4 m3)
130 {
131   return vec3 (m0[0] * v.x + m1[0] * v.y + m2[0] * v.z + m3[0] * v.w,
132                m0[1] * v.x + m1[1] * v.y + m2[1] * v.z + m3[1] * v.w,
133                m0[2] * v.x + m1[2] * v.y + m2[2] * v.z + m3[2] * v.w);
134 }
135
136 /////////////////////////////////////////////////////////////////////////////////////////
137 // Functions for compute ray-object intersection
138
139 // =======================================================================
140 // function : GenerateRay
141 // purpose  :
142 // =======================================================================
143 SRay GenerateRay (in vec2 thePixel)
144 {
145   vec3 aP0 = mix (uOriginLB, uOriginRB, thePixel.x);
146   vec3 aP1 = mix (uOriginLT, uOriginRT, thePixel.x);
147
148   vec3 aD0 = mix (uDirectLB, uDirectRB, thePixel.x);
149   vec3 aD1 = mix (uDirectLT, uDirectRT, thePixel.x);
150   
151   return SRay (mix (aP0, aP1, thePixel.y),
152                mix (aD0, aD1, thePixel.y));
153 }
154
155 // =======================================================================
156 // function : IntersectSphere
157 // purpose  : Computes ray-sphere intersection
158 // =======================================================================
159 float IntersectSphere (in SRay theRay, in float theRadius)
160 {
161   float aDdotD = dot (theRay.Direct, theRay.Direct);
162   float aDdotO = dot (theRay.Direct, theRay.Origin);
163   float aOdotO = dot (theRay.Origin, theRay.Origin);
164   
165   float aD = aDdotO * aDdotO - aDdotD * (aOdotO - theRadius * theRadius);
166   
167   if (aD > 0.f)
168   {
169     float aTime = (sqrt (aD) - aDdotO) * (1.f / aDdotD);
170     
171     return aTime > 0.f ? aTime : MAXFLOAT;
172   }
173   
174   return MAXFLOAT;
175 }
176
177 // =======================================================================
178 // function : IntersectTriangle
179 // purpose  : Computes ray-triangle intersection (branchless version)
180 // =======================================================================
181 float IntersectTriangle (in SRay theRay,
182                          in vec3 thePnt0,
183                          in vec3 thePnt1,
184                          in vec3 thePnt2,
185                          out vec2 theUV,
186                          out vec3 theNorm)
187 {
188   vec3 aEdge0 = thePnt1 - thePnt0;
189   vec3 aEdge1 = thePnt0 - thePnt2;
190   
191   theNorm = cross (aEdge1, aEdge0);
192
193   vec3 aEdge2 = (1.f / dot (theNorm, theRay.Direct)) * (thePnt0 - theRay.Origin);
194   
195   float aTime = dot (theNorm, aEdge2);
196
197   vec3 theVec = cross (theRay.Direct, aEdge2);
198   
199   theUV.x = dot (theVec, aEdge1);
200   theUV.y = dot (theVec, aEdge0);
201   
202   return bool (int(aTime >= 0.f) &
203                int(theUV.x >= 0.f) &
204                int(theUV.y >= 0.f) &
205                int(theUV.x + theUV.y <= 1.f)) ? aTime : MAXFLOAT;
206 }
207
208 //! Identifies the absence of intersection.
209 #define INALID_HIT ivec4 (-1)
210
211 //! Global stack shared between traversal functions.
212 int Stack[STACK_SIZE];
213
214 // =======================================================================
215 // function : ObjectNearestHit
216 // purpose  : Finds intersection with nearest object triangle
217 // =======================================================================
218 ivec4 ObjectNearestHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffset,
219   in SRay theRay, in vec3 theInverse, inout SIntersect theHit, in int theSentinel)
220 {
221   int aHead = theSentinel; // stack pointer
222   int aNode = 0;           // node to visit
223
224   ivec4 aTriIndex = INALID_HIT;
225
226   float aTimeOut;
227   float aTimeLft;
228   float aTimeRgh;
229
230   while (true)
231   {
232     ivec3 aData = texelFetch (uObjectNodeInfoTexture, aNode + theBVHOffset).xyz;
233
234     if (aData.x == 0) // if inner node
235     {
236       vec3 aNodeMinLft = texelFetch (uObjectMinPointTexture, aData.y + theBVHOffset).xyz;
237       vec3 aNodeMaxLft = texelFetch (uObjectMaxPointTexture, aData.y + theBVHOffset).xyz;
238       vec3 aNodeMinRgh = texelFetch (uObjectMinPointTexture, aData.z + theBVHOffset).xyz;
239       vec3 aNodeMaxRgh = texelFetch (uObjectMaxPointTexture, aData.z + theBVHOffset).xyz;
240
241       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
242       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
243       
244       vec3 aTimeMax = max (aTime0, aTime1);
245       vec3 aTimeMin = min (aTime0, aTime1);
246
247       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
248       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
249       
250       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
251       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
252
253       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeLft <= theHit.Time);
254
255       aTimeMax = max (aTime0, aTime1);
256       aTimeMin = min (aTime0, aTime1);
257
258       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
259       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
260
261       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeRgh <= theHit.Time);
262
263       if (bool(aHitLft & aHitRgh))
264       {
265         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
266         
267         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
268       }
269       else
270       {
271         if (bool(aHitLft | aHitRgh))
272         {
273           aNode = bool(aHitLft) ? aData.y : aData.z;
274         }
275         else
276         {
277           if (aHead == theSentinel)
278             return aTriIndex;
279             
280           aNode = Stack[aHead--];
281         }
282       }
283     }
284     else // if leaf node
285     {
286       vec3 aNormal;
287       vec2 aParams;
288
289       for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)
290       {
291         ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + theTrgOffset);
292
293         vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x + theVrtOffset).xyz;
294         vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y + theVrtOffset).xyz;
295         vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z + theVrtOffset).xyz;
296
297         float aTime = IntersectTriangle (theRay,
298                                          aPoint0,
299                                          aPoint1,
300                                          aPoint2,
301                                          aParams,
302                                          aNormal);
303                                          
304         if (aTime < theHit.Time)
305         {
306           aTriIndex = aTriangle;
307           
308           theHit = SIntersect (aTime, aParams, aNormal);
309         }
310       }
311       
312       if (aHead == theSentinel)
313         return aTriIndex;
314
315       aNode = Stack[aHead--];
316     }
317   }
318
319   return aTriIndex;
320 }
321
322 // =======================================================================
323 // function : ObjectAnyHit
324 // purpose  : Finds intersection with any object triangle
325 // =======================================================================
326 float ObjectAnyHit (in int theBVHOffset, in int theVrtOffset, in int theTrgOffset,
327   in SRay theRay, in vec3 theInverse, in float theDistance, in int theSentinel)
328 {
329   int aHead = theSentinel; // stack pointer
330   int aNode = 0;           // node to visit
331
332   float aTimeOut;
333   float aTimeLft;
334   float aTimeRgh;
335
336   while (true)
337   {
338     ivec4 aData = texelFetch (uObjectNodeInfoTexture, aNode + theBVHOffset);
339
340     if (aData.x == 0) // if inner node
341     {
342       vec3 aNodeMinLft = texelFetch (uObjectMinPointTexture, aData.y + theBVHOffset).xyz;
343       vec3 aNodeMaxLft = texelFetch (uObjectMaxPointTexture, aData.y + theBVHOffset).xyz;
344       vec3 aNodeMinRgh = texelFetch (uObjectMinPointTexture, aData.z + theBVHOffset).xyz;
345       vec3 aNodeMaxRgh = texelFetch (uObjectMaxPointTexture, aData.z + theBVHOffset).xyz;
346
347       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
348       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
349
350       vec3 aTimeMax = max (aTime0, aTime1);
351       vec3 aTimeMin = min (aTime0, aTime1);
352
353       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
354       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
355       
356       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
357       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
358
359       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeLft <= theDistance);
360
361       aTimeMax = max (aTime0, aTime1);
362       aTimeMin = min (aTime0, aTime1);
363
364       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
365       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
366
367       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeRgh <= theDistance);
368
369       if (bool(aHitLft & aHitRgh))
370       {
371         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
372
373         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
374       }
375       else
376       {
377         if (bool(aHitLft | aHitRgh))
378         {
379           aNode = bool(aHitLft) ? aData.y : aData.z;
380         }
381         else
382         {
383           if (aHead == theSentinel)
384             return 1.f;
385
386           aNode = Stack[aHead--];
387         }
388       }
389     }
390     else // if leaf node
391     {
392       vec3 aNormal;
393       vec2 aParams;
394       
395       for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)
396       {
397         ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + theTrgOffset);
398
399         vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x + theVrtOffset).xyz;
400         vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y + theVrtOffset).xyz;
401         vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z + theVrtOffset).xyz;
402
403         float aTime = IntersectTriangle (theRay,
404                                          aPoint0,
405                                          aPoint1,
406                                          aPoint2,
407                                          aParams,
408                                          aNormal);
409                                          
410         if (aTime < theDistance)
411           return 0.f;
412       }
413       
414       if (aHead == theSentinel)
415         return 1.f;
416
417       aNode = Stack[aHead--];
418     }
419   }
420
421   return 1.f;
422 }
423
424 // =======================================================================
425 // function : SceneNearestHit
426 // purpose  : Finds intersection with nearest scene triangle
427 // =======================================================================
428 ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theObjectId)
429 {
430   int aHead = -1; // stack pointer
431   int aNode =  0; // node to visit
432
433   ivec4 aHitObject = INALID_HIT;
434   
435   float aTimeOut;
436   float aTimeLft;
437   float aTimeRgh;
438
439   while (true)
440   {
441     ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
442
443     if (aData.x != 0) // if leaf node
444     {
445       vec3 aNodeMin = texelFetch (uSceneMinPointTexture, aNode).xyz;
446       vec3 aNodeMax = texelFetch (uSceneMaxPointTexture, aNode).xyz;
447       
448       vec3 aTime0 = (aNodeMin - theRay.Origin) * theInverse;
449       vec3 aTime1 = (aNodeMax - theRay.Origin) * theInverse;
450       
451       vec3 aTimes = min (aTime0, aTime1);
452       
453       if (max (aTimes.x, max (aTimes.y, aTimes.z)) < theHit.Time)
454       {
455         // fetch object transformation
456         int anObjectId = aData.x - 1;
457         vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0);
458         vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1);
459         vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2);
460         vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 3);
461
462         SRay aNewRay;
463
464         aNewRay.Origin = MatrixColMultiply (vec4 (theRay.Origin, 1.f), 
465           aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3);
466
467         aNewRay.Direct = MatrixColMultiply (vec4 (theRay.Direct, 0.f), 
468           aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3);
469
470         vec3 aNewInverse = 1.f / max (abs (aNewRay.Direct), SMALL);
471         
472         aNewInverse.x = aNewRay.Direct.x < 0.f ? -aNewInverse.x : aNewInverse.x;
473         aNewInverse.y = aNewRay.Direct.y < 0.f ? -aNewInverse.y : aNewInverse.y;
474         aNewInverse.z = aNewRay.Direct.z < 0.f ? -aNewInverse.z : aNewInverse.z;
475
476         ivec4 aTriIndex = ObjectNearestHit (
477           aData.y, aData.z, aData.w, aNewRay, aNewInverse, theHit, aHead);
478
479         if (aTriIndex.x != -1)
480         {
481           aHitObject = ivec4 (aTriIndex.x + aData.z,  // vertex 0
482                               aTriIndex.y + aData.z,  // vertex 1
483                               aTriIndex.z + aData.z,  // vertex 2
484                               aTriIndex.w);           // material
485
486           theObjectId = anObjectId;
487         }
488       }
489       
490       if (aHead < 0)
491         return aHitObject;
492             
493       aNode = Stack[aHead--];
494     }
495     else // if inner node
496     {
497       vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
498       vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
499       vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
500       vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
501
502       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
503       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
504
505       vec3 aTimeMax = max (aTime0, aTime1);
506       vec3 aTimeMin = min (aTime0, aTime1);
507
508       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
509       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
510
511       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeLft <= theHit.Time);
512       
513       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
514       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
515
516       aTimeMax = max (aTime0, aTime1);
517       aTimeMin = min (aTime0, aTime1);
518
519       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
520       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
521       
522       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeRgh <= theHit.Time);
523
524       if (bool(aHitLft & aHitRgh))
525       {
526         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
527
528         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
529       }
530       else
531       {
532         if (bool(aHitLft | aHitRgh))
533         {
534           aNode = bool(aHitLft) ? aData.y : aData.z;
535         }
536         else
537         {
538           if (aHead < 0)
539             return aHitObject;
540
541           aNode = Stack[aHead--];
542         }
543       }
544     }
545   }
546   
547   return aHitObject;
548 }
549
550 // =======================================================================
551 // function : SceneAnyHit
552 // purpose  : Finds intersection with any scene triangle
553 // =======================================================================
554 float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance)
555 {
556   int aHead = -1; // stack pointer
557   int aNode =  0; // node to visit
558   
559   float aTimeOut;
560   float aTimeLft;
561   float aTimeRgh;
562
563   while (true)
564   {
565     ivec4 aData = texelFetch (uSceneNodeInfoTexture, aNode);
566
567     if (aData.x != 0) // if leaf node
568     {
569       // fetch object transformation
570       int anObjectId = aData.x - 1;
571       vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0);
572       vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1);
573       vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2);
574       vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 3);
575
576       SRay aNewRay;
577
578       aNewRay.Origin = MatrixColMultiply (vec4 (theRay.Origin, 1.f), 
579         aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3);
580
581       aNewRay.Direct = MatrixColMultiply (vec4 (theRay.Direct, 0.f), 
582         aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3);
583
584       vec3 aNewInverse = 1.f / max (abs (aNewRay.Direct), SMALL);
585       
586       aNewInverse.x = aNewRay.Direct.x < 0.f ? -aNewInverse.x : aNewInverse.x;
587       aNewInverse.y = aNewRay.Direct.y < 0.f ? -aNewInverse.y : aNewInverse.y;
588       aNewInverse.z = aNewRay.Direct.z < 0.f ? -aNewInverse.z : aNewInverse.z;
589
590       bool isShadow = 0.f == ObjectAnyHit (
591         aData.y, aData.z, aData.w, aNewRay, aNewInverse, theDistance, aHead);
592         
593       if (aHead < 0 || isShadow)
594         return isShadow ? 0.f : 1.f;
595             
596       aNode = Stack[aHead--];
597     }
598     else // if inner node
599     {
600       vec3 aNodeMinLft = texelFetch (uSceneMinPointTexture, aData.y).xyz;
601       vec3 aNodeMaxLft = texelFetch (uSceneMaxPointTexture, aData.y).xyz;
602       vec3 aNodeMinRgh = texelFetch (uSceneMinPointTexture, aData.z).xyz;
603       vec3 aNodeMaxRgh = texelFetch (uSceneMaxPointTexture, aData.z).xyz;
604       
605       vec3 aTime0 = (aNodeMinLft - theRay.Origin) * theInverse;
606       vec3 aTime1 = (aNodeMaxLft - theRay.Origin) * theInverse;
607
608       vec3 aTimeMax = max (aTime0, aTime1);
609       vec3 aTimeMin = min (aTime0, aTime1);
610
611       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
612       aTimeLft = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
613
614       int aHitLft = int(aTimeLft <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeLft <= theDistance);
615       
616       aTime0 = (aNodeMinRgh - theRay.Origin) * theInverse;
617       aTime1 = (aNodeMaxRgh - theRay.Origin) * theInverse;
618
619       aTimeMax = max (aTime0, aTime1);
620       aTimeMin = min (aTime0, aTime1);
621
622       aTimeOut = min (aTimeMax.x, min (aTimeMax.y, aTimeMax.z));
623       aTimeRgh = max (aTimeMin.x, max (aTimeMin.y, aTimeMin.z));
624       
625       int aHitRgh = int(aTimeRgh <= aTimeOut) & int(aTimeOut >= 0.f) & int(aTimeRgh <= theDistance);
626
627       if (bool(aHitLft & aHitRgh))
628       {
629         aNode = (aTimeLft < aTimeRgh) ? aData.y : aData.z;
630
631         Stack[++aHead] = (aTimeLft < aTimeRgh) ? aData.z : aData.y;
632       }
633       else
634       {
635         if (bool(aHitLft | aHitRgh))
636         {
637           aNode = bool(aHitLft) ? aData.y : aData.z;
638         }
639         else
640         {
641           if (aHead < 0)
642             return 1.f;
643
644           aNode = Stack[aHead--];
645         }
646       }
647     }
648   }
649   
650   return 1.f;
651 }
652
653 #define PI 3.1415926f
654
655 // =======================================================================
656 // function : Latlong
657 // purpose  : Converts world direction to environment texture coordinates
658 // =======================================================================
659 vec2 Latlong (in vec3 thePoint, in float theRadius)
660 {
661   float aPsi = acos (-thePoint.z / theRadius);
662   
663   float aPhi = atan (thePoint.y, thePoint.x) + PI;
664   
665   return vec2 (aPhi * 0.1591549f,
666                aPsi * 0.3183098f);
667 }
668
669 // =======================================================================
670 // function : SmoothNormal
671 // purpose  : Interpolates normal across the triangle
672 // =======================================================================
673 vec3 SmoothNormal (in vec2 theUV, in ivec4 theTriangle)
674 {
675   vec3 aNormal0 = texelFetch (uGeometryNormalTexture, theTriangle.x).xyz;
676   vec3 aNormal1 = texelFetch (uGeometryNormalTexture, theTriangle.y).xyz;
677   vec3 aNormal2 = texelFetch (uGeometryNormalTexture, theTriangle.z).xyz;
678   
679   return normalize (aNormal1 * theUV.x +
680                     aNormal2 * theUV.y +
681                     aNormal0 * (1.f - theUV.x - theUV.y));
682 }
683
684 #define THRESHOLD vec3 (0.1f, 0.1f, 0.1f)
685
686 #define MATERIAL_AMBN(index) (7 * index + 0)
687 #define MATERIAL_DIFF(index) (7 * index + 1)
688 #define MATERIAL_SPEC(index) (7 * index + 2)
689 #define MATERIAL_EMIS(index) (7 * index + 3)
690 #define MATERIAL_REFL(index) (7 * index + 4)
691 #define MATERIAL_REFR(index) (7 * index + 5)
692 #define MATERIAL_TRAN(index) (7 * index + 6)
693
694 #define LIGHT_POS(index) (2 * index + 1)
695 #define LIGHT_PWR(index) (2 * index + 0)
696
697 // =======================================================================
698 // function : Radiance
699 // purpose  : Computes color of specified ray
700 // =======================================================================
701 vec4 Radiance (in SRay theRay, in vec3 theInverse)
702 {
703   vec3 aResult = vec3 (0.f);
704   vec4 aWeight = vec4 (1.f);
705
706   int anObjectId;
707   
708   for (int aDepth = 0; aDepth < 5; ++aDepth)
709   {
710     SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO);
711     
712     ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, anObjectId);
713
714     if (aTriIndex.x == -1)
715     {
716       if (aWeight.w != 0.f)
717       {
718         return vec4 (aResult.x,
719                      aResult.y,
720                      aResult.z,
721                      aWeight.w);
722       }
723
724       if (bool(uEnvironmentEnable))
725       {
726         float aTime = IntersectSphere (theRay, uSceneRadius);
727         
728         aResult.xyz += aWeight.xyz * textureLod (uEnvironmentMapTexture,
729           Latlong (theRay.Direct * aTime + theRay.Origin, uSceneRadius), 0.f).xyz;
730       }
731       
732       return vec4 (aResult.x,
733                    aResult.y,
734                    aResult.z,
735                    aWeight.w);
736     }
737     
738     vec3 aPoint = theRay.Direct * aHit.Time + theRay.Origin;
739     
740     vec3 aAmbient = vec3 (texelFetch (
741       uRaytraceMaterialTexture, MATERIAL_AMBN (aTriIndex.w)));
742     vec3 aDiffuse = vec3 (texelFetch (
743       uRaytraceMaterialTexture, MATERIAL_DIFF (aTriIndex.w)));
744     vec4 aSpecular = vec4 (texelFetch (
745       uRaytraceMaterialTexture, MATERIAL_SPEC (aTriIndex.w)));
746     vec2 aOpacity = vec2 (texelFetch (
747       uRaytraceMaterialTexture, MATERIAL_TRAN (aTriIndex.w)));
748       
749     vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);
750
751     vec4 aInvTransf0 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 0);
752     vec4 aInvTransf1 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 1);
753     vec4 aInvTransf2 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 2);
754     vec4 aInvTransf3 = texelFetch (uSceneTransformTexture, anObjectId * 4 + 3);
755
756     aNormal = MatrixRowMultiply (vec4 (aNormal, 0.f), aInvTransf0, aInvTransf1, aInvTransf2, aInvTransf3);
757     aNormal = normalize (aNormal);
758     
759     aHit.Normal = normalize (aHit.Normal);
760     
761     for (int aLightIdx = 0; aLightIdx < uLightCount; ++aLightIdx)
762     {
763       vec4 aLight = texelFetch (
764         uRaytraceLightSrcTexture, LIGHT_POS (aLightIdx));
765       
766       float aDistance = MAXFLOAT;
767       
768       if (aLight.w != 0.f) // point light source
769       {
770         aDistance = length (aLight.xyz -= aPoint);
771         
772         aLight.xyz *= 1.f / aDistance;
773       }
774
775       SRay aShadow = SRay (aPoint + aLight.xyz * uSceneEpsilon, aLight.xyz);
776       
777       aShadow.Origin += aHit.Normal * uSceneEpsilon *
778         (dot (aHit.Normal, aLight.xyz) >= 0.f ? 1.f : -1.f);
779       
780       float aVisibility = 1.f;
781      
782       if (bool(uShadowsEnable))
783       {
784         vec3 aInverse = 1.f / max (abs (aLight.xyz), SMALL);
785         
786         aInverse.x = aLight.x < 0.f ? -aInverse.x : aInverse.x;
787         aInverse.y = aLight.y < 0.f ? -aInverse.y : aInverse.y;
788         aInverse.z = aLight.z < 0.f ? -aInverse.z : aInverse.z;
789         
790         aVisibility = SceneAnyHit (aShadow, aInverse, aDistance);
791       }
792       
793       if (aVisibility > 0.f)
794       {
795         vec3 aIntensity = vec3 (texelFetch (
796           uRaytraceLightSrcTexture, LIGHT_PWR (aLightIdx)));
797  
798         float aLdotN = dot (aShadow.Direct, aNormal);
799         
800         if (aOpacity.y > 0.f)    // force two-sided lighting
801           aLdotN = abs (aLdotN); // for transparent surfaces
802           
803         if (aLdotN > 0.f)
804         {
805           float aRdotV = dot (reflect (aShadow.Direct, aNormal), theRay.Direct);
806           
807           aResult.xyz += aWeight.xyz * aOpacity.x * aIntensity *
808             (aDiffuse * aLdotN + aSpecular.xyz * pow (max (0.f, aRdotV), aSpecular.w));
809         }
810       }
811     }
812     
813     aResult.xyz += aWeight.xyz * uGlobalAmbient.xyz *
814       aAmbient * aOpacity.x * max (abs (dot (aNormal, theRay.Direct)), 0.5f);
815     
816     if (aOpacity.x != 1.f)
817     {
818       aWeight *= aOpacity.y;
819     }
820     else
821     {
822       aWeight *= bool(uReflectionsEnable) ?
823         texelFetch (uRaytraceMaterialTexture, MATERIAL_REFL (aTriIndex.w)) : vec4 (0.f);
824       
825       theRay.Direct = reflect (theRay.Direct, aNormal);
826       
827       if (dot (theRay.Direct, aHit.Normal) < 0.f)
828       {
829         theRay.Direct = reflect (theRay.Direct, aHit.Normal);      
830       }
831
832       theInverse = 1.0 / max (abs (theRay.Direct), SMALL);
833       
834       theInverse.x = theRay.Direct.x < 0.0 ? -theInverse.x : theInverse.x;
835       theInverse.y = theRay.Direct.y < 0.0 ? -theInverse.y : theInverse.y;
836       theInverse.z = theRay.Direct.z < 0.0 ? -theInverse.z : theInverse.z;
837       
838       aPoint += aHit.Normal * (dot (aHit.Normal, theRay.Direct) >= 0.f ? uSceneEpsilon : -uSceneEpsilon);
839     }
840     
841     if (all (lessThanEqual (aWeight.xyz, THRESHOLD)))
842     {
843       return vec4 (aResult.x,
844                    aResult.y,
845                    aResult.z,
846                    aWeight.w);
847     }
848     
849     theRay.Origin = theRay.Direct * uSceneEpsilon + aPoint;
850   }
851
852   return vec4 (aResult.x,
853                aResult.y,
854                aResult.z,
855                aWeight.w);
856 }