diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index 6436c82..56997c0 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -439,124 +439,7 @@ var FRAG_SRC = [ 'return;', '}', - // ---- CLOUDS pass ---- - // Ray-terrain intersection (used to discard clouds that are - // entirely behind the terrain mesh). - 'vec2 terrRoots = curvedLayerRoots(0.0, ro, rd);', - 'float tTerrain = -1.0;', - 'if (terrRoots.x > 0.0) tTerrain = terrRoots.x;', - 'else if (terrRoots.y > 0.0) tTerrain = terrRoots.y;', - // Keep cloud occlusion on the curved Earth horizon only. Detailed - // terrain-height clipping made the deck boil and cut holes. - 'if (tTerrain < 0.1) tTerrain = -1.0;', - // Cloud march. The output is the cloud's effective colour and - // coverage (alpha); the render pass uses alpha-blend so the - // terrain mesh from the previous pass shows through transparent - // cloud pixels. - 'float baseThresh = mix(0.72, 0.40, uDensity);', - // March the deck in HEIGHT, not along the ray. Sampling a slab along - // the ray gives every view angle its own set of sample heights, and - // those sets line up across the screen as bands - exactly the stacked - // slabs this looked like. Walking the layer from its own top to its - // own bottom and weighting each step by its path length is independent - // of the view angle, and grazing rays get the long path they really - // have. - 'vec2 bot = curvedLayerRoots(uCloudBot, ro, rd);', - 'vec2 top = curvedLayerRoots(uCloudTop, ro, rd);', - 'float roPy = ro.y + earthDrop(ro.xz);', - 'float tEnter = 1e9;', - 'float tExit = -1e9;', - 'if (roPy >= uCloudBot && roPy <= uCloudTop) {', - 'tEnter = 0.0;', - 'tExit = max(maxPositiveRoot(bot), maxPositiveRoot(top));', - '} else if (roPy > uCloudTop) {', - 'tEnter = minPositiveRoot(top);', - 'tExit = maxPositiveRoot(bot);', - 'if (tExit < 0.0) tExit = maxPositiveRoot(top);', - '} else {', - 'tEnter = minPositiveRoot(bot);', - 'tExit = maxPositiveRoot(top);', - 'if (tExit < 0.0) tExit = maxPositiveRoot(bot);', - '}', - 'if (tEnter > 1e8 || tExit <= tEnter) { tEnter = 0.0; tExit = 14.0; }', - 'if (tTerrain > 0.0 && tEnter > tTerrain) discard;', - 'if (tTerrain > 0.0) tExit = min(tExit, tTerrain);', - // A grazing ray would sample a path of well over a thousand km with - // only 24 steps - coarse blobs along the horizon. Past this length - // the deck is inside the haze anyway, so the march stops there. - 'tExit = min(tExit, tEnter + 18.0);', - 'float stepSize = max(0.0, (tExit - tEnter) / 24.0);', - 'float t = tEnter + stepSize * (0.5 + ign(gl_FragCoord.xy));', - 'vec3 cloudSum = vec3(0.0);', - 'float trans = 1.0;', - 'for (int i = 0; i < 24; i++) {', - 'vec3 p = ro + rd * t;', - 'p.y += (ign(gl_FragCoord.xy + vec2(float(i) * 7.13, float(i) * 3.71)) - 0.5) * stepSize * 0.35;', - 'float py = p.y + earthDrop(p.xz);', - 'if (py > uCloudTop || py < uCloudBot - 0.1) break;', - 'if (tTerrain > 0.0 && t >= tTerrain) break;', - 'float cSoft = smoothstep(4.0, 18.0, t);', - 'float d = cloudDensity(p, baseThresh, cSoft);', - 'if (d > 0.02) {', - 'float lit = clamp((py - uCloudBot) / ((uCloudTop - uCloudBot) * 1.167), 0.0, 1.0);', - 'vec3 cloudCol;', - 'if (uPhysical > 0.5) {', - // The night palette colours are already darkened for - // night, and multiplying them by the night light would - // double-darken into invisibility. Physical mode - // therefore uses a brightness-normalised albedo (theme - // hue kept, brightness from the light model) and lets - // the vertical gradient act as a shading ramp. - 'vec3 albC = uCloudAlbedo * mix(0.78, 1.0, lit);', - // Self-shading: probe the density towards the sun, so - // thick cloud interior stays dark and thin edges light up. - // d > 0.85 already clamps selfLit to its floor, so the - // second density lookup (a whole fbm) is skipped there. - 'float thick = (d > 0.85) ? 1.0 :', - 'cloudDensity(p + uSunDir * 2.2, baseThresh, cSoft);', - 'float selfLit = clamp(1.0 - thick * 1.3, 0.12, 1.0);', - // Forward scattering - clouds brighten towards the sun - // (silver lining) instead of a flat day/night ramp. - 'float cosT = dot(rd, -uSunDir);', - 'float phase = 0.30 + 0.70 * pow(max(0.0, cosT), 2.5);', - 'vec3 direct = albC * uSunColor * phase * uSunInt * selfLit;', - // A cloud deck is a bright, thick scatterer: it catches - // the whole diffuse column (skylight, starlight) and - // stands clearly above the dark sky at night. - 'vec3 ambC = albC * max(vec3(uSkyAmb), uStarAmb * vec3(0.75, 0.85, 1.0)) * 1.7;', - 'vec3 moonC = albC * uMoonColor * (0.35 + 0.65 * max(0.0, dot(rd, uMoonDir))) * uMoonInt * 3.0;', - 'cloudCol = direct + ambC + moonC;', - '} else {', - 'cloudCol = mix(uCloudShadow, uCloud, lit);', - 'cloudCol = mix(cloudCol, uLight, max(0.15, uDayProgress) * 0.35 * lit);', - '}', - 'float alpha = clamp(d * stepSize * 1.30, 0.0, 1.0);', - 'cloudSum += cloudCol * alpha * trans;', - 'trans *= (1.0 - alpha);', - 'if (trans < 0.04) break;', - '}', - 't += stepSize;', - '}', - 'float cloudAlpha = 1.0 - trans;', - 'vec3 cloudColor = (cloudAlpha > 0.001) ? cloudSum / cloudAlpha : vec3(0.0);', - // Aerial perspective for the deck too: a distant cloud is seen - // through its own air column, so it dissolves into the same airlight - // the ground fades into. Without it the far clouds keep full contrast - // and the horizon reads as stacked bands. - // A cloud at 12 km looks through far less air than the ground at the - // horizon, so this uses its own much smaller factor, a desaturated - // haze colour (not the saturated blue) and a hard cap: distant cloud - // loses contrast, it does not turn blue. - 'if (uPhysical > 0.5 && cloudAlpha > 0.001) {', - 'float cTau = uAirDensity * min(tExit, 60.0) * uCloudHaze;', - 'float cHaze = min(0.55, 1.0 - exp(-cTau));', - 'vec3 airAvg = vec3(dot(uAirColor, vec3(0.3333)));', - 'vec3 hazeCol = mix(uAirColor, airAvg, 0.55);', - 'float cosTA = dot(rd, uSunDir);', - 'float phaseA = 0.5 + 0.5 * cosTA * cosTA;', - 'cloudColor = mix(cloudColor, hazeCol * phaseA, cHaze);', - '}', - 'gl_FragColor = vec4(cloudColor, cloudAlpha);', + 'if (uMode == 1) { gl_FragColor = vec4(0.0); return; }', '}' ].join('\n'); @@ -6389,26 +6272,6 @@ window.clouds3dRender = function(target) { } } - // ---- Cloud pass (uMode = 1) -------------------------------------- - // Alpha-blend over the framebuffer (terrain mesh + sky+stars). - // Depth test/write OFF — the manual discard in the cloud shader - // (tCloudTop > tTerrain) handles cloud-behind-mesh occlusion, and - // the depth values from the full-screen quad would only confuse the - // alpha blend. - gl.useProgram(s.prog); - gl.bindBuffer(gl.ARRAY_BUFFER, s.buf); - gl.enableVertexAttribArray(s.aPos); - gl.vertexAttribPointer(s.aPos, 2, gl.FLOAT, false, 0, 0); - gl.uniformMatrix4fv(s.uViewProjInv, false, viewProjInv); - gl.uniform3f(s.uCamPos, camX, camY, camZ); - gl.uniform1f(s.uCurvatureOn, CURVATURE_ON ? 1.0 : 0.0); - gl.disable(gl.DEPTH_TEST); - gl.depthMask(false); - gl.enable(gl.BLEND); - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - gl.uniform1i(s.uMode, 1); - gl.drawArrays(gl.TRIANGLES, 0, 3); - if (ATMO_MODEL_ON) { gl.uniform1i(s.uMode, 2); gl.drawArrays(gl.TRIANGLES, 0, 3);