@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
JavaScript
/*
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};