UNPKG

@arcgis/core

Version:

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

129 lines (128 loc) • 8.68 kB
/* All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://js.arcgis.com/4.32/esri/copyright.txt for details. */ import{invertOrIdentity as e}from"../core/libs/gl-matrix-2/math/mat4.js";import{IDENTITY as i,create as o}from"../core/libs/gl-matrix-2/factories/mat4f64.js";import{ScreenSpacePass as t}from"../views/3d/webgl-engine/core/shaderLibrary/ScreenSpacePass.glsl.js";import{ReadDepth as s}from"../views/3d/webgl-engine/core/shaderLibrary/output/ReadDepth.glsl.js";import{PiUtils as r}from"../views/3d/webgl-engine/core/shaderLibrary/shading/PiUtils.glsl.js";import{LocalFromScreenSpacePassParameters as a,localFromScreenSpace as n}from"../views/3d/webgl-engine/core/shaderLibrary/util/LocalFromScreenSpace.glsl.js";import{Rgba4FloatEncoding as d}from"../views/3d/webgl-engine/core/shaderLibrary/util/RgbaFloat16Encoding.glsl.js";import{TextureAtlasLookup as l}from"../views/3d/webgl-engine/core/shaderLibrary/util/TextureAtlasLookup.glsl.js";import{Float2PassUniform as c}from"../views/3d/webgl-engine/core/shaderModules/Float2PassUniform.js";import{Float3PassUniform as h}from"../views/3d/webgl-engine/core/shaderModules/Float3PassUniform.js";import{FloatPassUniform as v}from"../views/3d/webgl-engine/core/shaderModules/FloatPassUniform.js";import{FloatsPassUniform as w}from"../views/3d/webgl-engine/core/shaderModules/FloatsPassUniform.js";import{glsl as f}from"../views/3d/webgl-engine/core/shaderModules/glsl.js";import{IntegerPassUniform as g}from"../views/3d/webgl-engine/core/shaderModules/IntegerPassUniform.js";import{Matrix4BindUniform as p}from"../views/3d/webgl-engine/core/shaderModules/Matrix4BindUniform.js";import{Matrix4sPassUniform as m}from"../views/3d/webgl-engine/core/shaderModules/Matrix4sPassUniform.js";import{Texture2DBindUniform as u}from"../views/3d/webgl-engine/core/shaderModules/Texture2DBindUniform.js";import{Texture2DPassUniform as V}from"../views/3d/webgl-engine/core/shaderModules/Texture2DPassUniform.js";import{ShaderBuilder as x}from"../views/webgl/ShaderBuilder.js";class b extends a{constructor(){super(...arguments),this.shadowMap={depthTexture:null,nearFar:[1,100],numActiveFaces:1,atlasRegions:[[0,0,1,1]]},this.targetVector=[1,0,0],this.upVector=[0,0,1],this.fovs=[45,45],this.headingAndTilt=[0,0],this.observerOffset=[0,0,0],this.projectionMatrices=i.flat(),this.viewMatrices=i.flat(),this.volumeOffset=0}}function P(){const i=new x,o=i.fragment;return i.include(t),i.include(n),i.include(l),o.include(s),o.include(d),i.include(r),o.uniforms.add(new u("depthTexture",(e=>e.depth?.attachment)),new p("inverseProjectionMatrix",(e=>e.camera.inverseProjectionMatrix)),new p("inverseViewNormalMatrix",(({camera:i})=>e(M,i.viewInverseTransposeMatrix))),new h("viewshedObserverOffset",(e=>e.observerOffset)),new h("viewshedTargetVector",(e=>e.targetVector)),new h("viewshedUpVector",(e=>e.upVector)),new c("viewshedFOVs",(e=>e.fovs)),new c("viewshedHeadingAndTilt",(e=>e.headingAndTilt)),new c("viewshedNearFar",(e=>e.shadowMap.nearFar??[1,100])),new v("viewshedVolumeOffset",(e=>e.volumeOffset)),new V("viewshedShadowMap",(e=>e.shadowMap.depthTexture)),new m("viewshedProjectionMatrices",(e=>e.projectionMatrices),6),new m("viewshedViewMatrices",(e=>e.viewMatrices),6),new g("viewshedNumFaces",(e=>e.shadowMap.numActiveFaces)),new w("viewshedAtlasRegions",(e=>e.shadowMap.atlasRegions.flat()),24),new V("normalMap",(e=>e.normals))),o.constants.add("visibleColor","vec4",[0,1,0,.5]),o.constants.add("occludedColor","vec4",[1,0,0,.5]),o.code.add(f`vec2 getViewshedUv(vec4 worldPosition, int face) { mat4 viewshedMatrix = viewshedProjectionMatrices[face]; vec4 viewshedUv4 = viewshedMatrix * worldPosition; vec3 viewshedUv = viewshedUv4.xyz / viewshedUv4.w; return viewshedUv.xy; } float viewshedDepthToFloat(float depth) { return (depth - viewshedNearFar[0]) / (viewshedNearFar[1] - viewshedNearFar[0]); } float getOrthographicDepthToViewshed(vec4 worldPosition, int face) { mat4 viewshedViewMatrix = viewshedViewMatrices[face]; vec4 viewshedUv4 = viewshedViewMatrix * worldPosition; vec3 viewshedUv = viewshedUv4.xyz / viewshedUv4.w; float depth = -viewshedUv.z; return viewshedDepthToFloat(depth); } float getDepthFromShadowMap(vec2 uv, int face) { int index = 4 * face; float umin = viewshedAtlasRegions[index]; float umax = viewshedAtlasRegions[index + 1]; float vmin = viewshedAtlasRegions[index + 2]; float vmax = viewshedAtlasRegions[index + 3]; vec4 atlasRegion = vec4(umin, vmin, umax, vmax); return rgba4ToFloat(textureAtlasLookup(viewshedShadowMap, uv, atlasRegion)); } struct ViewshedPoint { int face; vec2 uv; bool isWithin; float orthographicDepth; }; mat3 rotationMatrix(vec3 axis, float angle) { float s = sin(angle); float c = cos(angle); float oc = 1.0 - c; return mat3( oc * axis.xxz * axis.xyx + vec3(c, axis.zy) * vec3(1., -s, s), oc * axis.xyy * axis.yyz + vec3(axis.z, c, axis.x) * vec3(s, 1., -s), oc * axis.zyz * axis.xzz + vec3(axis.yx, c) * vec3(-s, s, 1.) ); } float distanceToPlane(vec3 position, vec3 normal) { return dot(position, normal); } bool outsideViewshed(float distance) { return distance > -viewshedVolumeOffset; } bool isWithinViewshed(vec3 position) { float positionLength = length(position - viewshedObserverOffset); float farSphereDistance = positionLength - viewshedNearFar[1]; if (outsideViewshed(farSphereDistance)) { return false; } float nearSphereDistance = viewshedNearFar[0] - positionLength; if (outsideViewshed(nearSphereDistance)) { return false; } vec3 westVector = normalize(cross(viewshedUpVector, viewshedTargetVector)); bool leftOfTarget = distanceToPlane(position, westVector) > 0.0; if (viewshedFOVs[0] < TWO_PI) { float horAngle = viewshedFOVs[0] / 2.0; horAngle = leftOfTarget ? horAngle : -horAngle; vec3 sideVector = viewshedTargetVector * rotationMatrix(viewshedUpVector, horAngle); bool inFront = distanceToPlane(position, sideVector) > 0.0; if (inFront) { vec3 sideNormal = cross(viewshedUpVector, sideVector) * (leftOfTarget ? 1. : -1.); float sideDistance = distanceToPlane(position, normalize(sideNormal)); if (outsideViewshed(sideDistance)) { return false; } } else if (viewshedFOVs[0] < PI) { return false; } } if (viewshedFOVs[1] < PI) { float t = dot(viewshedUpVector, position); vec3 nProjVector = normalize(position - t * viewshedUpVector); float heading = acos(clamp(dot(normalize(viewshedTargetVector), nProjVector), -1.0, 1.0)); heading = leftOfTarget ? heading : -heading; bool aboveTarget = distanceToPlane(position, viewshedUpVector) > 0.0; float verFOV = viewshedFOVs[1] / 2.0; verFOV = aboveTarget ? -verFOV : verFOV; mat3 rotateByHeading = rotationMatrix(viewshedUpVector, heading); vec3 sideVector = viewshedTargetVector * rotationMatrix(westVector, verFOV) * rotateByHeading; vec3 leftVector = westVector * rotateByHeading; vec3 sideNormal = cross(sideVector, leftVector) * (aboveTarget ? 1. : -1.); float sideDistance = distanceToPlane(position, normalize(sideNormal)); if (outsideViewshed(sideDistance)) { return false; } } return true; } bool getViewshedPoint(vec4 localPosition, out ViewshedPoint point) { for(int i=0; i < viewshedNumFaces; i++) { vec2 viewshedUv = getViewshedUv(localPosition, i); if (viewshedUv.x > 0. && viewshedUv.x < 1. && viewshedUv.y > 0. && viewshedUv.y < 1.) { float orthoDepth = getOrthographicDepthToViewshed(localPosition, i); if (orthoDepth >= 0.) { bool isWithin = isWithinViewshed(localPosition.xyz); point = ViewshedPoint(i, viewshedUv, isWithin, orthoDepth); return true; } } } return false; } float normalCosAngle(float linearDepth, vec3 localPosition) { vec4 normal4 = texture(normalMap, uv); vec3 normalN = vec3(-1.0) + 2.0 * normal4.xyz; vec3 normal = normalize((inverseViewNormalMatrix * vec4(normalN, 1.0)).xyz); vec3 viewingDir = normalize(localPosition); return dot(normal, viewingDir); }`),o.main.add(f`float depth = depthFromTexture(depthTexture, uv); if (depth >= 1.0 || depth <= 0.0) { return; } float linearDepth = linearizeDepth(depth); vec4 localPosition = reconstructLocalPosition(gl_FragCoord.xy, linearDepth); ViewshedPoint point; bool foundFace = getViewshedPoint(localPosition, point); if (!foundFace || !point.isWithin) { return; } float viewshedDepth = getDepthFromShadowMap(point.uv, point.face); float distance = point.orthographicDepth; bool visible = distance < viewshedDepth; fragColor = visible ? visibleColor : occludedColor; float cosAngle = normalCosAngle(linearDepth, localPosition.xyz); float threshold = -0.01; if (cosAngle > threshold) { fragColor = occludedColor; }`),i}const M=o(),F=Object.freeze(Object.defineProperty({__proto__:null,ViewshedPassParameters:b,build:P},Symbol.toStringTag,{value:"Module"}));export{b as V,F as a,P as b};