UNPKG

@arcgis/core

Version:

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

154 lines (143 loc) • 9.23 kB
/* All material copyright ESRI, All Rights Reserved, unless otherwise specified. See https://js.arcgis.com/4.33/esri/copyright.txt for details. */ import has from"../core/has.js";import{lerp as t}from"../core/mathUtils.js";import{create as e}from"../core/libs/gl-matrix-2/factories/mat3f64.js";import{set as a}from"../core/libs/gl-matrix-2/math/vec2.js";import{create as o}from"../core/libs/gl-matrix-2/factories/vec2f64.js";import{RayMarchingSteps as i}from"../views/3d/environment/CloudsTechniqueConfiguration.js";import{atlasSize as s,tileRows as r,tileSize as n,textureScale as l,weatherMapScale as c}from"../views/3d/environment/NoiseTextureAtlasDimensions.js";import{ScreenSpacePass as d}from"../views/3d/webgl-engine/core/shaderLibrary/ScreenSpacePass.glsl.js";import{BooleanPassUniform as u}from"../views/3d/webgl-engine/core/shaderModules/BooleanPassUniform.js";import{Float2PassUniform as f}from"../views/3d/webgl-engine/core/shaderModules/Float2PassUniform.js";import{FloatPassUniform as m}from"../views/3d/webgl-engine/core/shaderModules/FloatPassUniform.js";import{glsl as h}from"../views/3d/webgl-engine/core/shaderModules/glsl.js";import{Matrix3PassUniform as p}from"../views/3d/webgl-engine/core/shaderModules/Matrix3PassUniform.js";import{Texture2DPassUniform as g}from"../views/3d/webgl-engine/core/shaderModules/Texture2DPassUniform.js";import{SphereIntersect as v}from"../views/3d/webgl-engine/shaders/SphereIntersect.glsl.js";import{NoParameters as y}from"../views/webgl/NoParameters.js";import{ShaderBuilder as w}from"../views/webgl/ShaderBuilder.js";class S extends y{constructor(){super(...arguments),this.cloudRadius=0,this.cloudSize=0,this.detailSize=0,this.absorption=0,this.density=0,this.smoothness=0,this.cloudHeight=0,this.coverage=0,this.lastSlice=!1,this.viewMatrix=e()}}const x=has("esri-mobile")?1024:2048;function D(e){const o=new w;o.include(d,!1);const y=o.fragment;return y.include(v),y.uniforms.add(new m("cloudRadius",(t=>t.cloudRadius)),new m("power",(e=>t(35,120,e.absorption))),new m("sigmaE",(t=>1+t.absorption)),new m("density",(e=>t(0,.3,e.density))),new m("cloudSize",(e=>t(0,.02,Math.max(.01,1-e.cloudSize)))),new m("detailSize",(e=>t(0,.2,Math.max(.01,1-e.detailSize)))),new m("smoothness",(e=>t(0,.5,1-e.smoothness))),new m("cloudHeight",(e=>t(0,1500,e.cloudHeight))),new m("coverage",(t=>t.coverage)),new p("view",(t=>t.viewMatrix)),new g("cloudShapeTexture",(t=>null!=t.noiseTexture?t.noiseTexture.textureAtlas:null)),new f("cloudVariables",(t=>a(b,t.coverage,t.absorption))),new u("lastSlice",(t=>t.lastSlice))),y.constants.add("halfCubeMapSize","float",.5*x),y.code.add(h` const int STEPS = ${e.steps===i.SIXTEEN?h`16`:e.steps===i.HUNDRED?h`100`:h`200`}; const int STEPS_LIGHT = 6; const float stepL = 300.0 / float(STEPS_LIGHT); const float cloudStart = 1500.0; vec3 rayDirection(vec2 fragCoord) { vec2 xy = fragCoord; xy.x -= halfCubeMapSize; xy.y = lastSlice ? fragCoord.y - halfCubeMapSize : fragCoord.y; return normalize(vec3(-xy, -halfCubeMapSize)); } float remap(float x, float low1, float high1, float low2, float high2) { return low2 + (x - low1) * (high2 - low2) / (high1 - low1); } float saturate(float x) { return clamp(x, 0.0, 1.0); }`),y.code.add(h` float getCloudShape(vec3 pos, float pOffset) { const float textureWidth = ${h.float(s)}; const float dataWidth = ${h.float(s)}; const float tileRows = ${h.float(r)}; const vec3 atlasDimensions = vec3(${h.float(n)}, ${h.float(n)}, tileRows * tileRows); //Change from Y being height to Z being height vec3 p = float(${h.float(l)}) * pos.xzy; //Pixel coordinates of point in the 3D data vec3 coord = vec3(mod(p - pOffset * atlasDimensions, atlasDimensions)); float f = fract(coord.z); float level = floor(coord.z); float tileY = floor(level / tileRows); float tileX = level - tileY * tileRows; //The data coordinates are offset by the x and y tile, the two boundary cells between each tile pair and the initial boundary cell on the first row/column vec2 offset = atlasDimensions.x * vec2(tileX, tileY) + 2.0 * vec2(tileX, tileY) + 1.0; vec2 pixel = coord.xy + offset; vec2 data = texture(cloudShapeTexture, mod(pixel, dataWidth) / textureWidth).xy; return 1.0 - mix(data.x, data.y, f); } float getCloudMap(vec2 p){ // Shift the texture center to origin to avoid seam artifacts vec2 uv = (${h.float(c)} * p) / ${h.float(s)} + 0.5; return texture(cloudShapeTexture, uv).a; } `),y.code.add(h`float clouds(vec3 p) { float cloud = saturate(0.5 * mix(0.0, 1.0, min(2.0 * coverage, 1.0))); if (cloud <= 0.0) { return 0.0; } float cloudMap = getCloudMap(cloudSize * p.xy); cloud = mix(cloud, min(2.0 * (coverage), 1.0) * cloudMap, min(2.0 * (1.0 - coverage), 1.0)); if (cloud <= 0.0) { return 0.0; } float shape = getCloudShape(8.0 * cloudSize * p, 0.0); cloud = saturate(remap(cloud, smoothness * shape, 1.0, 0.0, 1.0)); if (cloud <= 0.0) { return 0.0; } float heightFraction = saturate((length(p) - cloudRadius - cloudStart) / cloudHeight); cloud *= saturate(remap(heightFraction, 0.0, 0.25, 0.0, 1.0)) * smoothstep(1.0, 0.25, heightFraction); if (cloud <= 0.0) { return 0.0; } return density * saturate(remap(cloud, 0.35 * smoothness * getCloudShape(detailSize * p, 0.0), 1.0, 0.0, 1.0)); }`),y.code.add(h`float HenyeyGreenstein(float g, float costh) { return (1.0 / (4.0 * 3.1415)) * ((1.0 - g * g) / pow(1.0 + g * g - 2.0 * g * costh, 1.5)); } float multipleOctaves(float extinction, float mu, float stepL) { float attenuation = 1.0; float contribution = 1.0; float phaseAttenuation = 1.0; float luminance = 0.0; for (int i = 0; i < 4; i++) { float phase = mix(HenyeyGreenstein(0.0, mu), HenyeyGreenstein(0.3 * phaseAttenuation, mu), 0.7); luminance += contribution * phase * exp(-stepL * extinction * sigmaE * attenuation); attenuation *= 0.2; contribution *= 0.6; phaseAttenuation *= 0.5; } return luminance; }`),y.code.add(h`float lightRay(vec3 org, vec3 p, float phaseFunction, float mu, vec3 sunDirection) { float lightRayDensity = clouds(p); lightRayDensity += clouds(p + sunDirection * 1.0 * stepL); lightRayDensity += clouds(p + sunDirection * 2.0 * stepL); lightRayDensity += clouds(p + sunDirection * 3.0 * stepL); lightRayDensity += clouds(p + sunDirection * 4.0 * stepL); lightRayDensity += clouds(p + sunDirection * 5.0 * stepL); float beersLaw = multipleOctaves(lightRayDensity, mu, stepL); return mix(beersLaw * 2.0 * (1.0 - (exp(-stepL * lightRayDensity * 2.0 * sigmaE ))), beersLaw, 0.5 + 0.5 * mu); }`),y.code.add(h`float mainRay(vec3 org, vec3 dir, vec3 sunDirection, float distToStart, float totalDistance, out float totalTransmittance) { if (dir.z < 0.0) { return 0.0; } totalTransmittance = 1.0; float stepS = totalDistance / float(STEPS); float cameraHeight = length(org); float mu = 0.5 + 0.5 * dot(sunDirection, dir); float phaseFunction = mix(HenyeyGreenstein(-0.3, mu), HenyeyGreenstein(0.3, mu), 0.7); vec3 p = org + distToStart * dir; float dist = distToStart; float shading = 0.0; for (int i = 0; i < STEPS; i++) { float sampleDensity = clouds(p); float sampleSigmaE = sampleDensity * sigmaE; if (sampleDensity > 0.0 ) { float ambient = mix((1.2), (1.6), saturate((length(p) - cloudRadius - cloudStart) / cloudHeight)); float luminance = sampleDensity * (ambient + power * phaseFunction * lightRay(org, p, phaseFunction, mu, sunDirection)); float transmittance = exp(-sampleSigmaE * stepS); shading += totalTransmittance * (luminance - luminance * transmittance) / sampleSigmaE; totalTransmittance *= transmittance; if (totalTransmittance <= 0.001) { totalTransmittance = 0.0; break; } } dist += stepS; p = org + dir * dist; } return shading; }`),y.main.add(h`if (coverage == 0.0) { fragColor = vec4(0.0, 1.0, 0.0, 1.0); return; } vec3 rayDir = rayDirection(gl_FragCoord.xy); rayDir = normalize(view * rayDir); vec3 viewPos = vec3(0, 0, cloudRadius + 1.0); float hazeFactor = smoothstep(-0.01, mix(0.0, 0.075, cloudVariables.x), abs(dot(rayDir, vec3(0, 0, 1)))); float totalTransmittance = 1.0; float shading = 0.0; float cloudDistance = cloudRadius + cloudStart; float rayStartDistance = dot(viewPos, viewPos) - (cloudDistance * cloudDistance); vec2 rayStartIntersect = sphereIntersect(viewPos, rayDir, rayStartDistance); float rayEndDistance = dot(viewPos, viewPos) - ((cloudDistance + cloudHeight) * (cloudDistance + cloudHeight)); vec2 rayEndIntersect = sphereIntersect(viewPos, rayDir, rayEndDistance); float distToStart = rayStartIntersect.y; float totalDistance = rayEndIntersect.y - distToStart; vec3 sunDirection = normalize(vec3(0, 0, 1)); shading = 0.5 * mainRay(viewPos, rayDir, sunDirection, distToStart, totalDistance, totalTransmittance); shading = mix(clamp(1.0 - cloudVariables.y, 0.6, 1.0), shading, hazeFactor); totalTransmittance = mix(0.0, totalTransmittance, hazeFactor); fragColor = vec4(shading, totalTransmittance, shading, totalTransmittance);`),o}const b=o(),T=Object.freeze(Object.defineProperty({__proto__:null,CloudsPassParameters:S,build:D,cubeMapSize:x},Symbol.toStringTag,{value:"Module"}));export{S as C,T as a,D as b,x as c};