UNPKG

@arcgis/core

Version:

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

144 lines (143 loc) • 9.32 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */ 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 a}from"../views/3d/webgl-engine/core/shaderLibrary/shading/PiUtils.glsl.js";import{LocalFromScreenSpacePassParameters as r,localFromScreenSpace as n}from"../views/3d/webgl-engine/core/shaderLibrary/util/LocalFromScreenSpace.glsl.js";import{Float2PassUniform as d}from"../views/3d/webgl-engine/core/shaderModules/Float2PassUniform.js";import{Float3PassUniform as l}from"../views/3d/webgl-engine/core/shaderModules/Float3PassUniform.js";import{FloatPassUniform as h}from"../views/3d/webgl-engine/core/shaderModules/FloatPassUniform.js";import{FloatsPassUniform as v}from"../views/3d/webgl-engine/core/shaderModules/FloatsPassUniform.js";import{glsl as c}from"../views/3d/webgl-engine/core/shaderModules/glsl.js";import{IntegerPassUniform as w}from"../views/3d/webgl-engine/core/shaderModules/IntegerPassUniform.js";import{Matrix4BindUniform as f}from"../views/3d/webgl-engine/core/shaderModules/Matrix4BindUniform.js";import{Matrix4sPassUniform as p}from"../views/3d/webgl-engine/core/shaderModules/Matrix4sPassUniform.js";import{Texture2DBindUniform as m}from"../views/3d/webgl-engine/core/shaderModules/Texture2DBindUniform.js";import{Texture2DPassUniform as g}from"../views/3d/webgl-engine/core/shaderModules/Texture2DPassUniform.js";import{ShaderBuilder as u}from"../views/webgl/ShaderBuilder.js";class x extends r{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 V(){const i=new u,o=i.fragment;return i.include(t),i.include(n),o.include(s),o.include(a),o.uniforms.add(new m("depthTexture",e=>e.depth?.attachment),new f("inverseProjectionMatrix",e=>e.camera.inverseProjectionMatrix),new f("inverseViewNormalMatrix",({camera:i})=>e(b,i.viewInverseTransposeMatrix)),new l("viewshedObserverOffset",e=>e.observerOffset),new l("viewshedTargetVector",e=>e.targetVector),new l("viewshedUpVector",e=>e.upVector),new d("viewshedFOVs",e=>e.fovs),new d("viewshedHeadingAndTilt",e=>e.headingAndTilt),new d("viewshedNearFar",e=>e.shadowMap.nearFar??[1,100]),new h("viewshedVolumeOffset",e=>e.volumeOffset),new g("viewshedShadowMap",e=>e.shadowMap.depthTexture),new p("viewshedProjectionMatrices",e=>e.projectionMatrices,6),new p("viewshedViewMatrices",e=>e.viewMatrices,6),new w("viewshedNumFaces",e=>e.shadowMap.numActiveFaces),new v("viewshedAtlasRegions",24,e=>e.shadowMap.atlasRegions.flat()),new g("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(c`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 viewshedReadShadowMapDepth(sampler2D _viewshedShadowmap, ivec2 uv) { return texelFetch(_viewshedShadowmap, uv, 0).r; } float viewshedFilterShadow(sampler2D _viewshedShadowmap, vec2 uv) { vec2 uvScaled = uv * vec2(textureSize(_viewshedShadowmap, 0)); vec2 st = fract(uvScaled); ivec2 base = ivec2(uvScaled); float s00 = viewshedReadShadowMapDepth( _viewshedShadowmap, ivec2(base.x, base.y )); float s10 = viewshedReadShadowMapDepth( _viewshedShadowmap, ivec2(base.x + 1, base.y )); float s11 = viewshedReadShadowMapDepth( _viewshedShadowmap, ivec2(base.x + 1, base.y + 1)); float s01 = viewshedReadShadowMapDepth( _viewshedShadowmap, ivec2(base.x, base.y + 1)); return mix(mix(s00, s10, st.x), mix(s01, s11, st.x), st.y); } float viewshedTextureAtlasLookup(sampler2D _viewshedShadowmap, vec2 uv, vec4 atlasRegion) { vec2 atlasScale = atlasRegion.zw - atlasRegion.xy; vec2 uvAtlas = fract(uv) * atlasScale + atlasRegion.xy; return viewshedFilterShadow(_viewshedShadowmap, uvAtlas); } 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 viewshedTextureAtlasLookup(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 = normalize(normal4.xyz * 2.0 - 1.0); vec3 normal = normalize((inverseViewNormalMatrix * vec4(normalN, 1.0)).xyz); vec3 viewingDir = normalize(localPosition); return dot(normal, viewingDir); }`),o.main.add(c`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 b=o(),M=Object.freeze(Object.defineProperty({__proto__:null,ViewshedPassParameters:x,build:V},Symbol.toStringTag,{value:"Module"}));export{x as V,M as a,V as b};