UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

5 lines (4 loc) 10.4 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */ import{set as e}from"../core/libs/gl-matrix-2/math/vec2.js";import{create as n}from"../core/libs/gl-matrix-2/factories/vec2f64.js";import{EvaluateSceneLighting as a,addAmbientBoostFactor as i,addLightingGlobalFactor as t}from"../views/3d/webgl-engine/core/shaderLibrary/shading/EvaluateSceneLighting.glsl.js";import{NormalUtils as r}from"../views/3d/webgl-engine/core/shaderLibrary/shading/NormalUtils.glsl.js";import{positionOutsideClipSpace as o}from"../views/3d/webgl-engine/core/shaderLibrary/shading/PositionOutsideClipSpace.js";import{terrainDepthTest as s}from"../views/3d/webgl-engine/core/shaderLibrary/shading/TerrainDepthTest.glsl.js";import{ComputeCovariance as c}from"../views/3d/webgl-engine/core/shaderLibrary/util/ComputeCovariance.glsl.js";import{GaussianSplatUnpackingPassParameters as l,GaussianSplatUnpacking as d}from"../views/3d/webgl-engine/core/shaderLibrary/util/GaussianSplatUnpacking.glsl.js";import{QuaternionToRotationMatrix as u}from"../views/3d/webgl-engine/core/shaderLibrary/util/QuaternionToRotationMatrix.glsl.js";import{RgbNormalizedDepthEncoding as v}from"../views/3d/webgl-engine/core/shaderLibrary/util/RgbNormalizedDepthEncoding.glsl.js";import{Float2BindUniform as g}from"../views/3d/webgl-engine/core/shaderModules/Float2BindUniform.js";import{Float2PassUniform as p}from"../views/3d/webgl-engine/core/shaderModules/Float2PassUniform.js";import{Float3BindUniform as m}from"../views/3d/webgl-engine/core/shaderModules/Float3BindUniform.js";import{FloatPassUniform as f}from"../views/3d/webgl-engine/core/shaderModules/FloatPassUniform.js";import{glsl as h,If as x}from"../views/3d/webgl-engine/core/shaderModules/glsl.js";import{Matrix4BindUniform as w}from"../views/3d/webgl-engine/core/shaderModules/Matrix4BindUniform.js";import{Texture2DUintPassUniform as y}from"../views/3d/webgl-engine/core/shaderModules/Texture2DUintPassUniform.js";import{getGaussianSplatAlphaCutoffValue as b}from"../views/3d/webgl-engine/shaders/GaussianSplatTechniqueConfiguration.js";import{ShaderBuilder as D}from"../views/webgl/ShaderBuilder.js";class C extends l{constructor(){super(...arguments),this.focalLength=-1,this.minSplatRadius=-1,this.tanFov=n()}}function P(n){const l=new D;l.varyings.add("vColor","vec4"),l.varyings.add("conicOpacity","vec4"),l.varyings.add("offsetFromCenter","vec2"),l.vertex.uniforms.add(new y("splatOrderTexture",e=>e.splatOrder),new y("splatFadingTexture",e=>e.splatFading),new y("splatAtlasTexture",e=>e.splatAtlas),new f("focalLength",e=>e.focalLength),new f("minSplatRadius",e=>e.minSplatRadius),new p("tanFov",e=>e.tanFov),new g("screenSize",({camera:n})=>e(z,n.fullWidth,n.fullHeight)),new w("proj",e=>e.camera.projectionMatrix),new w("view",e=>e.camera.viewMatrix),new g("nearFar",e=>e.camera.nearFar),new m("cameraPosition",e=>e.camera.eye)),l.vertex.include(d),l.vertex.include(u),l.vertex.include(c),l.vertex.include(a,n),l.include(r,n),i(l.vertex),t(l.vertex),l.include(s,n),l.outputs.add("fragColor","vec4",0),l.outputs.add("fragDepthColor","vec4",1);const C=b(n.alphaCutoff),P=Math.log(C),F=-2*P;return l.vertex.code.add(h`vec2 ndcToPixel(vec2 ndcCoord, vec2 screenSize) { return ((ndcCoord + 1.0) * screenSize - 1.0) * 0.5; }`),l.vertex.main.add(`\n uint instanceID = uint(gl_InstanceID);\n\n // Transform the instanceID into 2D coordinates\n uint orderTextureWidth = uint(textureSize(splatOrderTexture, 0).x);\n uint x = instanceID % orderTextureWidth;\n uint y = instanceID / orderTextureWidth;\n\n // Fetch the index of the remaining frontmost Gaussian\n uint gaussianIndex = texelFetch(splatOrderTexture, ivec2(x, y), 0).r;\n\n uint splatAtlasWidth = uint(textureSize(splatAtlasTexture, 0).x);\n\n // Fetch the packed Gaussian according to the index\n uint gaussianIndexX = gaussianIndex % splatAtlasWidth;\n uint gaussianIndexY = gaussianIndex / splatAtlasWidth;\n uvec4 packedGaussian = texelFetch(splatAtlasTexture, ivec2(gaussianIndexX, gaussianIndexY), 0);\n\n // Fetch the header associated with the packed Gaussian (contains tile origin and number of fractional bits)\n uint pageNum = gaussianIndex / 1024u;\n uint headerIndex = (pageNum + 1u) * 1024u - 1u;\n uint headerIndexX = headerIndex % splatAtlasWidth;\n uint headerIndexY = headerIndex / splatAtlasWidth;\n uvec4 packedHeader = texelFetch(splatAtlasTexture, ivec2(headerIndexX, headerIndexY), 0);\n\n // Unpack the Gaussian\n vColor = unpackColor(packedGaussian);\n\n // Handle fading\n ${x(n.fadingEnabled,"\n uint fadingTextureWidth = uint(textureSize(splatFadingTexture, 0).x);\n uint fadeX = pageNum % fadingTextureWidth;\n uint fadeY = pageNum / fadingTextureWidth;\n uint opacityModifierByte = texelFetch(splatFadingTexture, ivec2(fadeX , fadeY), 0).r;\n float opacityModifier = float(opacityModifierByte) / 255.0;\n vColor.a *= opacityModifier;\n ")}\n\n // set default position outside clipspace for early returns\n gl_Position = ${o};\n\n if(vColor.a < ${C}) {\n return;\n }\n\n vec3 scale = unpackScale(packedGaussian);\n vec4 quaternion = unpackQuaternion(packedGaussian);\n mat3 rotation = quaternionToRotationMatrix(quaternion);\n vec3 tileOriginRelativePosition = unpackTileOriginRelativePosition(packedGaussian);\n\n vec3 cameraRelativePosition = unpackCameraRelativeGaussianPosition(packedHeader, tileOriginRelativePosition);\n\n vec4 viewPos = vec4(mat3(view) * cameraRelativePosition, 1);\n\n if (viewPos.z > -nearFar.x || viewPos.z < -nearFar.y) {\n return;\n }\n\n forwardViewPosDepth(viewPos.xyz);\n\n // Handle environment (scene lighting)\n vec3 groundNormal = ${n.spherical?h`normalize(cameraRelativePosition + cameraPosition)`:h`vec3(0.0, 0.0, 1.0)`};\n float groundLightAlignment = dot(groundNormal, mainLightDirection);\n float additionalAmbientScale = additionalDirectedAmbientLight(groundLightAlignment);\n vec3 additionalLight = mainLightIntensity * additionalAmbientScale * ambientBoostFactor * lightingGlobalFactor;\n vColor.rgb = evaluateSceneLighting(groundNormal, vColor.rgb, 0.0, 0.0, mainLightIntensity);\n\n vec3 covarianceA;\n vec3 covarianceB;\n computeCovariance3D(rotation, scale.xyz, covarianceA, covarianceB);\n\n float covariance3D[6] = float[6](covarianceA.x, covarianceA.y, covarianceA.z, covarianceB.x, covarianceB.y, covarianceB.z);\n\n vec3 covariance2D = computeCovariance2D(viewPos.xyz, focalLength, tanFov, covariance3D, view);\n\n // Compute the Gaussians extent in screen space by finding the eigenvalues lambda1 and lambda2\n // of the 2D covariance matrix\n float mid = 0.5 * (covariance2D.x + covariance2D.z);\n float radius = length(vec2((covariance2D.x - covariance2D.z) * 0.5, covariance2D.y));\n float lambda1 = mid + radius;\n float lambda2 = mid - radius;\n\n // Compute the extents along the principal axes\n float l1 = ceil(sqrt(lambda1 * ${F}));\n float l2 = ceil(sqrt(lambda2 * ${F}));\n\n float maxRadius = max(l1, l2);\n\n // Ignore gaussians with very small contribution, with tolerance based on the quality profile\n if(minSplatRadius > 0.0) {\n float effectiveSize = maxRadius * vColor.a;\n if(effectiveSize < minSplatRadius) {\n return;\n }\n }\n\n vec4 projPos = proj * viewPos;\n float invW = 1. / (projPos.w + 1e-7);\n vec3 ndcPos = projPos.xyz * invW;\n\n // Screen space frustum culling\n vec2 radiusNDC = maxRadius * 2.0 / screenSize;\n\n if (any(greaterThan(abs(ndcPos.xy) - radiusNDC, vec2(1.0)))) {\n return;\n }\n\n // Compute the principal diagonal direction (eigenvector for lambda1)\n vec2 diagonalVector = normalize(vec2(covariance2D.y, lambda1 - covariance2D.x));\n\n vec2 majorAxis = l1 * diagonalVector;\n vec2 minorAxis = l2 * vec2(diagonalVector.y, -diagonalVector.x);\n\n vec2 gaussianCenterScreenPos = ndcToPixel(ndcPos.xy, screenSize);\n\n // This maps vertex IDs 0, 1, 2, 3 to (-1,-1), (1,-1), (-1,1), (1,1)\n vec2 corner = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2) - 1.0;\n offsetFromCenter = corner.x * majorAxis + corner.y * minorAxis;\n\n // Invert covariance (EWA algorithm)\n float determinant = (covariance2D.x * covariance2D.z - covariance2D.y * covariance2D.y);\n if (determinant <= 0.) {\n return;\n }\n float invDeterminant = 1. / determinant;\n\n // We use a conic function to derive the opacity\n vec3 conic = vec3(covariance2D.z, -covariance2D.y, covariance2D.x) * invDeterminant;\n conicOpacity = vec4(conic, vColor.a);\n\n // Convert from screen-space to clip-space using center + offset\n vec2 clipPos = (gaussianCenterScreenPos + offsetFromCenter) / screenSize * 2. - 1.;\n\n gl_Position = vec4(clipPos, ndcPos.z, 1.0);\n\n `),l.fragment.include(v),l.fragment.main.add(`\n discardByTerrainDepth();\n\n // Evaluate the 2D elliptical Gaussian exponent using the general conic form: Ax^2+2Bxy+Cy^2\n float x = offsetFromCenter.x;\n float y = offsetFromCenter.y;\n float conic = dot(conicOpacity.xyz, vec3(x * x, 2.0 * x * y, y * y));\n float gaussianExponent = -0.5 * conic;\n\n // A positive exponent indicates alpha > 1, this should not happen\n // We also early check the alphaCutoff (i.e., ln(alphaCutoff)), to avoid unnecessary exp()\n if (gaussianExponent > 0.0 || gaussianExponent < ${P}) {\n discard;\n }\n\n float gaussianFalloff = exp(gaussianExponent);\n\n // cap at 0.99 to avoid blending issues, such as seams between overlapping Gaussians\n float alpha = min(.99f, conicOpacity.w * gaussianFalloff);\n\n fragColor = vec4(vColor.rgb * alpha, alpha);\n\n // We simulate first hit based depth using 0.25 as the opacity threshold.\n // This works because we render in front-to-back order,\n // i.e. the first hit that counts completelly saturates the alpha channel\n // and further splats do not contribute.\n float countHit = step(0.25, alpha);\n float normalizedDepth = gl_FragCoord.z;\n fragDepthColor = vec4(encodeNormalizedDepthToRGB(normalizedDepth) * countHit, countHit);\n `),l}const z=n(),F=Object.freeze(Object.defineProperty({__proto__:null,GaussianSplatPassParameters:C,build:P},Symbol.toStringTag,{value:"Module"}));export{C as G,F as a,P as b};