/** * GLSL sources for the procedural atmosphere shell. * * Kept in their own module so {@link buildAtmoShell} stays focused on * orchestration. The vertex shader simply forwards world position + * normal + colour attribute; the fragment shader carries the entire * look (FBm + warped FBm + latitude bands + storm cells + cloud cover * + per-tile paint mask + rim-only mode). * * Uniform contract — every uniform name used by the fragment shader is * also listed in the {@link ATMO_SHELL_UNIFORM_NAMES} array, so the * factory can build the uniform map from a single source of truth * without typoing a name in two places. */ export declare const ATMO_SHELL_VERTEX_SHADER = "\n attribute vec3 color;\n varying vec3 vWorldPos;\n varying vec3 vWorldNormal;\n varying vec3 vObjectDir;\n varying vec3 vTileColor;\n\n void main() {\n vec4 wp = modelMatrix * vec4(position, 1.0);\n vWorldPos = wp.xyz;\n vWorldNormal = normalize(mat3(modelMatrix) * normal);\n vObjectDir = normalize(position);\n vTileColor = color;\n gl_Position = projectionMatrix * viewMatrix * wp;\n }\n"; export declare const ATMO_SHELL_FRAGMENT_SHADER = "\n uniform vec3 uTint;\n uniform vec3 uLightDir;\n uniform float uOpacity;\n uniform float uTime;\n uniform float uTurbulence;\n uniform float uBandiness;\n uniform float uBandFreq;\n uniform float uDriftSpeed;\n uniform float uStorms;\n uniform float uCloudAmount;\n uniform vec3 uCloudColor;\n uniform float uCloudScale;\n uniform float uTileColorMix;\n uniform float uFlatLighting;\n // Rim-only mode: collapses the alpha to a thin fresnel-driven liser\u00E9 at\n // the silhouette and forces the colour to the pure body tint. Used by\n // setHaloMode(true) so the playable-sol view sees the atmosphere's full\n // outer radius as a hint, without any band/cloud content covering the\n // body's centre. 0 = default volumetric look, 1 = pure rim.\n uniform float uRimOnly;\n\n varying vec3 vWorldPos;\n varying vec3 vWorldNormal;\n varying vec3 vObjectDir;\n varying vec3 vTileColor;\n\n // 3D hash + value-noise \u2014 cheap, deterministic, wraps trig.\n float _hash(vec3 p) {\n return fract(sin(dot(p, vec3(12.9898, 78.233, 37.719))) * 43758.5453);\n }\n float _vnoise(vec3 p) {\n vec3 i = floor(p);\n vec3 f = fract(p);\n vec3 u = f * f * (3.0 - 2.0 * f);\n float n000 = _hash(i + vec3(0,0,0));\n float n100 = _hash(i + vec3(1,0,0));\n float n010 = _hash(i + vec3(0,1,0));\n float n110 = _hash(i + vec3(1,1,0));\n float n001 = _hash(i + vec3(0,0,1));\n float n101 = _hash(i + vec3(1,0,1));\n float n011 = _hash(i + vec3(0,1,1));\n float n111 = _hash(i + vec3(1,1,1));\n return mix(\n mix(mix(n000, n100, u.x), mix(n010, n110, u.x), u.y),\n mix(mix(n001, n101, u.x), mix(n011, n111, u.x), u.y),\n u.z\n );\n }\n // FBm \u2014 five octaves give finer roiling without pricing the fragment\n // shader out of frame budget at typical viewport sizes.\n float _fbm(vec3 p) {\n float s = 0.0;\n float a = 0.5;\n for (int i = 0; i < 5; i++) {\n s += a * _vnoise(p);\n p *= 2.07;\n a *= 0.5;\n }\n return s;\n }\n\n // Domain-warped FBm \u2014 feeds an FBm gradient back into the sample point,\n // producing the roiling-fluid look you get on gas giants and storm-rich\n // atmospheres. `strength` scales the warp; 0 collapses back to plain FBm.\n float _warpedFbm(vec3 p, float strength) {\n vec3 w = vec3(\n _fbm(p + vec3(0.7, 1.3, 2.1)),\n _fbm(p + vec3(5.2, 1.3, 2.8)),\n _fbm(p + vec3(2.4, 4.7, 1.1))\n );\n return _fbm(p + (w - 0.5) * 2.0 * strength);\n }\n\n // Storm-cell field \u2014 three rotating \"spots\" advected by uTime. Returns\n // 0 outside cells, peaks at ~1 inside. uStorms gates the contribution.\n float _stormField(vec3 dir, float t) {\n if (uStorms < 0.001) return 0.0;\n vec3 c0 = normalize(vec3(sin(t * 0.04 + 0.0), 0.35, cos(t * 0.04 + 0.0)));\n vec3 c1 = normalize(vec3(sin(t * 0.03 + 2.7), -0.20, cos(t * 0.03 + 2.7)));\n vec3 c2 = normalize(vec3(sin(t * 0.05 + 4.9), 0.55, cos(t * 0.05 + 4.9)));\n float s = 0.0;\n s += smoothstep(0.92, 1.0, dot(dir, c0)) * 1.0;\n s += smoothstep(0.94, 1.0, dot(dir, c1)) * 0.7;\n s += smoothstep(0.95, 1.0, dot(dir, c2)) * 0.5;\n return s * uStorms;\n }\n\n void main() {\n vec3 N = normalize(vWorldNormal);\n vec3 V = normalize(cameraPosition - vWorldPos);\n // BackSide rendering \u2014 flip toward the viewer. Use gl_FrontFacing\n // (not a dot test) so silhouette fragments with marginally\n // negative interpolated dot values don't get mis-flipped on\n // FrontSide rendering, which would leak band tint onto the dark\n // hemisphere's limb.\n if (!gl_FrontFacing) N = -N;\n\n // Drifting object-space sample point. Speed is uniform-driven so a\n // playground slider can freeze / accelerate the animation live.\n float t = uTime * uDriftSpeed;\n vec3 q = vObjectDir * 2.4 + vec3(t * 0.04, t * 0.02, 0.0);\n\n // Multi-scale warped FBm \u2014 macro features (cloud masses) on top of\n // mid-scale turbulence and a fine-grain layer that catches the eye\n // when the camera zooms in. `uTurbulence` scales the domain warp.\n float macro = _warpedFbm(q * 0.6, uTurbulence);\n float meso = _fbm(q * 1.6 + vec3(0.0, t * 0.05, 0.0));\n float micro = _fbm(q * 4.5 + vec3(t * 0.08, 0.0, 0.0));\n float turb = clamp(macro * 0.6 + meso * 0.3 + micro * 0.1, 0.0, 1.0);\n\n // Adaptive latitude bands \u2014 the FBm offset gives the belts a wavy,\n // organic edge instead of mathematical sine perfection.\n float lat = vObjectDir.y;\n float bandPhase = lat * uBandFreq + (macro - 0.5) * 1.8;\n float bands = sin(bandPhase) * 0.5 + 0.5;\n // Slight asymmetry per band \u2014 alternating belts read darker / lighter.\n bands = mix(bands, bands * 0.65 + 0.18, 0.35 + 0.30 * sin(lat * uBandFreq * 0.5));\n\n // Combine turbulence and bands with the configured ratio. Storm cells\n // punch through any blend.\n float density = mix(turb, bands, clamp(uBandiness, 0.0, 1.0));\n density = clamp(density + _stormField(vObjectDir, t) * 0.6, 0.0, 1.0);\n\n // Per-tile painted colour blended over the body-level tint. The\n // mask uses a smoothstep ramp instead of a hard step so adjacent\n // tiles with different paint values fade into each other rather\n // than producing visible polygon boundaries on the icosphere \u2014 the\n // procedural FBm pattern then breaks up any residual hex shape.\n float tileEnergy = max(max(vTileColor.r, vTileColor.g), vTileColor.b);\n float tileMask = smoothstep(0.005, 0.15, tileEnergy);\n vec3 baseTint = mix(uTint, vTileColor, tileMask * uTileColorMix);\n float colorEnergy = (vTileColor.r + vTileColor.g + vTileColor.b) / 3.0;\n density = mix(density, max(density, 0.5 + colorEnergy * 0.5), tileMask);\n\n // Bands also tint the base \u2014 alternating belts pick up a slightly\n // tinted shade so the silhouette breaks out of mono-tint flatness.\n vec3 bandTint = mix(baseTint * 0.85, baseTint * 1.15, bands);\n\n // \u2500\u2500 High-altitude clouds \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Lives on the atmospheric shell instead of the body's surface\n // shader \u2014 keeps the playable sol clean while the cloud cover\n // animates on the atmosphere layer that wraps it. Driven by\n // uCloudAmount (slider) so the user can dial coverage live.\n //\n // Pattern shaping : the cloud layer follows the same bandiness /\n // storms / turbulence archetype as the base atmo, so the user\n // perceives a coherent identity when switching the playground\n // cloud pattern preset (Dispers\u00E9 / Bandes / Cyclones / Voile).\n // At default values (Dispers\u00E9) the formula collapses back to the\n // legacy FBm-only mask.\n float cloudWeight = 0.0;\n if (uCloudAmount > 0.001) {\n vec3 cp = vObjectDir * max(0.1, uCloudScale) * 1.4 + vec3(t * 0.05, t * 0.02, 0.0);\n vec3 cq = vec3(\n _fbm(cp + vec3(0.7, 1.3, 2.1)),\n _fbm(cp + vec3(5.2, 1.3, 2.8)),\n _fbm(cp + vec3(2.4, 4.7, 1.1))\n ) * 2.0 - 1.0;\n float cloudNoise = _fbm(cp * 1.5 + cq * 1.8 + vec3(t * 0.07, 0.0, 0.0));\n // Bandes : pulls clouds into latitude stripes when uBandiness > 0.\n float bandStripe = sin(lat * uBandFreq) * 0.5 + 0.5;\n float patterned = mix(cloudNoise, max(cloudNoise, bandStripe), uBandiness * 0.85);\n // Cyclones : boosts coverage near the storm cells (gated by uStorms inside).\n patterned += _stormField(vObjectDir, t) * 0.45;\n // Voile : when turbulence collapses (bandiness/storms also low), widen\n // the smoothstep window so coverage reads as a dense overcast.\n float coverageLo = mix(0.20, 0.45, smoothstep(0.10, 0.45, uTurbulence));\n cloudWeight = pow(smoothstep(coverageLo, 0.65, patterned), 1.3) * uCloudAmount;\n }\n vec3 withClouds = mix(bandTint, uCloudColor, cloudWeight);\n\n // Rim falloff \u2014 atmo reads thicker at the silhouette than at the\n // sub-camera point.\n float fres = pow(1.0 - clamp(dot(N, V), 0.0, 1.0), 1.6);\n\n // Lambertian shading. Color carries a tiny ambient floor so the\n // shaded face stays barely visible; alpha gates HARDER (no floor)\n // via smoothstep on the raw cosine, so the dark hemisphere fades\n // to fully transparent at the silhouette and no thin warm line\n // forms at the limb. uFlatLighting=1 (Sol view dome) collapses\n // both to uniform 1.\n float ndl = max(dot(N, normalize(uLightDir)), 0.0);\n float colorDiff = mix(0.05 + 0.95 * ndl, 1.0, uFlatLighting);\n float alphaGate = mix(smoothstep(0.0, 0.15, ndl), 1.0, uFlatLighting);\n\n float baseAlpha = mix(0.55, 1.0, fres) * (0.40 + 0.60 * density);\n // Rim-only collapse \u2014 keeps only the geometric silhouette opaque\n // and fades to 0 toward the body's centre. The shader's fres\n // varies poorly on BackSide rendering (caps at ~0.32 at the\n // limb), so we derive the rim from the outward normal directly:\n // |dot(N_outward, V)| is 0 at the limb and 1 at the sub-camera /\n // sub-anti-camera point; the smoothstep window is widened to the\n // [0.5, 0.85] band so the rim is visibly thick at typical camera\n // distances while still discarding the body's centre.\n float silhouetteDot = abs(dot(normalize(vWorldNormal), V));\n // Tight rim concentrated near the geometric silhouette, scaled\n // down so the additive glow stays subtle. Going wider or stronger\n // reads as a thick coloured ring rather than a discreet \"presence\"\n // hint at the atmosphere's outer edge.\n float rimAlpha = (1.0 - smoothstep(0.5, 0.95, silhouetteDot)) * 0.4;\n baseAlpha = mix(baseAlpha, rimAlpha, uRimOnly);\n // Cloud contribution is inhibited in rim-only mode so the centre\n // stays clean even when a stale cloudAmount lingers in the uniforms.\n float cloudGate = (1.0 - uRimOnly) * cloudWeight * 0.85;\n float alpha = uOpacity * max(baseAlpha, cloudGate) * alphaGate;\n if (alpha <= 0.001) discard;\n\n // Rim-only mode forces the pure tint \u2014 strips bands, painted-tile\n // colours and clouds from the visible liser\u00E9.\n vec3 finalColor = mix(withClouds, uTint, uRimOnly);\n gl_FragColor = vec4(finalColor * colorDiff, alpha);\n }\n"; //# sourceMappingURL=atmoShellShaders.d.ts.map