IntersectionUtils.glsl 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /* Intersection defines
  2. #define INTERSECTION_COUNT ###
  3. */
  4. #define NO_HIT (-czm_infinity)
  5. #define INF_HIT (czm_infinity * 0.5)
  6. #define RAY_SHIFT (0.000003163)
  7. #define RAY_SCALE (1.003163)
  8. struct Ray {
  9. vec3 pos;
  10. vec3 dir;
  11. #if defined(SHAPE_BOX)
  12. vec3 dInv;
  13. #endif
  14. };
  15. struct RayShapeIntersection {
  16. vec4 entry;
  17. vec4 exit;
  18. };
  19. struct Intersections {
  20. // Don't access these member variables directly - call the functions instead.
  21. // Store an array of ray-surface intersections. Each intersection is composed of:
  22. // .xyz for the surface normal at the intersection point
  23. // .w for the T value
  24. // The scale of the normal encodes the shape intersection type:
  25. // length(intersection.xyz) = 1: positive shape entry
  26. // length(intersection.xyz) = 2: positive shape exit
  27. // length(intersection.xyz) = 3: negative shape entry
  28. // length(intersection.xyz) = 4: negative shape exit
  29. // INTERSECTION_COUNT is the number of ray-*shape* (volume) intersections,
  30. // so we need twice as many to track ray-*surface* intersections
  31. vec4 intersections[INTERSECTION_COUNT * 2];
  32. #if (INTERSECTION_COUNT > 1)
  33. // Maintain state for future nextIntersection calls
  34. int index;
  35. int surroundCount;
  36. bool surroundIsPositive;
  37. #endif
  38. };
  39. RayShapeIntersection getFirstIntersection(in Intersections ix)
  40. {
  41. return RayShapeIntersection(ix.intersections[0], ix.intersections[1]);
  42. }
  43. vec4 encodeIntersectionType(vec4 intersection, int index, bool entry)
  44. {
  45. float scale = float(index > 0) * 2.0 + float(!entry) + 1.0;
  46. return vec4(intersection.xyz * scale, intersection.w);
  47. }
  48. // Use defines instead of real functions because WebGL1 cannot access array with non-constant index.
  49. #define setIntersection(/*inout Intersections*/ ix, /*int*/ index, /*float*/ t, /*bool*/ positive, /*bool*/ enter) (ix).intersections[(index)] = vec4(0.0, float(!positive) * 2.0 + float(!enter) + 1.0, 0.0, (t))
  50. #define setIntersectionPair(/*inout Intersections*/ ix, /*int*/ index, /*vec2*/ entryExit) (ix).intersections[(index) * 2 + 0] = vec4(0.0, float((index) > 0) * 2.0 + 1.0, 0.0, (entryExit).x); (ix).intersections[(index) * 2 + 1] = vec4(0.0, float((index) > 0) * 2.0 + 2.0, 0.0, (entryExit).y)
  51. #define setSurfaceIntersection(/*inout Intersections*/ ix, /*int*/ index, /*vec4*/ intersection) (ix).intersections[(index)] = intersection;
  52. #define setShapeIntersection(/*inout Intersections*/ ix, /*int*/ index, /*RayShapeIntersection*/ intersection) (ix).intersections[(index) * 2 + 0] = encodeIntersectionType((intersection).entry, (index), true); (ix).intersections[(index) * 2 + 1] = encodeIntersectionType((intersection).exit, (index), false)
  53. #if (INTERSECTION_COUNT > 1)
  54. void initializeIntersections(inout Intersections ix) {
  55. // Sort the intersections from min T to max T with bubble sort.
  56. // Note: If this sorting function changes, some of the intersection test may
  57. // need to be updated. Search for "bubble sort" to find those areas.
  58. const int sortPasses = INTERSECTION_COUNT * 2 - 1;
  59. for (int n = sortPasses; n > 0; --n) {
  60. for (int i = 0; i < sortPasses; ++i) {
  61. // The loop should be: for (i = 0; i < n; ++i) {...} but WebGL1 cannot
  62. // loop with non-constant condition, so it has to break early instead
  63. if (i >= n) { break; }
  64. vec4 intersect0 = ix.intersections[i + 0];
  65. vec4 intersect1 = ix.intersections[i + 1];
  66. bool inOrder = intersect0.w <= intersect1.w;
  67. ix.intersections[i + 0] = inOrder ? intersect0 : intersect1;
  68. ix.intersections[i + 1] = inOrder ? intersect1 : intersect0;
  69. }
  70. }
  71. // Prepare initial state for nextIntersection
  72. ix.index = 0;
  73. ix.surroundCount = 0;
  74. ix.surroundIsPositive = false;
  75. }
  76. #endif
  77. #if (INTERSECTION_COUNT > 1)
  78. RayShapeIntersection nextIntersection(inout Intersections ix) {
  79. vec4 surfaceIntersection = vec4(0.0, 0.0, 0.0, NO_HIT);
  80. RayShapeIntersection shapeIntersection = RayShapeIntersection(surfaceIntersection, surfaceIntersection);
  81. const int passCount = INTERSECTION_COUNT * 2;
  82. if (ix.index == passCount) {
  83. return shapeIntersection;
  84. }
  85. for (int i = 0; i < passCount; ++i) {
  86. // The loop should be: for (i = ix.index; i < passCount; ++i) {...} but WebGL1 cannot
  87. // loop with non-constant condition, so it has to continue instead.
  88. if (i < ix.index) {
  89. continue;
  90. }
  91. ix.index = i + 1;
  92. surfaceIntersection = ix.intersections[i];
  93. int intersectionType = int(length(surfaceIntersection.xyz) - 0.5);
  94. bool currShapeIsPositive = intersectionType < 2;
  95. bool enter = intMod(intersectionType, 2) == 0;
  96. ix.surroundCount += enter ? +1 : -1;
  97. ix.surroundIsPositive = currShapeIsPositive ? enter : ix.surroundIsPositive;
  98. // entering positive or exiting negative
  99. if (ix.surroundCount == 1 && ix.surroundIsPositive && enter == currShapeIsPositive) {
  100. shapeIntersection.entry = surfaceIntersection;
  101. }
  102. // exiting positive or entering negative after being inside positive
  103. bool exitPositive = !enter && currShapeIsPositive && ix.surroundCount == 0;
  104. bool enterNegativeFromPositive = enter && !currShapeIsPositive && ix.surroundCount == 2 && ix.surroundIsPositive;
  105. if (exitPositive || enterNegativeFromPositive) {
  106. shapeIntersection.exit = surfaceIntersection;
  107. // entry and exit have been found, so the loop can stop
  108. if (exitPositive) {
  109. // After exiting positive shape there is nothing left to intersect, so jump to the end index.
  110. ix.index = passCount;
  111. }
  112. break;
  113. }
  114. }
  115. return shapeIntersection;
  116. }
  117. #endif
  118. // NOTE: initializeIntersections, nextIntersection aren't even declared unless INTERSECTION_COUNT > 1