convertUvToEllipsoid.glsl 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /* Ellipsoid defines (set in Scene/VoxelEllipsoidShape.js)
  2. #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY
  3. #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY
  4. #define ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE
  5. #define ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO
  6. #define ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED
  7. #define ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE
  8. #define ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO
  9. #define ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN
  10. #define ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT
  11. #define ELLIPSOID_IS_SPHERE
  12. */
  13. uniform vec3 u_ellipsoidRadiiUv; // [0,1]
  14. #if !defined(ELLIPSOID_IS_SPHERE)
  15. uniform vec3 u_ellipsoidInverseRadiiSquaredUv;
  16. #endif
  17. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY) || defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY) || defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED)
  18. uniform vec3 u_ellipsoidShapeUvLongitudeMinMaxMid;
  19. #endif
  20. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE)
  21. uniform vec2 u_ellipsoidUvToShapeUvLongitude; // x = scale, y = offset
  22. #endif
  23. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE)
  24. uniform vec2 u_ellipsoidUvToShapeUvLatitude; // x = scale, y = offset
  25. #endif
  26. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN) && !defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT)
  27. uniform float u_ellipsoidInverseHeightDifferenceUv;
  28. uniform vec2 u_ellipseInnerRadiiUv; // [0,1]
  29. #endif
  30. // robust iterative solution without trig functions
  31. // https://github.com/0xfaded/ellipse_demo/issues/1
  32. // https://stackoverflow.com/questions/22959698/distance-from-given-point-to-given-ellipse
  33. // Pro: Good when radii.x ~= radii.y
  34. // Con: Breaks at pos.x ~= 0.0, especially inside the ellipse
  35. // Con: Inaccurate with exterior points and thin ellipses
  36. float ellipseDistanceIterative (vec2 pos, vec2 radii) {
  37. vec2 p = abs(pos);
  38. vec2 invRadii = 1.0 / radii;
  39. vec2 a = vec2(1.0, -1.0) * (radii.x * radii.x - radii.y * radii.y) * invRadii;
  40. vec2 t = vec2(0.70710678118); // sqrt(2) / 2
  41. vec2 v = radii * t;
  42. const int iterations = 3;
  43. for (int i = 0; i < iterations; ++i) {
  44. vec2 e = a * pow(t, vec2(3.0));
  45. vec2 q = normalize(p - e) * length(v - e);
  46. t = normalize((q + e) * invRadii);
  47. v = radii * t;
  48. }
  49. return length(v * sign(pos) - pos) * sign(p.y - v.y);
  50. }
  51. vec3 convertUvToShapeUvSpace(in vec3 positionUv) {
  52. // Compute position and normal.
  53. // Convert positionUv [0,1] to local space [-1,+1] to "normalized" cartesian space [-a,+a] where a = (radii + height) / (max(radii) + height).
  54. // A point on the largest ellipsoid axis would be [-1,+1] and everything else would be smaller.
  55. vec3 positionLocal = positionUv * 2.0 - 1.0;
  56. #if defined(ELLIPSOID_IS_SPHERE)
  57. vec3 posEllipsoid = positionLocal * u_ellipsoidRadiiUv.x;
  58. vec3 normal = normalize(posEllipsoid);
  59. #else
  60. vec3 posEllipsoid = positionLocal * u_ellipsoidRadiiUv;
  61. vec3 normal = normalize(posEllipsoid * u_ellipsoidInverseRadiiSquaredUv); // geodetic surface normal
  62. #endif
  63. // Compute longitude
  64. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO)
  65. float longitude = 1.0;
  66. #else
  67. float longitude = (atan(normal.y, normal.x) + czm_pi) / czm_twoPi;
  68. // Correct the angle when max < min
  69. // Technically this should compare against min longitude - but it has precision problems so compare against the middle of empty space.
  70. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED)
  71. longitude += float(longitude < u_ellipsoidShapeUvLongitudeMinMaxMid.z);
  72. #endif
  73. // Avoid flickering from reading voxels from both sides of the -pi/+pi discontinuity.
  74. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY)
  75. longitude = longitude > u_ellipsoidShapeUvLongitudeMinMaxMid.z ? u_ellipsoidShapeUvLongitudeMinMaxMid.x : longitude;
  76. #endif
  77. #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY)
  78. longitude = longitude < u_ellipsoidShapeUvLongitudeMinMaxMid.z ? u_ellipsoidShapeUvLongitudeMinMaxMid.y : longitude;
  79. #endif
  80. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE)
  81. longitude = longitude * u_ellipsoidUvToShapeUvLongitude.x + u_ellipsoidUvToShapeUvLongitude.y;
  82. #endif
  83. #endif
  84. // Compute latitude
  85. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO)
  86. float latitude = 1.0;
  87. #else
  88. float latitude = (asin(normal.z) + czm_piOverTwo) / czm_pi;
  89. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE)
  90. latitude = latitude * u_ellipsoidUvToShapeUvLatitude.x + u_ellipsoidUvToShapeUvLatitude.y;
  91. #endif
  92. #endif
  93. // Compute height
  94. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT)
  95. // TODO: This breaks down when minBounds == maxBounds. To fix it, this
  96. // function would have to know if ray is intersecting the front or back of the shape
  97. // and set the shape space position to 1 (front) or 0 (back) accordingly.
  98. float height = 1.0;
  99. #else
  100. #if defined(ELLIPSOID_IS_SPHERE)
  101. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN)
  102. float height = (length(posEllipsoid) - u_ellipseInnerRadiiUv.x) * u_ellipsoidInverseHeightDifferenceUv;
  103. #else
  104. float height = length(posEllipsoid);
  105. #endif
  106. #else
  107. #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN)
  108. // Convert the 3D position to a 2D position relative to the ellipse (radii.x, radii.z) (assuming radii.x == radii.y which is true for WGS84).
  109. // This is an optimization so that math can be done with ellipses instead of ellipsoids.
  110. vec2 posEllipse = vec2(length(posEllipsoid.xy), posEllipsoid.z);
  111. float height = ellipseDistanceIterative(posEllipse, u_ellipseInnerRadiiUv) * u_ellipsoidInverseHeightDifferenceUv;
  112. #else
  113. // TODO: this is probably not correct
  114. float height = length(posEllipsoid);
  115. #endif
  116. #endif
  117. #endif
  118. return vec3(longitude, latitude, height);
  119. }