From 94fc0904480e1068cc5a41b2af1c87c242d0f831 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Mon, 19 Feb 2024 18:56:04 -0500 Subject: [PATCH 1/7] Return normals from ellipsoid voxel height intersections --- .../Source/Scene/VoxelEllipsoidShape.js | 53 +----- .../Shaders/Voxels/IntersectEllipsoid.glsl | 157 +++++++----------- 2 files changed, 71 insertions(+), 139 deletions(-) diff --git a/packages/engine/Source/Scene/VoxelEllipsoidShape.js b/packages/engine/Source/Scene/VoxelEllipsoidShape.js index 43b6a828ab35..745ca3f727a5 100644 --- a/packages/engine/Source/Scene/VoxelEllipsoidShape.js +++ b/packages/engine/Source/Scene/VoxelEllipsoidShape.js @@ -103,8 +103,7 @@ function VoxelEllipsoidShape() { ellipsoidRenderLatitudeCosSqrHalfMinMax: new Cartesian2(), ellipsoidInverseHeightDifferenceUv: 0.0, ellipseInnerRadiiUv: new Cartesian2(), - ellipsoidInverseInnerScaleUv: 0.0, - ellipsoidInverseOuterScaleUv: 0.0, + clipMinMaxHeight: new Cartesian2(), }; /** @@ -127,8 +126,6 @@ function VoxelEllipsoidShape() { ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF: undefined, ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF: undefined, ELLIPSOID_HAS_SHAPE_BOUNDS_LATITUDE: undefined, - ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX: undefined, - ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN: undefined, ELLIPSOID_HAS_SHAPE_BOUNDS_HEIGHT_FLAT: undefined, ELLIPSOID_IS_SPHERE: undefined, ELLIPSOID_INTERSECTION_INDEX_LONGITUDE: undefined, @@ -150,7 +147,6 @@ const scratchScale = new Cartesian3(); const scratchRotationScale = new Matrix3(); const scratchShapeOuterExtent = new Cartesian3(); const scratchRenderOuterExtent = new Cartesian3(); -const scratchRenderInnerExtent = new Cartesian3(); const scratchRenderRectangle = new Rectangle(); /** @@ -270,16 +266,6 @@ VoxelEllipsoidShape.prototype.update = function ( ); const shapeMaxExtent = Cartesian3.maximumComponent(shapeOuterExtent); - const renderInnerExtent = Cartesian3.add( - radii, - Cartesian3.fromElements( - renderMinHeight, - renderMinHeight, - renderMinHeight, - scratchRenderInnerExtent - ), - scratchRenderInnerExtent - ); const renderOuterExtent = Cartesian3.add( radii, Cartesian3.fromElements( @@ -441,16 +427,6 @@ VoxelEllipsoidShape.prototype.update = function ( shapeIsLatitudeMinOverHalf; const shapeHasLatitude = shapeHasLatitudeMax || shapeHasLatitudeMin; - // Height - const renderHasMinHeight = !Cartesian3.equals( - renderInnerExtent, - Cartesian3.ZERO - ); - const renderHasMaxHeight = !Cartesian3.equals( - renderOuterExtent, - Cartesian3.ZERO - ); - const { shaderUniforms, shaderDefines } = this; // To keep things simple, clear the defines every time @@ -484,27 +460,14 @@ VoxelEllipsoidShape.prototype.update = function ( // Intersects an outer ellipsoid for the max height. shaderDefines["ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX"] = intersectionCount; intersectionCount += 1; + shaderDefines["ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN"] = intersectionCount; + intersectionCount += 1; - if (renderHasMinHeight) { - shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN"] = true; - shaderDefines[ - "ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN" - ] = intersectionCount; - intersectionCount += 1; - - // The inverse of the percent of space that is taken up by the inner ellipsoid, relative to the shape bounds - // 1.0 / (1.0 - thickness) // thickness = percent of space that is between the min and max height. - // 1.0 / (1.0 - (shapeMaxHeight - renderMinHeight) / shapeMaxExtent) - // shapeMaxExtent / (shapeMaxExtent - (shapeMaxHeight - renderMinHeight)) - shaderUniforms.ellipsoidInverseInnerScaleUv = - shapeMaxExtent / (shapeMaxExtent - (shapeMaxHeight - renderMinHeight)); - } - - if (renderHasMaxHeight) { - shaderDefines["ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX"] = true; - shaderUniforms.ellipsoidInverseOuterScaleUv = - shapeMaxExtent / (shapeMaxExtent - (shapeMaxHeight - renderMaxHeight)); - } + shaderUniforms.clipMinMaxHeight = Cartesian2.fromElements( + (renderMinHeight - shapeMaxHeight) / shapeMaxExtent, + (renderMaxHeight - shapeMaxHeight) / shapeMaxExtent, + shaderUniforms.clipMinMaxHeight + ); // The percent of space that is between the inner and outer ellipsoid. const thickness = (shapeMaxHeight - shapeMinHeight) / shapeMaxExtent; diff --git a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl index 9adb67077520..d327ba770c7e 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl @@ -1,5 +1,5 @@ -// See IntersectionUtils.glsl for the definitions of Ray, Intersections, -// setIntersection, setIntersectionPair, INF_HIT, NO_HIT +// See IntersectionUtils.glsl for the definitions of Ray, NO_HIT, INF_HIT, Intersections, +// RayShapeIntersection, setSurfaceIntersection, setShapeIntersection, setIntersectionPair // See IntersectLongitude.glsl for the definitions of intersectHalfPlane, // intersectFlippedWedge, intersectRegularWedge @@ -14,8 +14,6 @@ #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF #define ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF -#define ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX -#define ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN #define ELLIPSOID_INTERSECTION_INDEX_LONGITUDE #define ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX #define ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN @@ -29,12 +27,7 @@ #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) uniform vec2 u_ellipsoidRenderLatitudeCosSqrHalfMinMax; #endif -#if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX) - uniform float u_ellipsoidInverseOuterScaleUv; -#endif -#if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN) - uniform float u_ellipsoidInverseInnerScaleUv; -#endif +uniform vec2 u_clipMinMaxHeight; vec2 intersectZPlane(Ray ray) { @@ -47,49 +40,41 @@ vec2 intersectZPlane(Ray ray) else return vec2(-INF_HIT, t); } -vec2 intersectUnitSphere(Ray ray) +RayShapeIntersection intersectHeight(in Ray ray, in float relativeHeight, in bool convex) { - vec3 o = ray.pos; - vec3 d = ray.dir; - - float b = dot(d, o); - float c = dot(o, o) - 1.0; - float det = b * b - c; - - if (det < 0.0) { - return vec2(NO_HIT); + // Scale the ray by the ellipsoid axes to make it a unit sphere + // Note: approximating ellipsoid + height as an ellipsoid + // Note: may be better to approximate radiiCorrection for small relative heights + vec3 radiiCorrection = u_ellipsoidRadiiUv / (u_ellipsoidRadiiUv + relativeHeight); + vec3 position = ray.pos * radiiCorrection; + vec3 direction = ray.dir * radiiCorrection; + + float a = dot(direction, direction); // ~ 1.0 (or maybe 4.0 if ray is scaled) + float b = dot(direction, position); // roughly inside [-1.0, 1.0] when zoomed in + float c = dot(position, position) - 1.0; // ~ 0.0 when zoomed in. Note cancellation problem! + float determinant = b * b - a * c; // ~ b * b when zoomed in + + if (determinant < 0.0) { + vec4 miss = vec4(normalize(direction), NO_HIT); + return RayShapeIntersection(miss, miss); } - det = sqrt(det); - float t1 = -b - det; - float t2 = -b + det; - float tmin = min(t1, t2); - float tmax = max(t1, t2); - - return vec2(tmin, tmax); -} + determinant = sqrt(determinant); -vec2 intersectUnitSphereUnnormalizedDirection(Ray ray) -{ - vec3 o = ray.pos; - vec3 d = ray.dir; - - float a = dot(d, d); - float b = dot(d, o); - float c = dot(o, o) - 1.0; - float det = b * b - a * c; - - if (det < 0.0) { - return vec2(NO_HIT); - } - - det = sqrt(det); - float t1 = (-b - det) / a; - float t2 = (-b + det) / a; + // Compute larger root using standard formula + float signB = b < 0.0 ? -1.0 : 1.0; + // The other root may suffer from subtractive cancellation in the standard formula. + // Compute it from the first root instead. + float t1 = (-b - signB * determinant) / a; + float t2 = c / (a * t1); float tmin = min(t1, t2); float tmax = max(t1, t2); - return vec2(tmin, tmax); + float directionScale = convex ? 1.0 : -1.0; + vec3 d1 = directionScale * normalize(position + tmin * direction); + vec3 d2 = directionScale * normalize(position + tmax * direction); + + return RayShapeIntersection(vec4(d1, tmin), vec4(d2, tmax)); } vec2 intersectDoubleEndedCone(Ray ray, float cosSqrHalfAngle) @@ -161,63 +146,47 @@ void intersectShape(in Ray ray, inout Intersections ix) { ray.pos = ray.pos * 2.0 - 1.0; ray.dir *= 2.0; - #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MAX) - Ray outerRay = Ray(ray.pos * u_ellipsoidInverseOuterScaleUv, ray.dir * u_ellipsoidInverseOuterScaleUv); - #else - Ray outerRay = ray; - #endif - // Outer ellipsoid - vec2 outerIntersect = intersectUnitSphereUnnormalizedDirection(outerRay); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX, outerIntersect); + RayShapeIntersection outerIntersect = intersectHeight(ray, u_clipMinMaxHeight.y, true); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MAX, outerIntersect); // Exit early if the outer ellipsoid was missed. - if (outerIntersect.x == NO_HIT) { + if (outerIntersect.entry.w == NO_HIT) { return; } - // Inner ellipsoid - #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_HEIGHT_MIN) - Ray innerRay = Ray(ray.pos * u_ellipsoidInverseInnerScaleUv, ray.dir * u_ellipsoidInverseInnerScaleUv); - vec2 innerIntersect = intersectUnitSphereUnnormalizedDirection(innerRay); - - if (innerIntersect == vec2(NO_HIT)) { - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN, innerIntersect); - } else { - // When the ellipsoid is large and thin it's possible for floating point math - // to cause the ray to intersect the inner ellipsoid before the outer ellipsoid. - // To prevent this from happening, clamp innerIntersect to outerIntersect and - // sandwich the inner ellipsoid intersection inside the outer ellipsoid intersection. - - // Without this special case, - // [outerMin, outerMax, innerMin, innerMax] will bubble sort to - // [outerMin, innerMin, outerMax, innerMax] which will cause the back - // side of the ellipsoid to be invisible because it will think the ray - // is still inside the inner (negative) ellipsoid after exiting the - // outer (positive) ellipsoid. - - // With this special case, - // [outerMin, innerMin, innerMax, outerMax] will bubble sort to - // [outerMin, innerMin, innerMax, outerMax] which will work correctly. - - // Note: If initializeIntersections() changes its sorting function - // from bubble sort to something else, this code may need to change. - - // In theory a similar fix is needed for cylinders, however it's more - // complicated to implement because the inner shape is allowed to be - // intersected first. - innerIntersect.x = max(innerIntersect.x, outerIntersect.x); - innerIntersect.y = min(innerIntersect.y, outerIntersect.y); - setIntersection(ix, 0, outerIntersect.x, true, true); // positive, enter - setIntersection(ix, 1, innerIntersect.x, false, true); // negative, enter - setIntersection(ix, 2, innerIntersect.y, false, false); // negative, exit - setIntersection(ix, 3, outerIntersect.y, true, false); // positive, exit - } - #endif + if (innerIntersect.entry.w == NO_HIT) { + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN, innerIntersect); + } else { + // When the ellipsoid is large and thin it's possible for floating point math + // to cause the ray to intersect the inner ellipsoid before the outer ellipsoid. + // To prevent this from happening, clamp innerIntersect to outerIntersect and + // sandwich the inner ellipsoid intersection inside the outer ellipsoid intersection. + + // Without this special case, + // [outerMin, outerMax, innerMin, innerMax] will bubble sort to + // [outerMin, innerMin, outerMax, innerMax] which will cause the back + // side of the ellipsoid to be invisible because it will think the ray + // is still inside the inner (negative) ellipsoid after exiting the + // outer (positive) ellipsoid. + + // With this special case, + // [outerMin, innerMin, innerMax, outerMax] will bubble sort to + // [outerMin, innerMin, innerMax, outerMax] which will work correctly. + + // Note: If initializeIntersections() changes its sorting function + // from bubble sort to something else, this code may need to change. + innerIntersect.entry.w = max(innerIntersect.entry.w, outerIntersect.entry.w); + innerIntersect.exit.w = min(innerIntersect.exit.w, outerIntersect.exit.w); + setSurfaceIntersection(ix, 0, outerIntersect.entry, true, true); // positive, enter + setSurfaceIntersection(ix, 1, innerIntersect.entry, false, true); // negative, enter + setSurfaceIntersection(ix, 2, innerIntersect.exit, false, false); // negative, exit + setSurfaceIntersection(ix, 3, outerIntersect.exit, true, false); // positive, exit + } // Flip the ray because the intersection function expects a cone growing towards +Z. #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) - Ray flippedRay = outerRay; + Ray flippedRay = ray; flippedRay.dir.z *= -1.0; flippedRay.pos.z *= -1.0; #endif From 4ea6fd722c4f0149ff7bdf711ee9b5e337b04fe2 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 20 Feb 2024 09:05:28 -0500 Subject: [PATCH 2/7] Return normals from ellipsoid voxel latitude bounds --- .../Source/Scene/VoxelEllipsoidShape.js | 20 +- .../Shaders/Voxels/IntersectEllipsoid.glsl | 198 +++++++++++++----- 2 files changed, 147 insertions(+), 71 deletions(-) diff --git a/packages/engine/Source/Scene/VoxelEllipsoidShape.js b/packages/engine/Source/Scene/VoxelEllipsoidShape.js index 745ca3f727a5..a8bece24aef1 100644 --- a/packages/engine/Source/Scene/VoxelEllipsoidShape.js +++ b/packages/engine/Source/Scene/VoxelEllipsoidShape.js @@ -100,7 +100,7 @@ function VoxelEllipsoidShape() { ellipsoidShapeUvLongitudeMinMaxMid: new Cartesian3(), ellipsoidUvToShapeUvLongitude: new Cartesian2(), ellipsoidUvToShapeUvLatitude: new Cartesian2(), - ellipsoidRenderLatitudeCosSqrHalfMinMax: new Cartesian2(), + ellipsoidRenderLatitudeSinMinMax: new Cartesian2(), ellipsoidInverseHeightDifferenceUv: 0.0, ellipseInnerRadiiUv: new Cartesian2(), clipMinMaxHeight: new Cartesian2(), @@ -643,18 +643,12 @@ VoxelEllipsoidShape.prototype.update = function ( } } - const minCosHalfAngleSqr = Math.pow( - Math.cos(CesiumMath.PI_OVER_TWO - Math.abs(renderMinLatitude)), - 2.0 - ); - const maxCosHalfAngleSqr = Math.pow( - Math.cos(CesiumMath.PI_OVER_TWO - Math.abs(renderMaxLatitude)), - 2.0 - ); - shaderUniforms.ellipsoidRenderLatitudeCosSqrHalfMinMax = Cartesian2.fromElements( - minCosHalfAngleSqr, - maxCosHalfAngleSqr, - shaderUniforms.ellipsoidRenderLatitudeCosSqrHalfMinMax + const sinMinLatitude = Math.sin(renderMinLatitude); + const sinMaxLatitude = Math.sin(renderMaxLatitude); + shaderUniforms.ellipsoidRenderLatitudeSinMinMax = Cartesian2.fromElements( + sinMinLatitude, + sinMaxLatitude, + shaderUniforms.ellipsoidRenderLatitudeSinMinMax ); } diff --git a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl index d327ba770c7e..efd440270c88 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl @@ -1,5 +1,5 @@ // See IntersectionUtils.glsl for the definitions of Ray, NO_HIT, INF_HIT, Intersections, -// RayShapeIntersection, setSurfaceIntersection, setShapeIntersection, setIntersectionPair +// RayShapeIntersection, setSurfaceIntersection, setShapeIntersection // See IntersectLongitude.glsl for the definitions of intersectHalfPlane, // intersectFlippedWedge, intersectRegularWedge @@ -24,20 +24,23 @@ #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LONGITUDE) uniform vec2 u_ellipsoidRenderLongitudeMinMax; #endif -#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) - uniform vec2 u_ellipsoidRenderLatitudeCosSqrHalfMinMax; -#endif +uniform vec2 u_ellipsoidRenderLatitudeSinMinMax; uniform vec2 u_clipMinMaxHeight; -vec2 intersectZPlane(Ray ray) -{ - float o = ray.pos.z; - float d = ray.dir.z; - float t = -o / d; - float s = sign(o); +RayShapeIntersection intersectZPlane(in Ray ray, in float z) { + float t = -ray.pos.z / ray.dir.z; + + bool startsOutside = sign(ray.pos.z) == sign(z); + bool entry = (t >= 0.0) != startsOutside; - if (t >= 0.0 != s >= 0.0) return vec2(t, +INF_HIT); - else return vec2(-INF_HIT, t); + vec4 intersect = vec4(0.0, 0.0, z, t); + vec4 farSide = vec4(normalize(ray.dir), INF_HIT); + + if (entry) { + return RayShapeIntersection(intersect, farSide); + } else { + return RayShapeIntersection(-1.0 * farSide, intersect); + } } RayShapeIntersection intersectHeight(in Ray ray, in float relativeHeight, in bool convex) @@ -77,67 +80,144 @@ RayShapeIntersection intersectHeight(in Ray ray, in float relativeHeight, in boo return RayShapeIntersection(vec4(d1, tmin), vec4(d2, tmax)); } -vec2 intersectDoubleEndedCone(Ray ray, float cosSqrHalfAngle) +/** + * Given a circular cone around the z-axis, with apex at the origin, + * find the parametric distance(s) along a ray where that ray intersects + * the cone. + * The cone opening angle is described by the squared cosine of + * its half-angle (the angle between the Z-axis and the surface) + */ +vec2 intersectDoubleEndedCone(in Ray ray, in float cosSqrHalfAngle) { vec3 o = ray.pos; vec3 d = ray.dir; - float a = d.z * d.z - dot(d, d) * cosSqrHalfAngle; - float b = d.z * o.z - dot(o, d) * cosSqrHalfAngle; - float c = o.z * o.z - dot(o, o) * cosSqrHalfAngle; - float det = b * b - a * c; + float sinSqrHalfAngle = 1.0 - cosSqrHalfAngle; + // a = d.z * d.z - dot(d, d) * cosSqrHalfAngle; + float aSin = d.z * d.z * sinSqrHalfAngle; + float aCos = -dot(d.xy, d.xy) * cosSqrHalfAngle; + float a = aSin + aCos; + // b = d.z * o.z - dot(o, d) * cosSqrHalfAngle; + float bSin = d.z * o.z * sinSqrHalfAngle; + float bCos = -dot(o.xy, d.xy) * cosSqrHalfAngle; + float b = bSin + bCos; + // c = o.z * o.z - dot(o, o) * cosSqrHalfAngle; + float cSin = o.z * o.z * sinSqrHalfAngle; + float cCos = -dot(o.xy, o.xy) * cosSqrHalfAngle; + float c = cSin + cCos; + // determinant = b * b - a * c. But bSin * bSin = aSin * cSin. + // Avoid subtractive cancellation by expanding to eliminate these terms + float determinant = 2.0 * bSin * bCos + bCos * bCos - aSin * cCos - aCos * cSin - aCos * cCos; - if (det < 0.0) { + if (determinant < 0.0) { return vec2(NO_HIT); + } else if (a == 0.0) { + // Ray is parallel to cone surface + return (b == 0.0) + ? vec2(NO_HIT) // Ray is on cone surface + : vec2(-0.5 * c / b, NO_HIT); } - det = sqrt(det); - float t1 = (-b - det) / a; - float t2 = (-b + det) / a; + determinant = sqrt(determinant); + + // Compute larger root using standard formula + float signB = b < 0.0 ? -1.0 : 1.0; + float t1 = (-b - signB * determinant) / a; + // The other root may suffer from subtractive cancellation in the standard formula. + // Compute it from the first root instead. + float t2 = c / (a * t1); float tmin = min(t1, t2); float tmax = max(t1, t2); return vec2(tmin, tmax); } -vec4 intersectFlippedCone(Ray ray, float cosSqrHalfAngle) { +/** + * Given a point on a conical surface, find the surface normal at that point. + */ +vec3 getConeNormal(in vec3 p, in bool convex) { + // Start with radial component pointing toward z-axis + vec2 radial = -abs(p.z) * normalize(p.xy); + // Z component points toward opening of cone + float zSign = (p.z < 0.0) ? -1.0 : 1.0; + float z = length(p.xy) * zSign; + // Flip normal if shape is convex + float flip = (convex) ? -1.0 : 1.0; + return normalize(vec3(radial, z) * flip); +} + +void intersectFlippedCone(in Ray ray, in float cosHalfAngle, out RayShapeIntersection intersections[2]) { + float cosSqrHalfAngle = cosHalfAngle * cosHalfAngle; vec2 intersect = intersectDoubleEndedCone(ray, cosSqrHalfAngle); + vec4 miss = vec4(normalize(ray.dir), NO_HIT); + vec4 farSide = vec4(normalize(ray.dir), INF_HIT); + + // Initialize output with no intersections + intersections[0].entry = -1.0 * farSide; + intersections[0].exit = farSide; + intersections[1].entry = miss; + intersections[1].exit = miss; + if (intersect.x == NO_HIT) { - return vec4(-INF_HIT, +INF_HIT, NO_HIT, NO_HIT); + return; } - vec3 o = ray.pos; - vec3 d = ray.dir; + // Find the points of intersection float tmin = intersect.x; float tmax = intersect.y; - float zmin = o.z + tmin * d.z; - float zmax = o.z + tmax * d.z; - - // One interval - if (zmin < 0.0 && zmax < 0.0) return vec4(-INF_HIT, +INF_HIT, NO_HIT, NO_HIT); - else if (zmin < 0.0) return vec4(-INF_HIT, tmax, NO_HIT, NO_HIT); - else if (zmax < 0.0) return vec4(tmin, +INF_HIT, NO_HIT, NO_HIT); - // Two intervals - else return vec4(-INF_HIT, tmin, tmax, +INF_HIT); + vec3 p0 = ray.pos + tmin * ray.dir; + vec3 p1 = ray.pos + tmax * ray.dir; + + vec4 intersect0 = vec4(getConeNormal(p0, true), tmin); + vec4 intersect1 = vec4(getConeNormal(p1, true), tmax); + + bool p0InShadowCone = sign(p0.z) != sign(cosHalfAngle); + bool p1InShadowCone = sign(p1.z) != sign(cosHalfAngle); + + if (p0InShadowCone && p1InShadowCone) { + // no valid intersections + } else if (p0InShadowCone) { + intersections[0].exit = intersect1; + } else if (p1InShadowCone) { + intersections[0].entry = intersect0; + } else { + intersections[0].exit = intersect0; + intersections[1].entry = intersect1; + intersections[1].exit = farSide; + } } -vec2 intersectRegularCone(Ray ray, float cosSqrHalfAngle) { +RayShapeIntersection intersectRegularCone(in Ray ray, in float cosHalfAngle, in bool convex) { + float cosSqrHalfAngle = cosHalfAngle * cosHalfAngle; vec2 intersect = intersectDoubleEndedCone(ray, cosSqrHalfAngle); + vec4 miss = vec4(normalize(ray.dir), NO_HIT); + vec4 farSide = vec4(normalize(ray.dir), INF_HIT); + if (intersect.x == NO_HIT) { - return vec2(NO_HIT); + return RayShapeIntersection(miss, miss); } - vec3 o = ray.pos; - vec3 d = ray.dir; + // Find the points of intersection float tmin = intersect.x; float tmax = intersect.y; - float zmin = o.z + tmin * d.z; - float zmax = o.z + tmax * d.z; + vec3 p0 = ray.pos + tmin * ray.dir; + vec3 p1 = ray.pos + tmax * ray.dir; + + vec4 intersect0 = vec4(getConeNormal(p0, convex), tmin); + vec4 intersect1 = vec4(getConeNormal(p1, convex), tmax); - if (zmin < 0.0 && zmax < 0.0) return vec2(NO_HIT); - else if (zmin < 0.0) return vec2(tmax, +INF_HIT); - else if (zmax < 0.0) return vec2(-INF_HIT, tmin); - else return vec2(tmin, tmax); + bool p0InShadowCone = sign(p0.z) != sign(cosHalfAngle); + bool p1InShadowCone = sign(p1.z) != sign(cosHalfAngle); + + if (p0InShadowCone && p1InShadowCone) { + return RayShapeIntersection(miss, miss); + } else if (p0InShadowCone) { + return RayShapeIntersection(intersect1, farSide); + } else if (p1InShadowCone) { + return RayShapeIntersection(-1.0 * farSide, intersect0); + } else { + return RayShapeIntersection(intersect0, intersect1); + } } void intersectShape(in Ray ray, inout Intersections ix) { @@ -193,28 +273,30 @@ void intersectShape(in Ray ray, inout Intersections ix) { // Bottom cone #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF) - vec2 bottomConeIntersection = intersectRegularCone(flippedRay, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.x); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection); + RayShapeIntersection bottomConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.x, false); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF) - vec2 bottomConeIntersection = intersectZPlane(flippedRay); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection); + RayShapeIntersection bottomConeIntersection = intersectZPlane(ray, -1.0); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF) - vec4 bottomConeIntersection = intersectFlippedCone(ray, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.x); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 0, bottomConeIntersection.xy); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 1, bottomConeIntersection.zw); + RayShapeIntersection bottomConeIntersections[2]; + intersectFlippedCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.x, bottomConeIntersections); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 0, bottomConeIntersections[0]); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 1, bottomConeIntersections[1]); #endif // Top cone #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF) - vec4 topConeIntersection = intersectFlippedCone(flippedRay, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.y); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 0, topConeIntersection.xy); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 1, topConeIntersection.zw); + RayShapeIntersection topConeIntersections[2]; + intersectFlippedCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.y, topConeIntersections); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 0, topConeIntersections[0]); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 1, topConeIntersections[1]); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF) - vec2 topConeIntersection = intersectZPlane(ray); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection); + RayShapeIntersection topConeIntersection = intersectZPlane(ray, 1.0); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF) - vec2 topConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeCosSqrHalfMinMax.y); - setIntersectionPair(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection); + RayShapeIntersection topConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.y, false); + setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection); #endif // Wedge From 92ed4ea90763780b902f0f636ac73771991ea996 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 20 Feb 2024 11:20:32 -0500 Subject: [PATCH 3/7] Fix bugs in ellipsoid voxel normals --- packages/engine/Source/Scene/VoxelRenderResources.js | 4 ++-- .../Source/Shaders/Voxels/IntersectEllipsoid.glsl | 11 +++++++---- .../Source/Shaders/Voxels/IntersectionUtils.glsl | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/engine/Source/Scene/VoxelRenderResources.js b/packages/engine/Source/Scene/VoxelRenderResources.js index 53562a65e39e..ac9bb204f8c9 100644 --- a/packages/engine/Source/Scene/VoxelRenderResources.js +++ b/packages/engine/Source/Scene/VoxelRenderResources.js @@ -132,17 +132,17 @@ function VoxelRenderResources(primitive) { ]); } else if (shapeType === "CYLINDER") { shaderBuilder.addFragmentLines([ + convertUvToCylinder, IntersectLongitude, IntersectCylinder, Intersection, - convertUvToCylinder, ]); } else if (shapeType === "ELLIPSOID") { shaderBuilder.addFragmentLines([ + convertUvToEllipsoid, IntersectLongitude, IntersectEllipsoid, Intersection, - convertUvToEllipsoid, ]); } diff --git a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl index efd440270c88..4ed141b6f09c 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl @@ -235,6 +235,9 @@ void intersectShape(in Ray ray, inout Intersections ix) { return; } + // Inner ellipsoid + RayShapeIntersection innerIntersect = intersectHeight(ray, u_clipMinMaxHeight.x, false); + if (innerIntersect.entry.w == NO_HIT) { setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_HEIGHT_MIN, innerIntersect); } else { @@ -273,14 +276,14 @@ void intersectShape(in Ray ray, inout Intersections ix) { // Bottom cone #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF) - RayShapeIntersection bottomConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.x, false); + RayShapeIntersection bottomConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeSinMinMax.x, false); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_EQUAL_HALF) RayShapeIntersection bottomConeIntersection = intersectZPlane(ray, -1.0); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN, bottomConeIntersection); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_OVER_HALF) RayShapeIntersection bottomConeIntersections[2]; - intersectFlippedCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.x, bottomConeIntersections); + intersectFlippedCone(ray, u_ellipsoidRenderLatitudeSinMinMax.x, bottomConeIntersections); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 0, bottomConeIntersections[0]); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MIN + 1, bottomConeIntersections[1]); #endif @@ -288,14 +291,14 @@ void intersectShape(in Ray ray, inout Intersections ix) { // Top cone #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_UNDER_HALF) RayShapeIntersection topConeIntersections[2]; - intersectFlippedCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.y, topConeIntersections); + intersectFlippedCone(ray, u_ellipsoidRenderLatitudeSinMinMax.y, topConeIntersections); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 0, topConeIntersections[0]); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX + 1, topConeIntersections[1]); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_EQUAL_HALF) RayShapeIntersection topConeIntersection = intersectZPlane(ray, 1.0); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection); #elif defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MAX_OVER_HALF) - RayShapeIntersection topConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeCosHalfMinMax.y, false); + RayShapeIntersection topConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeSinMinMax.y, false); setShapeIntersection(ix, ELLIPSOID_INTERSECTION_INDEX_LATITUDE_MAX, topConeIntersection); #endif diff --git a/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl b/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl index c10465ec0bf5..0b2e9e3d44dc 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl @@ -57,7 +57,7 @@ vec4 encodeIntersectionType(vec4 intersection, int index, bool entry) // Use defines instead of real functions because WebGL1 cannot access array with non-constant index. #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)) #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) -#define setSurfaceIntersection(/*inout Intersections*/ ix, /*int*/ index, /*vec4*/ intersection) (ix).intersections[(index)] = intersection; +#define setSurfaceIntersection(/*inout Intersections*/ ix, /*int*/ index, /*vec4*/ intersection, /*bool*/ positive, /*bool*/ enter) (ix).intersections[(index)] = encodeIntersectionType((intersection), int(!positive), (enter)) #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) #if (INTERSECTION_COUNT > 1) From 08409cc94d2bb4774a3006da0cb841e59642f8d5 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 20 Feb 2024 13:28:20 -0500 Subject: [PATCH 4/7] Return normals from voxel cylinder shape intersections --- .../engine/Source/Scene/VoxelCylinderShape.js | 82 ++------- .../Shaders/Voxels/IntersectCylinder.glsl | 160 +++++++----------- .../Shaders/Voxels/IntersectionUtils.glsl | 30 ++++ 3 files changed, 108 insertions(+), 164 deletions(-) diff --git a/packages/engine/Source/Scene/VoxelCylinderShape.js b/packages/engine/Source/Scene/VoxelCylinderShape.js index 893f2d037991..3a88bccfc601 100644 --- a/packages/engine/Source/Scene/VoxelCylinderShape.js +++ b/packages/engine/Source/Scene/VoxelCylinderShape.js @@ -96,9 +96,8 @@ function VoxelCylinderShape() { * @readonly */ this.shaderUniforms = { - cylinderUvToRenderBoundsScale: new Cartesian3(), - cylinderUvToRenderBoundsTranslate: new Cartesian3(), - cylinderUvToRenderRadiusMin: 0.0, + cylinderRenderHeightMinMax: new Cartesian2(), + cylinderRenderRadiusMinMax: new Cartesian2(), cylinderRenderAngleMinMax: new Cartesian2(), cylinderUvToShapeUvRadius: new Cartesian2(), cylinderUvToShapeUvHeight: new Cartesian2(), @@ -113,10 +112,7 @@ function VoxelCylinderShape() { */ this.shaderDefines = { CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MIN: undefined, - CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MAX: undefined, CYLINDER_HAS_RENDER_BOUNDS_RADIUS_FLAT: undefined, - CYLINDER_HAS_RENDER_BOUNDS_HEIGHT: undefined, - CYLINDER_HAS_RENDER_BOUNDS_HEIGHT_FLAT: undefined, CYLINDER_HAS_RENDER_BOUNDS_ANGLE: undefined, CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_EQUAL_ZERO: undefined, CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_UNDER_HALF: undefined, @@ -143,17 +139,6 @@ function VoxelCylinderShape() { } const scratchScale = new Cartesian3(); -const scratchBoundsTranslation = new Cartesian3(); -const scratchBoundsScale = new Cartesian3(); -const scratchBoundsScaleMatrix = new Matrix3(); -const scratchTransformLocalToBounds = new Matrix4(); -const scratchTransformUvToBounds = new Matrix4(); - -const transformUvToLocal = Matrix4.fromRotationTranslation( - Matrix3.fromUniformScale(2.0, new Matrix3()), - new Cartesian3(-1.0, -1.0, -1.0), - new Matrix4() -); /** * Update the shape's state. @@ -339,11 +324,7 @@ VoxelCylinderShape.prototype.update = function ( epsilonAngleDiscontinuity ); - const renderIsDefaultMaxRadius = renderMaxRadius === defaultMaxRadius; const renderIsDefaultMinRadius = renderMinRadius === defaultMinRadius; - const renderIsDefaultHeight = - renderMinHeight === defaultMinHeight && - renderMaxHeight === defaultMaxHeight; const renderIsAngleReversed = renderMaxAngle < renderMinAngle; const renderAngleRange = renderMaxAngle - renderMinAngle + renderIsAngleReversed * defaultAngleRange; @@ -376,22 +357,16 @@ VoxelCylinderShape.prototype.update = function ( shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MIN"] = true; shaderDefines["CYLINDER_INTERSECTION_INDEX_RADIUS_MIN"] = intersectionCount; intersectionCount += 1; - - shaderUniforms.cylinderUvToRenderRadiusMin = - renderMaxRadius / renderMinRadius; - } - if (!renderIsDefaultMaxRadius) { - shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MAX"] = true; } + shaderUniforms.cylinderRenderRadiusMinMax = Cartesian2.fromElements( + renderMinRadius, + renderMaxRadius, + shaderUniforms.cylinderRenderRadiusMinMax + ); + if (renderMinRadius === renderMaxRadius) { shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_RADIUS_FLAT"] = true; } - if (!renderIsDefaultHeight) { - shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_HEIGHT"] = true; - } - if (renderMinHeight === renderMaxHeight) { - shaderDefines["CYLINDER_HAS_RENDER_BOUNDS_HEIGHT_FLAT"] = true; - } if (!shapeIsDefaultRadius) { shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_RADIUS"] = true; @@ -442,42 +417,11 @@ VoxelCylinderShape.prototype.update = function ( shaderUniforms.cylinderUvToShapeUvHeight ); } - - if (!renderIsDefaultMaxRadius || !renderIsDefaultHeight) { - const heightScale = 0.5 * (renderMaxHeight - renderMinHeight); - const scaleLocalToBounds = Cartesian3.fromElements( - 1.0 / renderMaxRadius, - 1.0 / renderMaxRadius, - 1.0 / (heightScale === 0.0 ? 1.0 : heightScale), - scratchBoundsScale - ); - // -inverse(scale) * translation // affine inverse - // -inverse(scale) * 0.5 * (minHeight + maxHeight) - const translateLocalToBounds = Cartesian3.fromElements( - 0.0, - 0.0, - -scaleLocalToBounds.z * 0.5 * (renderMinHeight + renderMaxHeight), - scratchBoundsTranslation - ); - const transformLocalToBounds = Matrix4.fromRotationTranslation( - Matrix3.fromScale(scaleLocalToBounds, scratchBoundsScaleMatrix), - translateLocalToBounds, - scratchTransformLocalToBounds - ); - const transformUvToBounds = Matrix4.multiplyTransformation( - transformLocalToBounds, - transformUvToLocal, - scratchTransformUvToBounds - ); - shaderUniforms.cylinderUvToRenderBoundsScale = Matrix4.getScale( - transformUvToBounds, - shaderUniforms.cylinderUvToRenderBoundsScale - ); - shaderUniforms.cylinderUvToRenderBoundsTranslate = Matrix4.getTranslation( - transformUvToBounds, - shaderUniforms.cylinderUvToRenderBoundsTranslate - ); - } + shaderUniforms.cylinderRenderHeightMinMax = Cartesian2.fromElements( + renderMinHeight, + renderMaxHeight, + shaderUniforms.cylinderRenderHeightMinMax + ); if (shapeIsAngleReversed) { shaderDefines["CYLINDER_HAS_SHAPE_BOUNDS_ANGLE_MIN_MAX_REVERSED"] = true; diff --git a/packages/engine/Source/Shaders/Voxels/IntersectCylinder.glsl b/packages/engine/Source/Shaders/Voxels/IntersectCylinder.glsl index b35223e00286..a1848b433d6d 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectCylinder.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectCylinder.glsl @@ -1,14 +1,12 @@ // See IntersectionUtils.glsl for the definitions of Ray, NO_HIT, -// setIntersection, setIntersectionPair, setShapeIntersection +// RayShapeIntersection, setSurfaceIntersection, setShapeIntersection, +// intersectIntersections // See IntersectLongitude.glsl for the definitions of intersectHalfPlane, // intersectFlippedWedge, intersectRegularWedge /* Cylinder defines (set in Scene/VoxelCylinderShape.js) #define CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MIN -#define CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MAX #define CYLINDER_HAS_RENDER_BOUNDS_RADIUS_FLAT -#define CYLINDER_HAS_RENDER_BOUNDS_HEIGHT -#define CYLINDER_HAS_RENDER_BOUNDS_HEIGHT_FLAT #define CYLINDER_HAS_RENDER_BOUNDS_ANGLE #define CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_UNDER_HALF #define CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_OVER_HALF @@ -20,114 +18,87 @@ */ // Cylinder uniforms -#if defined(CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MAX) || defined(CYLINDER_HAS_RENDER_BOUNDS_HEIGHT) - uniform vec3 u_cylinderUvToRenderBoundsScale; - uniform vec3 u_cylinderUvToRenderBoundsTranslate; -#endif -#if defined(CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MIN) && !defined(CYLINDER_HAS_RENDER_BOUNDS_RADIUS_FLAT) - uniform float u_cylinderUvToRenderRadiusMin; -#endif +uniform vec2 u_cylinderRenderRadiusMinMax; +uniform vec2 u_cylinderRenderHeightMinMax; #if defined(CYLINDER_HAS_RENDER_BOUNDS_ANGLE) uniform vec2 u_cylinderRenderAngleMinMax; #endif -vec2 intersectUnitCylinder(Ray ray) +/** + * Find the intersection of a ray with the volume defined by two planes of constant z + */ +RayShapeIntersection intersectHeightBounds(in Ray ray, in vec2 minMaxHeight, in bool convex) { - vec3 o = ray.pos; - vec3 d = ray.dir; + float zPosition = ray.pos.z; + float zDirection = ray.dir.z; - float a = dot(d.xy, d.xy); - float b = dot(o.xy, d.xy); - float c = dot(o.xy, o.xy) - 1.0; - float det = b * b - a * c; + float tmin = (minMaxHeight.x - zPosition) / zDirection; + float tmax = (minMaxHeight.y - zPosition) / zDirection; - if (det < 0.0) { - return vec2(NO_HIT); - } + // Normals point outside the volume + float signFlip = convex ? 1.0 : -1.0; + vec4 intersectMin = vec4(0.0, 0.0, -1.0 * signFlip, tmin); + vec4 intersectMax = vec4(0.0, 0.0, 1.0 * signFlip, tmax); - det = sqrt(det); - float ta = (-b - det) / a; - float tb = (-b + det) / a; - float t1 = min(ta, tb); - float t2 = max(ta, tb); + bool topEntry = zDirection < 0.0; + vec4 entry = topEntry ? intersectMax : intersectMin; + vec4 exit = topEntry ? intersectMin : intersectMax; - float z1 = o.z + t1 * d.z; - float z2 = o.z + t2 * d.z; - - if (abs(z1) >= 1.0) - { - float tCap = (sign(z1) - o.z) / d.z; - t1 = abs(b + a * tCap) < det ? tCap : NO_HIT; - } - - if (abs(z2) >= 1.0) - { - float tCap = (sign(z2) - o.z) / d.z; - t2 = abs(b + a * tCap) < det ? tCap : NO_HIT; - } - - return vec2(t1, t2); + return RayShapeIntersection(entry, exit); } -vec2 intersectUnitCircle(Ray ray) { - vec3 o = ray.pos; - vec3 d = ray.dir; +/** + * Find the intersection of a ray with a right cylindrical surface of a given radius + * about the z-axis. + */ +RayShapeIntersection intersectCylinder(in Ray ray, in float radius, in bool convex) +{ + vec2 position = ray.pos.xy; + vec2 direction = ray.dir.xy; - float t = -o.z / d.z; - vec2 zPlanePos = o.xy + d.xy * t; - float distSqr = dot(zPlanePos, zPlanePos); + float a = dot(direction, direction); + float b = dot(position, direction); + float c = dot(position, position) - radius * radius; + float determinant = b * b - a * c; - if (distSqr > 1.0) { - return vec2(NO_HIT); + if (determinant < 0.0) { + vec4 miss = vec4(normalize(ray.dir), NO_HIT); + return RayShapeIntersection(miss, miss); } - return vec2(t, t); + determinant = sqrt(determinant); + float t1 = (-b - determinant) / a; + float t2 = (-b + determinant) / a; + float signFlip = convex ? 1.0 : -1.0; + vec4 intersect1 = vec4(normalize(position + t1 * direction) * signFlip, 0.0, t1); + vec4 intersect2 = vec4(normalize(position + t2 * direction) * signFlip, 0.0, t2); + + return RayShapeIntersection(intersect1, intersect2); } -vec2 intersectInfiniteUnitCylinder(Ray ray) +/** + * Find the intersection of a ray with a right cylindrical solid of given + * radius and height bounds. NOTE: The shape is assumed to be convex. + */ +RayShapeIntersection intersectBoundedCylinder(in Ray ray, in float radius, in vec2 minMaxHeight) { - vec3 o = ray.pos; - vec3 d = ray.dir; - - float a = dot(d.xy, d.xy); - float b = dot(o.xy, d.xy); - float c = dot(o.xy, o.xy) - 1.0; - float det = b * b - a * c; - - if (det < 0.0) { - return vec2(NO_HIT); - } - - det = sqrt(det); - float t1 = (-b - det) / a; - float t2 = (-b + det) / a; - float tmin = min(t1, t2); - float tmax = max(t1, t2); - - return vec2(tmin, tmax); + RayShapeIntersection cylinderIntersection = intersectCylinder(ray, radius, true); + RayShapeIntersection heightBoundsIntersection = intersectHeightBounds(ray, minMaxHeight, true); + return intersectIntersections(ray, cylinderIntersection, heightBoundsIntersection); } void intersectShape(Ray ray, inout Intersections ix) { - #if defined(CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MAX) || defined(CYLINDER_HAS_RENDER_BOUNDS_HEIGHT) - ray.pos = ray.pos * u_cylinderUvToRenderBoundsScale + u_cylinderUvToRenderBoundsTranslate; - ray.dir *= u_cylinderUvToRenderBoundsScale; - #else - // Position is converted from [0,1] to [-1,+1] because shape intersections assume unit space is [-1,+1]. - // Direction is scaled as well to be in sync with position. - ray.pos = ray.pos * 2.0 - 1.0; - ray.dir *= 2.0; - #endif + // Position is converted from [0,1] to [-1,+1] because shape intersections assume unit space is [-1,+1]. + // Direction is scaled as well to be in sync with position. + ray.pos = ray.pos * 2.0 - 1.0; + ray.dir *= 2.0; - #if defined(CYLINDER_HAS_RENDER_BOUNDS_HEIGHT_FLAT) - vec2 outerIntersect = intersectUnitCircle(ray); - #else - vec2 outerIntersect = intersectUnitCylinder(ray); - #endif + RayShapeIntersection outerIntersect = intersectBoundedCylinder(ray, u_cylinderRenderRadiusMinMax.y, u_cylinderRenderHeightMinMax); - setIntersectionPair(ix, CYLINDER_INTERSECTION_INDEX_RADIUS_MAX, outerIntersect); + setShapeIntersection(ix, CYLINDER_INTERSECTION_INDEX_RADIUS_MAX, outerIntersect); - if (outerIntersect.x == NO_HIT) { + if (outerIntersect.entry.w == NO_HIT) { return; } @@ -148,15 +119,14 @@ void intersectShape(Ray ray, inout Intersections ix) // Note: If initializeIntersections() changes its sorting function // from bubble sort to something else, this code may need to change. - vec2 innerIntersect = intersectInfiniteUnitCylinder(ray); - setIntersection(ix, 0, outerIntersect.x, true, true); // positive, enter - setIntersection(ix, 1, innerIntersect.x, false, true); // negative, enter - setIntersection(ix, 2, innerIntersect.y, false, false); // negative, exit - setIntersection(ix, 3, outerIntersect.y, true, false); // positive, exit + RayShapeIntersection innerIntersect = intersectCylinder(ray, 1.0, false); + setSurfaceIntersection(ix, 0, outerIntersect.entry, true, true); // positive, enter + setSurfaceIntersection(ix, 1, innerIntersect.entry, false, true); // negative, enter + setSurfaceIntersection(ix, 2, innerIntersect.exit, false, false); // negative, exit + setSurfaceIntersection(ix, 3, outerIntersect.exit, true, false); // positive, exit #elif defined(CYLINDER_HAS_RENDER_BOUNDS_RADIUS_MIN) - Ray innerRay = Ray(ray.pos * u_cylinderUvToRenderRadiusMin, ray.dir * u_cylinderUvToRenderRadiusMin); - vec2 innerIntersect = intersectInfiniteUnitCylinder(innerRay); - setIntersectionPair(ix, CYLINDER_INTERSECTION_INDEX_RADIUS_MIN, innerIntersect); + RayShapeIntersection innerIntersect = intersectCylinder(ray, u_cylinderRenderRadiusMinMax.x, false); + setShapeIntersection(ix, CYLINDER_INTERSECTION_INDEX_RADIUS_MIN, innerIntersect); #endif #if defined(CYLINDER_HAS_RENDER_BOUNDS_ANGLE_RANGE_UNDER_HALF) diff --git a/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl b/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl index 0b2e9e3d44dc..04ebeb75c5ab 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl @@ -20,6 +20,36 @@ struct RayShapeIntersection { vec4 exit; }; +vec4 intersectionMin(in vec4 intersect0, in vec4 intersect1) +{ + if (intersect0.w == NO_HIT) { + return intersect1; + } + return (intersect0.w <= intersect1.w) ? intersect0 : intersect1; +} + +vec4 intersectionMax(in vec4 intersect0, in vec4 intersect1) +{ + return (intersect0.w >= intersect1.w) ? intersect0 : intersect1; +} + +RayShapeIntersection intersectIntersections(in Ray ray, in RayShapeIntersection intersect0, in RayShapeIntersection intersect1) +{ + bool missed = (intersect0.entry.w == NO_HIT) || + (intersect1.entry.w == NO_HIT) || + (intersect0.exit.w < intersect1.entry.w) || + (intersect0.entry.w > intersect1.exit.w); + if (missed) { + vec4 miss = vec4(normalize(ray.dir), NO_HIT); + return RayShapeIntersection(miss, miss); + } + + vec4 entry = intersectionMax(intersect0.entry, intersect1.entry); + vec4 exit = intersectionMin(intersect0.exit, intersect1.exit); + + return RayShapeIntersection(entry, exit); +} + struct Intersections { // Don't access these member variables directly - call the functions instead. From 0b4ca316143de7705373bcd4c4db4efc5ef34b20 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 20 Feb 2024 20:40:45 -0500 Subject: [PATCH 5/7] Fixes for voxel shape normals --- .../engine/Source/Scene/VoxelCylinderShape.js | 2 +- .../Source/Shaders/Voxels/IntersectEllipsoid.glsl | 7 ------- .../engine/Source/Shaders/Voxels/VoxelFS.glsl | 15 +++++++++------ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/engine/Source/Scene/VoxelCylinderShape.js b/packages/engine/Source/Scene/VoxelCylinderShape.js index 3a88bccfc601..4ad60f2ff35b 100644 --- a/packages/engine/Source/Scene/VoxelCylinderShape.js +++ b/packages/engine/Source/Scene/VoxelCylinderShape.js @@ -445,7 +445,7 @@ VoxelCylinderShape.prototype.update = function ( shaderUniforms.cylinderRenderAngleMinMax = Cartesian2.fromElements( renderMinAngle, renderMaxAngle, - shaderUniforms.cylinderAngleMinMax + shaderUniforms.cylinderRenderAngleMinMax ); } diff --git a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl index 4ed141b6f09c..0135190a60fc 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl @@ -267,13 +267,6 @@ void intersectShape(in Ray ray, inout Intersections ix) { setSurfaceIntersection(ix, 3, outerIntersect.exit, true, false); // positive, exit } - // Flip the ray because the intersection function expects a cone growing towards +Z. - #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) - Ray flippedRay = ray; - flippedRay.dir.z *= -1.0; - flippedRay.pos.z *= -1.0; - #endif - // Bottom cone #if defined(ELLIPSOID_HAS_RENDER_BOUNDS_LATITUDE_MIN_UNDER_HALF) RayShapeIntersection bottomConeIntersection = intersectRegularCone(ray, u_ellipsoidRenderLatitudeSinMinMax.x, false); diff --git a/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl b/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl index 5a7b82e7f96d..c977fb5af48a 100644 --- a/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl +++ b/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl @@ -28,17 +28,20 @@ float hash(vec2 p) } #endif -vec4 getStepSize(in SampleData sampleData, in Ray viewRay, in RayShapeIntersection shapeIntersection) { +vec4 getStepSize(in SampleData sampleData, in Ray viewRay, in RayShapeIntersection shapeIntersection, in float currT) { #if defined(SHAPE_BOX) Box voxelBox = constructVoxelBox(sampleData.tileCoords, sampleData.tileUv); RayShapeIntersection voxelIntersection = intersectBox(viewRay, voxelBox); - vec4 entry = shapeIntersection.entry.w >= voxelIntersection.entry.w ? shapeIntersection.entry : voxelIntersection.entry; + vec4 entry = intersectionMax(shapeIntersection.entry, voxelIntersection.entry); float exit = min(voxelIntersection.exit.w, shapeIntersection.exit.w); float dt = (exit - entry.w) * RAY_SCALE; return vec4(normalize(entry.xyz), dt); #else float dimAtLevel = pow(2.0, float(sampleData.tileCoords.w)); - return vec4(viewRay.dir, u_stepSize / dimAtLevel); + float shapeEntryT = shapeIntersection.entry.w * RAY_SCALE; + float constantStep = u_stepSize / dimAtLevel; + vec3 normal = (currT < shapeEntryT + constantStep) ? shapeIntersection.entry.xyz : viewRay.dir; + return vec4(normalize(normal), constantStep); #endif } @@ -74,7 +77,7 @@ void main() TraversalData traversalData; SampleData sampleDatas[SAMPLE_COUNT]; traverseOctreeFromBeginning(positionUvShapeSpace, traversalData, sampleDatas); - vec4 step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection); + vec4 step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection, currT); #if defined(JITTER) float noise = hash(screenCoord); // [0,1] @@ -87,7 +90,7 @@ void main() setStatistics(fragmentInput.metadata.statistics); #endif - vec4 colorAccum =vec4(0.0); + vec4 colorAccum = vec4(0.0); for (int stepCount = 0; stepCount < STEP_COUNT_MAX; ++stepCount) { // Read properties from the megatexture based on the traversal state @@ -152,7 +155,7 @@ void main() // This is similar to traverseOctreeFromBeginning but is faster when the ray is in the same tile as the previous step. positionUvShapeSpace = convertUvToShapeUvSpace(positionUv); traverseOctreeFromExisting(positionUvShapeSpace, traversalData, sampleDatas); - step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection); + step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection, currT); } // Convert the alpha from [0,ALPHA_ACCUM_MAX] to [0,1] From c04ab187230c28ca63dd02a53258d7fbba0890fa Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 22 Feb 2024 08:10:02 -0500 Subject: [PATCH 6/7] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7c028092af6d..0a60537befde 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,10 @@ #### @cesium/engine +##### Additions :tada: + +- Surface normals are now computed for clipping and shape bounds in VoxelEllipsoidShape and VoxelCylinderShape. [#11847](https://github.com/CesiumGS/cesium/pull/11847) + ##### Fixes :wrench: - Fixed a bug affecting voxel shader compilation in WebGL1 contexts. [#11798](https://github.com/CesiumGS/cesium/pull/11798) From 3c62e035c9f385dedb7b0290ce4e0914d6850cb3 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 6 Mar 2024 18:29:49 -0500 Subject: [PATCH 7/7] PR feedback --- .../Shaders/Voxels/IntersectEllipsoid.glsl | 9 ++++---- .../Shaders/Voxels/IntersectionUtils.glsl | 2 ++ .../engine/Source/Shaders/Voxels/VoxelFS.glsl | 22 +++++++++---------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl index f4f30bd0e0ec..f6e246deb4e9 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectEllipsoid.glsl @@ -47,14 +47,13 @@ RayShapeIntersection intersectHeight(in Ray ray, in float relativeHeight, in boo { // Scale the ray by the ellipsoid axes to make it a unit sphere // Note: approximating ellipsoid + height as an ellipsoid - // Note: may be better to approximate radiiCorrection for small relative heights vec3 radiiCorrection = u_ellipsoidRadiiUv / (u_ellipsoidRadiiUv + relativeHeight); vec3 position = ray.pos * radiiCorrection; vec3 direction = ray.dir * radiiCorrection; float a = dot(direction, direction); // ~ 1.0 (or maybe 4.0 if ray is scaled) float b = dot(direction, position); // roughly inside [-1.0, 1.0] when zoomed in - float c = dot(position, position) - 1.0; // ~ 0.0 when zoomed in. Note cancellation problem! + float c = dot(position, position) - 1.0; // ~ 0.0 when zoomed in. float determinant = b * b - a * c; // ~ b * b when zoomed in if (determinant < 0.0) { @@ -92,15 +91,15 @@ vec2 intersectDoubleEndedCone(in Ray ray, in float cosSqrHalfAngle) vec3 o = ray.pos; vec3 d = ray.dir; float sinSqrHalfAngle = 1.0 - cosSqrHalfAngle; - // a = d.z * d.z - dot(d, d) * cosSqrHalfAngle; + float aSin = d.z * d.z * sinSqrHalfAngle; float aCos = -dot(d.xy, d.xy) * cosSqrHalfAngle; float a = aSin + aCos; - // b = d.z * o.z - dot(o, d) * cosSqrHalfAngle; + float bSin = d.z * o.z * sinSqrHalfAngle; float bCos = -dot(o.xy, d.xy) * cosSqrHalfAngle; float b = bSin + bCos; - // c = o.z * o.z - dot(o, o) * cosSqrHalfAngle; + float cSin = o.z * o.z * sinSqrHalfAngle; float cCos = -dot(o.xy, o.xy) * cosSqrHalfAngle; float c = cSin + cCos; diff --git a/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl b/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl index 04ebeb75c5ab..00a6112dc60d 100644 --- a/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl +++ b/packages/engine/Source/Shaders/Voxels/IntersectionUtils.glsl @@ -24,6 +24,8 @@ vec4 intersectionMin(in vec4 intersect0, in vec4 intersect1) { if (intersect0.w == NO_HIT) { return intersect1; + } else if (intersect1.w == NO_HIT) { + return intersect0; } return (intersect0.w <= intersect1.w) ? intersect0 : intersect1; } diff --git a/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl b/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl index fefa2b3ebcfc..5960de4874e0 100644 --- a/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl +++ b/packages/engine/Source/Shaders/Voxels/VoxelFS.glsl @@ -32,7 +32,7 @@ float hash(vec2 p) } #endif -vec4 getStepSize(in SampleData sampleData, in Ray viewRay, in RayShapeIntersection shapeIntersection, in float currT) { +vec4 getStepSize(in SampleData sampleData, in Ray viewRay, in RayShapeIntersection shapeIntersection, in float currentT) { #if defined(SHAPE_BOX) Box voxelBox = constructVoxelBox(sampleData.tileCoords, sampleData.tileUv); RayShapeIntersection voxelIntersection = intersectBox(viewRay, voxelBox); @@ -44,7 +44,7 @@ vec4 getStepSize(in SampleData sampleData, in Ray viewRay, in RayShapeIntersecti float dimAtLevel = pow(2.0, float(sampleData.tileCoords.w)); float shapeEntryT = shapeIntersection.entry.w * RAY_SCALE; float constantStep = u_stepSize / dimAtLevel; - vec3 normal = (currT < shapeEntryT + constantStep) ? shapeIntersection.entry.xyz : viewRay.dir; + vec3 normal = (currentT < shapeEntryT + constantStep) ? shapeIntersection.entry.xyz : viewRay.dir; return vec4(normalize(normal), constantStep); #endif } @@ -96,20 +96,20 @@ void main() discard; } - float currT = shapeIntersection.entry.w * RAY_SCALE; + float currentT = shapeIntersection.entry.w * RAY_SCALE; float endT = shapeIntersection.exit.w; - vec3 positionUv = viewPosUv + currT * viewDirUv; + vec3 positionUv = viewPosUv + currentT * viewDirUv; vec3 positionUvShapeSpace = convertUvToShapeUvSpace(positionUv); // Traverse the tree from the start position TraversalData traversalData; SampleData sampleDatas[SAMPLE_COUNT]; traverseOctreeFromBeginning(positionUvShapeSpace, traversalData, sampleDatas); - vec4 step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection, currT); + vec4 step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection, currentT); #if defined(JITTER) float noise = hash(screenCoord); // [0,1] - currT += noise * step.w; + currentT += noise * step.w; positionUv += noise * step.w * viewDirUv; #endif @@ -161,11 +161,11 @@ void main() } // Keep raymarching - currT += step.w; + currentT += step.w; positionUv += step.w * viewDirUv; // Check if there's more intersections. - if (currT > endT) { + if (currentT > endT) { #if (INTERSECTION_COUNT == 1) break; #else @@ -174,9 +174,9 @@ void main() break; } else { // Found another intersection. Resume raymarching there - currT = shapeIntersection.entry.w * RAY_SCALE; + currentT = shapeIntersection.entry.w * RAY_SCALE; endT = shapeIntersection.exit.w; - positionUv = viewPosUv + currT * viewDirUv; + positionUv = viewPosUv + currentT * viewDirUv; } #endif } @@ -185,7 +185,7 @@ void main() // This is similar to traverseOctreeFromBeginning but is faster when the ray is in the same tile as the previous step. positionUvShapeSpace = convertUvToShapeUvSpace(positionUv); traverseOctreeFromExisting(positionUvShapeSpace, traversalData, sampleDatas); - step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection, currT); + step = getStepSize(sampleDatas[0], viewRayUv, shapeIntersection, currentT); } // Convert the alpha from [0,ALPHA_ACCUM_MAX] to [0,1]