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