123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- /* Ellipsoid defines (set in Scene/VoxelEllipsoidShape.js)
- #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY
- #define ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY
- #define ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE
- #define ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO
- #define ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED
- #define ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE
- #define ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO
- #define ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN
- #define ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT
- #define ELLIPSOID_IS_SPHERE
- */
- uniform vec3 u_ellipsoidRadiiUv; // [0,1]
- #if !defined(ELLIPSOID_IS_SPHERE)
- uniform vec3 u_ellipsoidInverseRadiiSquaredUv;
- #endif
- #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)
- uniform vec3 u_ellipsoidShapeUvLongitudeMinMaxMid;
- #endif
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE)
- uniform vec2 u_ellipsoidUvToShapeUvLongitude; // x = scale, y = offset
- #endif
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE)
- uniform vec2 u_ellipsoidUvToShapeUvLatitude; // x = scale, y = offset
- #endif
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN) && !defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT)
- uniform float u_ellipsoidInverseHeightDifferenceUv;
- uniform vec2 u_ellipseInnerRadiiUv; // [0,1]
- #endif
- // robust iterative solution without trig functions
- // https://github.com/0xfaded/ellipse_demo/issues/1
- // https://stackoverflow.com/questions/22959698/distance-from-given-point-to-given-ellipse
- // Pro: Good when radii.x ~= radii.y
- // Con: Breaks at pos.x ~= 0.0, especially inside the ellipse
- // Con: Inaccurate with exterior points and thin ellipses
- float ellipseDistanceIterative (vec2 pos, vec2 radii) {
- vec2 p = abs(pos);
- vec2 invRadii = 1.0 / radii;
- vec2 a = vec2(1.0, -1.0) * (radii.x * radii.x - radii.y * radii.y) * invRadii;
- vec2 t = vec2(0.70710678118); // sqrt(2) / 2
- vec2 v = radii * t;
- const int iterations = 3;
- for (int i = 0; i < iterations; ++i) {
- vec2 e = a * pow(t, vec2(3.0));
- vec2 q = normalize(p - e) * length(v - e);
- t = normalize((q + e) * invRadii);
- v = radii * t;
- }
- return length(v * sign(pos) - pos) * sign(p.y - v.y);
- }
- vec3 convertUvToShapeUvSpace(in vec3 positionUv) {
- // Compute position and normal.
- // Convert positionUv [0,1] to local space [-1,+1] to "normalized" cartesian space [-a,+a] where a = (radii + height) / (max(radii) + height).
- // A point on the largest ellipsoid axis would be [-1,+1] and everything else would be smaller.
- vec3 positionLocal = positionUv * 2.0 - 1.0;
- #if defined(ELLIPSOID_IS_SPHERE)
- vec3 posEllipsoid = positionLocal * u_ellipsoidRadiiUv.x;
- vec3 normal = normalize(posEllipsoid);
- #else
- vec3 posEllipsoid = positionLocal * u_ellipsoidRadiiUv;
- vec3 normal = normalize(posEllipsoid * u_ellipsoidInverseRadiiSquaredUv); // geodetic surface normal
- #endif
- // Compute longitude
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_RANGE_EQUAL_ZERO)
- float longitude = 1.0;
- #else
- float longitude = (atan(normal.y, normal.x) + czm_pi) / czm_twoPi;
- // Correct the angle when max < min
- // Technically this should compare against min longitude - but it has precision problems so compare against the middle of empty space.
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE_MIN_MAX_REVERSED)
- longitude += float(longitude < u_ellipsoidShapeUvLongitudeMinMaxMid.z);
- #endif
- // Avoid flickering from reading voxels from both sides of the -pi/+pi discontinuity.
- #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MIN_DISCONTINUITY)
- longitude = longitude > u_ellipsoidShapeUvLongitudeMinMaxMid.z ? u_ellipsoidShapeUvLongitudeMinMaxMid.x : longitude;
- #endif
- #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE_MAX_DISCONTINUITY)
- longitude = longitude < u_ellipsoidShapeUvLongitudeMinMaxMid.z ? u_ellipsoidShapeUvLongitudeMinMaxMid.y : longitude;
- #endif
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LONGITUDE)
- longitude = longitude * u_ellipsoidUvToShapeUvLongitude.x + u_ellipsoidUvToShapeUvLongitude.y;
- #endif
- #endif
- // Compute latitude
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE_RANGE_EQUAL_ZERO)
- float latitude = 1.0;
- #else
- float latitude = (asin(normal.z) + czm_piOverTwo) / czm_pi;
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE)
- latitude = latitude * u_ellipsoidUvToShapeUvLatitude.x + u_ellipsoidUvToShapeUvLatitude.y;
- #endif
- #endif
- // Compute height
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT)
- // TODO: This breaks down when minBounds == maxBounds. To fix it, this
- // function would have to know if ray is intersecting the front or back of the shape
- // and set the shape space position to 1 (front) or 0 (back) accordingly.
- float height = 1.0;
- #else
- #if defined(ELLIPSOID_IS_SPHERE)
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN)
- float height = (length(posEllipsoid) - u_ellipseInnerRadiiUv.x) * u_ellipsoidInverseHeightDifferenceUv;
- #else
- float height = length(posEllipsoid);
- #endif
- #else
- #if defined(ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_MIN)
- // 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).
- // This is an optimization so that math can be done with ellipses instead of ellipsoids.
- vec2 posEllipse = vec2(length(posEllipsoid.xy), posEllipsoid.z);
- float height = ellipseDistanceIterative(posEllipse, u_ellipseInnerRadiiUv) * u_ellipsoidInverseHeightDifferenceUv;
- #else
- // TODO: this is probably not correct
- float height = length(posEllipsoid);
- #endif
- #endif
- #endif
- return vec3(longitude, latitude, height);
- }
|