// All material copyright Esri, All Rights Reserved, unless otherwise specified. // See https://js.arcgis.com/4.30/esri/copyright.txt for details. //>>built define("exports ../core/libs/gl-matrix-2/math/mat4 ../core/libs/gl-matrix-2/factories/mat4f64 ../core/libs/gl-matrix-2/factories/vec3f64 ../views/3d/environment/atmosphereUtils ../views/3d/webgl-engine/core/shaderLibrary/attributes/TextureCoordinateAttribute.glsl ../views/3d/webgl-engine/core/shaderLibrary/output/ReadDepth.glsl ../views/3d/webgl-engine/core/shaderLibrary/shading/Gamma.glsl ../views/3d/webgl-engine/core/shaderLibrary/shading/MainLighting.glsl ../views/3d/webgl-engine/core/shaderModules/Float2PassUniform ../views/3d/webgl-engine/core/shaderModules/Float3PassUniform ../views/3d/webgl-engine/core/shaderModules/Float4PassUniform ../views/3d/webgl-engine/core/shaderModules/FloatPassUniform ../views/3d/webgl-engine/core/shaderModules/interfaces ../views/3d/webgl-engine/core/shaderModules/Matrix4PassUniform ../views/3d/webgl-engine/core/shaderModules/ShaderBuilder ../views/3d/webgl-engine/core/shaderModules/Texture2DPassUniform ../views/3d/webgl-engine/lib/VertexAttribute".split(" "), function(h,w,k,l,p,q,x,y,z,A,r,B,m,a,t,C,D,E){function u(c){const e=new C.ShaderBuilder;e.attributes.add(E.VertexAttribute.POSITION,"vec2");e.include(q.TextureCoordinateAttribute,{textureCoordinateType:q.TextureCoordinateAttributeType.Default});e.varyings.add("worldRay","vec3");e.varyings.add("eyeDir","vec3");const {vertex:v,fragment:d}=e;v.uniforms.add(new t.Matrix4PassUniform("inverseProjectionMatrix",(b,g)=>g.camera.inverseProjectionMatrix),new t.Matrix4PassUniform("inverseViewMatrix",(b,g)=>w.invertOrIdentity(F, g.camera.viewMatrix)));v.code.add(a.glsl`void main(void) { vec3 posViewNear = (inverseProjectionMatrix * vec4(position, -1, 1)).xyz; eyeDir = posViewNear; worldRay = (inverseViewMatrix * vec4(posViewNear, 0)).xyz; forwardTextureCoordinates(); gl_Position = vec4(position, 1, 1); }`);d.uniforms.add(new r.Float3PassUniform("backgroundColor",b=>b.backgroundColor),new A.Float2PassUniform("radii",b=>b.radii),new r.Float3PassUniform("cameraPosition",(b,g)=>g.camera.eye),new B.Float4PassUniform("heightParameters",b=>b.heightParameters),new m.FloatPassUniform("innerFadeDistance",b=>b.innerFadeDistance),new m.FloatPassUniform("altitudeFade",b=>b.altitudeFade),new D.Texture2DPassUniform("depthTexture",b=>b.depthTexture),new m.FloatPassUniform("hazeStrength",b=>b.hazeStrength));d.constants.add("betaRayleigh", "vec3",f);d.constants.add("betaCombined","vec3",G);d.constants.add("betaMie","float",3.996E-6);d.constants.add("scaleHeight","float",p.rayLeighScaleHeight*p.atmosphereHeight);z.addMainLightDirection(d);e.include(y.Gamma);c.haze&&d.include(x.ReadDepth);d.code.add(a.glsl`vec2 sphereIntersect(vec3 start, vec3 dir, float radius, bool planet) { float a = dot(dir, dir); float b = 2.0 * dot(dir, start); float c = planet ? heightParameters[1] - radius * radius : heightParameters[2]; float d = (b * b) - 4.0 * a * c; if (d < 0.0) { return vec2(1e5, -1e5); } return vec2((-b - sqrt(d)) / (2.0 * a), (-b + sqrt(d)) / (2.0 * a)); }`);d.code.add(a.glsl`float chapmanApproximation(float X, float h, float cosZenith) { float c = sqrt(X + h); float cExpH = c * exp(-h); if (cosZenith >= 0.0) { return cExpH / (c * cosZenith + 1.0); } else { float x0 = sqrt(1.0 - cosZenith * cosZenith) * (X + h); float c0 = sqrt(x0); return 2.0 * c0 * exp(X - x0) - cExpH / (1.0 - c * cosZenith); } }`);d.code.add(a.glsl`float getOpticalDepth(vec3 position, vec3 dir, float h) { return scaleHeight * chapmanApproximation(radii[0] / scaleHeight, h, dot(normalize(position), dir)); }`);d.code.add(a.glsl` const int STEPS = 6; vec3 getAtmosphereColour(vec3 cameraPos, vec3 rayDir, vec3 lightDir, float terrainDepth) { float reducedPlanetRadius = radii[0] - 20000.0; vec2 rayPlanetIntersect = sphereIntersect(cameraPos, rayDir, reducedPlanetRadius, true); vec2 rayAtmosphereIntersect = sphereIntersect(cameraPos, rayDir, radii[1], false); bool hitsAtmosphere = (rayAtmosphereIntersect.x <= rayAtmosphereIntersect.y) && rayAtmosphereIntersect.x > 0.0; bool insideAtmosphere = heightParameters[0] < radii[1]; if (!(hitsAtmosphere || insideAtmosphere)) { return vec3(0); } bool hitsPlanet = (rayPlanetIntersect.x <= rayPlanetIntersect.y) && rayPlanetIntersect.x > 0.0; float start = insideAtmosphere ? 0.0 : rayAtmosphereIntersect.x; if (heightParameters[0] < reducedPlanetRadius) { // Long light rays from the night side of the planet lead to numerical instability // Do not render the atmosphere in such cases if (dot(rayDir, normalize(cameraPos)) < -0.025) { return vec3(0); } start = rayPlanetIntersect.y; } float end = hitsPlanet ? rayPlanetIntersect.x : rayAtmosphereIntersect.y; float maxEnd = end; ${c.haze?a.glsl`if (terrainDepth != -1.0) { end = terrainDepth; }`:""} vec3 samplePoint = cameraPos + rayDir * end; float multiplier = hitsPlanet ? -1.0 : 1.0; vec3 scattering = vec3(0); float scaleFract = (length(samplePoint) - radii[0]) / scaleHeight; float lastOpticalDepth = getOpticalDepth(samplePoint, rayDir, scaleFract); float stepSize = (end - start) / float(STEPS); for (int i = 0; i < STEPS; i++) { samplePoint -= stepSize * rayDir; scaleFract = (length(samplePoint) - radii[0]) / scaleHeight; float opticalDepth = multiplier * getOpticalDepth(samplePoint, rayDir * multiplier, scaleFract); if (i > 0) { scattering *= ${c.haze?"":a.glsl`mix(2.5, 1.0, clamp((length(cameraPos) - radii[0]) / 50e3, 0.0, 1.0)) * `} exp(-(mix(betaCombined, betaRayleigh, 0.5) + betaMie) * max(0.0, (opticalDepth - lastOpticalDepth))); } if (dot(normalize(samplePoint), lightDir) > -0.3) { float scale = exp(-scaleFract); float lightDepth = getOpticalDepth(samplePoint, lightDir, scaleFract); scattering += scale * exp(-(betaCombined + betaMie) * lightDepth); ${c.haze?"":a.glsl`scattering += scale * exp(-(0.25 * betaCombined ) * lightDepth);`} } lastOpticalDepth = opticalDepth; } float mu = dot(rayDir, lightDir); float mumu = 1.0 + mu * mu; float phaseRayleigh = 0.0596831 * mumu; ${c.haze?a.glsl`return 3.0 * scattering * stepSize * phaseRayleigh * betaRayleigh;`:a.glsl` const float g = 0.8; const float gg = g * g; float phaseMie = end == maxEnd ? 0.1193662 * ((1.0 - gg) * mumu) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg)) : 0.0; phaseMie = clamp(phaseMie, 0.0, 128.0); return 3.0 * scattering * stepSize * (phaseRayleigh * betaRayleigh + 0.025 * phaseMie * betaMie);`} } ${c.haze?"":a.glsl` vec4 applyUndergroundAtmosphere(vec3 rayDir, vec3 lightDirection, vec4 fragColor) { vec2 rayPlanetIntersect = sphereIntersect(cameraPosition, rayDir, radii[0], true); if (!((rayPlanetIntersect.x <= rayPlanetIntersect.y) && rayPlanetIntersect.y > 0.0)) { return fragColor; } float lightAngle = dot(lightDirection, normalize(cameraPosition + rayDir * max(0.0, rayPlanetIntersect.x))); vec4 surfaceColor = vec4(vec3(max(0.0, (smoothstep(-1.0, 0.8, 2.0 * lightAngle)))), 1.0 - altitudeFade); float relDist = (rayPlanetIntersect.y - max(0.0, rayPlanetIntersect.x)) / innerFadeDistance; if (relDist > 1.0) { return surfaceColor; } return mix(fragColor, surfaceColor, smoothstep(0.0, 1.0, relDist * relDist)); } float getGlow(float dist, float radius, float intensity) { return pow(radius / max(dist, 1e-6), intensity); } vec3 getSun(vec3 cameraPos, vec3 rayDir, vec3 lightDir){ // Get the amount of atmosphere between camera and the Sun along the view ray float scaleFract = (length(cameraPos) - radii[0]) / scaleHeight; float sunOpticalDepth = getOpticalDepth(cameraPos, rayDir, max(scaleFract, 0.0)); // Find the amount of light that remains after travelling through the atmosphere from the Sun along the view ray // This will make the colour of the Sun reddish on the horizon and white from space vec3 sunTransmittance = exp(-(mix(betaCombined, betaRayleigh, 0.5)) * max(0.0, sunOpticalDepth)); // Alignment of light direction and view ray float mu = clamp(dot(rayDir, lightDir), 0.0, 1.0); // Draw the Sun as a bright disc float sunDisc = 256.0 * smoothstep(0.0, 128.0, clamp(getGlow(1.0 - mu, 3e-5, 3.0), 0.0, 128.0)); return normalize(sunTransmittance) * sunDisc; }`} ${c.haze&&c.reduced?a.glsl` float getDepth(vec2 uv){ return linearDepthFromTexture(depthTexture, uv); } float textureBilinear(vec2 uv) { // Information about the high-resolution depth texture vec2 depthTextureSize = vec2(textureSize(depthTexture, 0)); vec2 texelSize = 1.0 / depthTextureSize; // The uv inside the upper right pixel - of the tile of 4 pixels - // that the atmosphere uv maps to in the depth texture vec2 depthUV = (uv * depthTextureSize) - vec2(0.5); // Relative distance of the uv coordinates inside the depth texture pixel vec2 f = fract(depthUV); // Snap to the centre of the depth texture pixel vec2 snapUV = (floor(depthUV) + vec2(0.5)) / depthTextureSize; // Read the depth texture pixel and its three neighbours float d0 = getDepth(snapUV); float d1 = getDepth(snapUV + vec2(texelSize.x, 0.0)); float d2 = getDepth(snapUV + vec2(0.0, texelSize.y)); float d3 = getDepth(snapUV + texelSize); // Return the bilinearly interpolated value of the neighbouring pixels based // on the sample position in the depth texture pixel return mix(mix(d0, d1, f.x), mix(d2, d3, f.x), f.y); } `:""} vec3 tonemapACES(vec3 x) { return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0); } void main() { vec3 rayDir = normalize(worldRay); float terrainDepth = -1.0; ${c.haze?a.glsl` float depthSample = texture(depthTexture, vuv0).r; if (depthSample != 1.0) { vec3 cameraSpaceRay = normalize(eyeDir); cameraSpaceRay /= cameraSpaceRay.z; ${c.reduced?a.glsl`cameraSpaceRay *= -textureBilinear(vuv0);`:a.glsl`cameraSpaceRay *= -linearDepthFromTexture(depthTexture, vuv0);`} terrainDepth = max(0.0, length(cameraSpaceRay)); }else{ discard; } `:a.glsl`${c.reduced?"":a.glsl` float depthSample = texture(depthTexture, vuv0).r; if (depthSample != 1.0) { fragColor = vec4(0); return; }`}`} ${c.haze?a.glsl` vec3 col = vec3(0); float fadeOut = smoothstep(-10000.0, -15000.0, heightParameters[0] - radii[0]); if(depthSample != 1.0){ col = (1.0 - fadeOut) * hazeStrength * getAtmosphereColour(cameraPosition, rayDir, mainLightDirection, terrainDepth); } // Alpha is ignored for haze blending float alpha = 1.0; `:a.glsl` vec3 col = linearizeGamma(backgroundColor); col += getAtmosphereColour(cameraPosition, rayDir, mainLightDirection, terrainDepth); col += getSun(cameraPosition, rayDir, mainLightDirection); float alpha = smoothstep(0.0, mix(0.15, 0.01, heightParameters[3]), length(col));`} col = tonemapACES(col); fragColor = delinearizeGamma(vec4(col, alpha)); ${c.haze?"":a.glsl`fragColor = applyUndergroundAtmosphere(rayDir, mainLightDirection, fragColor);`} } `);return e}const f=l.fromValues(parseFloat(Number(5.802E-6).toFixed(6)),parseFloat(Number(1.3558E-5).toFixed(6)),parseFloat(Number(3.31E-5).toFixed(6))),n=l.fromValues(3*parseFloat(Number(6.5E-7).toFixed(6)),3*parseFloat(Number(1.881E-6).toFixed(6)),3*parseFloat(Number(8.5E-8).toFixed(6))),G=l.fromValues(parseFloat(Number(f[0]+n[0]).toFixed(6)),parseFloat(Number(f[1]+n[1]).toFixed(6)),parseFloat(Number(f[2]+n[2]).toFixed(6))),F=k.create();k=Object.freeze(Object.defineProperty({__proto__:null,betaRayleigh:f, build:u},Symbol.toStringTag,{value:"Module"}));h.ChapmanAtmosphere=k;h.betaRayleigh=f;h.build=u});