IntersectEllipsoid.glsl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // See IntersectionUtils.glsl for the definitions of Ray, Intersections,
  2. // setIntersection, setIntersectionPair, INF_HIT, NO_HIT
  3. /* Ellipsoid defines (set in Scene/VoxelEllipsoidShape.js)
  4. #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE
  5. #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO
  6. #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_UNDER_HALF
  7. #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_HALF
  8. #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_OVER_HALF
  9. #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE
  10. #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF
  11. #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF
  12. #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF
  13. #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF
  14. #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF
  15. #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF
  16. #define ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX
  17. #define ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN
  18. #define ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_FLAT
  19. #define ELLIPSOID_INTERSECTION_INDEX_LONGITUDE
  20. #define ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX
  21. #define ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN
  22. #define ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX
  23. #define ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN
  24. */
  25. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE)
  26. uniform vec2 u_ellipsoidRenderLongitudeMinMax;
  27. #endif
  28. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF) || defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF) || defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF) || defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF)
  29. uniform vec2 u_ellipsoidRenderLatitudeCosSqrHalfMinMax;
  30. #endif
  31. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX)
  32. uniform float u_ellipsoidInverseOuterScaleUv;
  33. #endif
  34. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN)
  35. uniform float u_ellipsoidInverseInnerScaleUv;
  36. #endif
  37. vec2 intersectZPlane(Ray ray)
  38. {
  39. float o = ray.pos.z;
  40. float d = ray.dir.z;
  41. float t = -o / d;
  42. float s = sign(o);
  43. if (t >= 0.0 != s >= 0.0) return vec2(t, +INF_HIT);
  44. else return vec2(-INF_HIT, t);
  45. }
  46. vec4 intersectHalfPlane(Ray ray, float angle) {
  47. vec2 o = ray.pos.xy;
  48. vec2 d = ray.dir.xy;
  49. vec2 planeDirection = vec2(cos(angle), sin(angle));
  50. vec2 planeNormal = vec2(planeDirection.y, -planeDirection.x);
  51. float a = dot(o, planeNormal);
  52. float b = dot(d, planeNormal);
  53. float t = -a / b;
  54. vec2 p = o + t * d;
  55. bool outside = dot(p, planeDirection) < 0.0;
  56. if (outside) return vec4(-INF_HIT, +INF_HIT, NO_HIT, NO_HIT);
  57. return vec4(-INF_HIT, t, t, +INF_HIT);
  58. }
  59. vec2 intersectHalfSpace(Ray ray, float angle)
  60. {
  61. vec2 o = ray.pos.xy;
  62. vec2 d = ray.dir.xy;
  63. vec2 n = vec2(sin(angle), -cos(angle));
  64. float a = dot(o, n);
  65. float b = dot(d, n);
  66. float t = -a / b;
  67. float s = sign(a);
  68. if (t >= 0.0 != s >= 0.0) return vec2(t, +INF_HIT);
  69. else return vec2(-INF_HIT, t);
  70. }
  71. vec2 intersectRegularWedge(Ray ray, float minAngle, float maxAngle)
  72. {
  73. vec2 o = ray.pos.xy;
  74. vec2 d = ray.dir.xy;
  75. vec2 n1 = vec2(sin(minAngle), -cos(minAngle));
  76. vec2 n2 = vec2(-sin(maxAngle), cos(maxAngle));
  77. float a1 = dot(o, n1);
  78. float a2 = dot(o, n2);
  79. float b1 = dot(d, n1);
  80. float b2 = dot(d, n2);
  81. float t1 = -a1 / b1;
  82. float t2 = -a2 / b2;
  83. float s1 = sign(a1);
  84. float s2 = sign(a2);
  85. float tmin = min(t1, t2);
  86. float tmax = max(t1, t2);
  87. float smin = tmin == t1 ? s1 : s2;
  88. float smax = tmin == t1 ? s2 : s1;
  89. bool e = tmin >= 0.0;
  90. bool f = tmax >= 0.0;
  91. bool g = smin >= 0.0;
  92. bool h = smax >= 0.0;
  93. if (e != g && f == h) return vec2(tmin, tmax);
  94. else if (e == g && f == h) return vec2(-INF_HIT, tmin);
  95. else if (e != g && f != h) return vec2(tmax, +INF_HIT);
  96. else return vec2(NO_HIT);
  97. }
  98. vec4 intersectFlippedWedge(Ray ray, float minAngle, float maxAngle)
  99. {
  100. vec2 planeIntersectMin = intersectHalfSpace(ray, minAngle);
  101. vec2 planeIntersectMax = intersectHalfSpace(ray, maxAngle + czm_pi);
  102. return vec4(planeIntersectMin, planeIntersectMax);
  103. }
  104. vec2 intersectUnitSphere(Ray ray)
  105. {
  106. vec3 o = ray.pos;
  107. vec3 d = ray.dir;
  108. float b = dot(d, o);
  109. float c = dot(o, o) - 1.0;
  110. float det = b * b - c;
  111. if (det < 0.0) {
  112. return vec2(NO_HIT);
  113. }
  114. det = sqrt(det);
  115. float t1 = -b - det;
  116. float t2 = -b + det;
  117. float tmin = min(t1, t2);
  118. float tmax = max(t1, t2);
  119. return vec2(tmin, tmax);
  120. }
  121. vec2 intersectUnitSphereUnnormalizedDirection(Ray ray)
  122. {
  123. vec3 o = ray.pos;
  124. vec3 d = ray.dir;
  125. float a = dot(d, d);
  126. float b = dot(d, o);
  127. float c = dot(o, o) - 1.0;
  128. float det = b * b - a * c;
  129. if (det < 0.0) {
  130. return vec2(NO_HIT);
  131. }
  132. det = sqrt(det);
  133. float t1 = (-b - det) / a;
  134. float t2 = (-b + det) / a;
  135. float tmin = min(t1, t2);
  136. float tmax = max(t1, t2);
  137. return vec2(tmin, tmax);
  138. }
  139. vec2 intersectDoubleEndedCone(Ray ray, float cosSqrHalfAngle)
  140. {
  141. vec3 o = ray.pos;
  142. vec3 d = ray.dir;
  143. float a = d.z * d.z - dot(d, d) * cosSqrHalfAngle;
  144. float b = d.z * o.z - dot(o, d) * cosSqrHalfAngle;
  145. float c = o.z * o.z - dot(o, o) * cosSqrHalfAngle;
  146. float det = b * b - a * c;
  147. if (det < 0.0) {
  148. return vec2(NO_HIT);
  149. }
  150. det = sqrt(det);
  151. float t1 = (-b - det) / a;
  152. float t2 = (-b + det) / a;
  153. float tmin = min(t1, t2);
  154. float tmax = max(t1, t2);
  155. return vec2(tmin, tmax);
  156. }
  157. vec4 intersectFlippedCone(Ray ray, float cosSqrHalfAngle) {
  158. vec2 intersect = intersectDoubleEndedCone(ray, cosSqrHalfAngle);
  159. if (intersect.x == NO_HIT) {
  160. return vec4(-INF_HIT, +INF_HIT, NO_HIT, NO_HIT);
  161. }
  162. vec3 o = ray.pos;
  163. vec3 d = ray.dir;
  164. float tmin = intersect.x;
  165. float tmax = intersect.y;
  166. float zmin = o.z + tmin * d.z;
  167. float zmax = o.z + tmax * d.z;
  168. // One interval
  169. if (zmin < 0.0 && zmax < 0.0) return vec4(-INF_HIT, +INF_HIT, NO_HIT, NO_HIT);
  170. else if (zmin < 0.0) return vec4(-INF_HIT, tmax, NO_HIT, NO_HIT);
  171. else if (zmax < 0.0) return vec4(tmin, +INF_HIT, NO_HIT, NO_HIT);
  172. // Two intervals
  173. else return vec4(-INF_HIT, tmin, tmax, +INF_HIT);
  174. }
  175. vec2 intersectRegularCone(Ray ray, float cosSqrHalfAngle) {
  176. vec2 intersect = intersectDoubleEndedCone(ray, cosSqrHalfAngle);
  177. if (intersect.x == NO_HIT) {
  178. return vec2(NO_HIT);
  179. }
  180. vec3 o = ray.pos;
  181. vec3 d = ray.dir;
  182. float tmin = intersect.x;
  183. float tmax = intersect.y;
  184. float zmin = o.z + tmin * d.z;
  185. float zmax = o.z + tmax * d.z;
  186. if (zmin < 0.0 && zmax < 0.0) return vec2(NO_HIT);
  187. else if (zmin < 0.0) return vec2(tmax, +INF_HIT);
  188. else if (zmax < 0.0) return vec2(-INF_HIT, tmin);
  189. else return vec2(tmin, tmax);
  190. }
  191. void intersectShape(in Ray ray, inout Intersections ix) {
  192. // Position is converted from [0,1] to [-1,+1] because shape intersections assume unit space is [-1,+1].
  193. // Direction is scaled as well to be in sync with position.
  194. ray.pos = ray.pos * 2.0 - 1.0;
  195. ray.dir *= 2.0;
  196. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX)
  197. Ray outerRay = Ray(ray.pos * u_ellipsoidInverseOuterScaleUv, ray.dir * u_ellipsoidInverseOuterScaleUv);
  198. #else
  199. Ray outerRay = ray;
  200. #endif
  201. // Outer ellipsoid
  202. vec2 outerIntersect = intersectUnitSphereUnnormalizedDirection(outerRay);
  203. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX, outerIntersect);
  204. // Exit early if the outer ellipsoid was missed.
  205. if (outerIntersect.x == NO_HIT) {
  206. return;
  207. }
  208. // Inner ellipsoid
  209. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_FLAT)
  210. // When the ellipsoid is perfectly thin it's necessary to sandwich the
  211. // inner ellipsoid intersection inside the outer ellipsoid intersection.
  212. // Without this special case,
  213. // [outerMin, outerMax, innerMin, innerMax] will bubble sort to
  214. // [outerMin, innerMin, outerMax, innerMax] which will cause the back
  215. // side of the ellipsoid to be invisible because it will think the ray
  216. // is still inside the inner (negative) ellipsoid after exiting the
  217. // outer (positive) ellipsoid.
  218. // With this special case,
  219. // [outerMin, innerMin, innerMax, outerMax] will bubble sort to
  220. // [outerMin, innerMin, innerMax, outerMax] which will work correctly.
  221. // Note: If initializeIntersections() changes its sorting function
  222. // from bubble sort to something else, this code may need to change.
  223. setIntersection(ix, 0, outerIntersect.x, true, true); // positive, enter
  224. setIntersection(ix, 1, outerIntersect.x, false, true); // negative, enter
  225. setIntersection(ix, 2, outerIntersect.y, false, false); // negative, exit
  226. setIntersection(ix, 3, outerIntersect.y, true, false); // positive, exit
  227. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN)
  228. Ray innerRay = Ray(ray.pos * u_ellipsoidInverseInnerScaleUv, ray.dir * u_ellipsoidInverseInnerScaleUv);
  229. vec2 innerIntersect = intersectUnitSphereUnnormalizedDirection(innerRay);
  230. if (innerIntersect == vec2(NO_HIT)) {
  231. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN, innerIntersect);
  232. } else {
  233. // When the ellipsoid is very large and thin it's possible for floating
  234. // point math to cause the ray to intersect the inner ellipsoid before
  235. // the outer ellipsoid. To prevent this from happening, clamp innerIntersect
  236. // to outerIntersect and sandwhich the intersections like described above.
  237. //
  238. // In theory a similar fix is needed for cylinders, however it's more
  239. // complicated to implement because the inner shape is allowed to be
  240. // intersected first.
  241. innerIntersect.x = max(innerIntersect.x, outerIntersect.x);
  242. innerIntersect.y = min(innerIntersect.y, outerIntersect.y);
  243. setIntersection(ix, 0, outerIntersect.x, true, true); // positive, enter
  244. setIntersection(ix, 1, innerIntersect.x, false, true); // negative, enter
  245. setIntersection(ix, 2, innerIntersect.y, false, false); // negative, exit
  246. setIntersection(ix, 3, outerIntersect.y, true, false); // positive, exit
  247. }
  248. #endif
  249. // Flip the ray because the intersection function expects a cone growing towards +Z.
  250. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF) || defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF) || defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF)
  251. Ray flippedRay = outerRay;
  252. flippedRay.dir.z *= -1.0;
  253. flippedRay.pos.z *= -1.0;
  254. #endif
  255. // Bottom cone
  256. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF)
  257. vec2 bottomConeIntersection = intersectRegularCone(flippedRay, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.x);
  258. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection);
  259. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF)
  260. vec2 bottomConeIntersection = intersectZPlane(flippedRay);
  261. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection);
  262. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF)
  263. vec4 bottomConeIntersection = intersectFlippedCone(ray, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.x);
  264. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 0, bottomConeIntersection.xy);
  265. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 1, bottomConeIntersection.zw);
  266. #endif
  267. // Top cone
  268. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF)
  269. vec4 topConeIntersection = intersectFlippedCone(flippedRay, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.y);
  270. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 0, topConeIntersection.xy);
  271. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 1, topConeIntersection.zw);
  272. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF)
  273. vec2 topConeIntersection = intersectZPlane(ray);
  274. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection);
  275. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF)
  276. vec2 topConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.y);
  277. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection);
  278. #endif
  279. // Wedge
  280. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO)
  281. vec4 wedgeIntersect = intersectHalfPlane(ray, u_ellipsoidRenderLongitudeMinMax.x);
  282. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE + 0, wedgeIntersect.xy);
  283. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE + 1, wedgeIntersect.zw);
  284. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_UNDER_HALF)
  285. vec2 wedgeIntersect = intersectRegularWedge(ray, u_ellipsoidRenderLongitudeMinMax.x, u_ellipsoidRenderLongitudeMinMax.y);
  286. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE, wedgeIntersect);
  287. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_EQUAL_HALF)
  288. vec2 wedgeIntersect = intersectHalfSpace(ray, u_ellipsoidRenderLongitudeMinMax.x);
  289. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE, wedgeIntersect);
  290. #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_RANGE_OVER_HALF)
  291. vec4 wedgeIntersect = intersectFlippedWedge(ray, u_ellipsoidRenderLongitudeMinMax.x, u_ellipsoidRenderLongitudeMinMax.y);
  292. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE + 0, wedgeIntersect.xy);
  293. setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE + 1, wedgeIntersect.zw);
  294. #endif
  295. }