Variable LIQUID_APPEARANCE_GLSLConst
LIQUID_APPEARANCE_GLSL: "\n uniform float uOpacity;\n uniform float uFlowSpeed;\n uniform float uWaveScale;\n uniform float uFoamIntensity;\n uniform vec3 uEmissive;\n uniform float uEmissiveStrength;\n uniform vec3 uLightTint;\n uniform vec3 uIceColor;\n uniform float uIceOpacity;\n\n // Drifting cloud shadows — the SAME field the terrain shader samples (a\n // WeatherSystem keeps offset/coverage in sync), so clouds darken lakes and\n // rivers in step with the ground under them.\n uniform float uCloudsEnabled;\n uniform vec2 uCloudOffset;\n uniform float uCloudScale;\n uniform float uCloudCoverage;\n uniform float uCloudOpacity;\n\n // The shared world wind, from the same Wind the clouds above drift by (see\n // setMaterialWind). uWindDrift is how far the ripple pattern has marched\n // downwind, in world units, integrated on the CPU so a change of wind turns\n // the surface rather than teleporting it; uWindChop is 0–1 surface wind, and\n // roughens the water and builds the surf as it rises.\n //\n // Only *open* water reads these. A river's direction is its channel's, not\n // the weather's — see RiverMaterial — so it and the estuary leave them alone\n // and a wind never runs a stream backwards uphill. Both are zero until a wind\n // drives them, which is what keeps a scene with none looking untouched.\n uniform vec2 uWindDrift;\n uniform float uWindChop;\n\n \n float cw_hash(vec2 p) {\n return fract(sin(dot(p, vec2(157.31, 269.53))) * 43758.5453);\n }\n float cw_noise(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n f = f * f * (3.0 - 2.0 * f);\n return mix(mix(cw_hash(i), cw_hash(i + vec2(1,0)), f.x),\n mix(cw_hash(i + vec2(0,1)), cw_hash(i + vec2(1,1)), f.x), f.y);\n }\n // Raw cloud density 0..1 at a world XZ point. offset is the accumulated\n // wind drift in world units; scale is the cloud feature size.\n float cloudField(vec2 worldXZ, vec2 offset, float scale) {\n vec2 uv = (worldXZ + offset) / max(scale, 1e-3);\n float n = cw_noise(uv) * 0.65 + cw_noise(uv * 2.63 + 19.17) * 0.35;\n // Two-octave value noise clusters tightly around 0.5 — stretch it toward\n // a uniform 0..1 spread so cloudMask's threshold selects roughly the\n // intended coverage fraction and clouds have real contrast. Without this\n // the shadows read as faint grey mush and the precipitation gate (which\n // thresholds high into the field) barely lets anything fall.\n return smoothstep(0.30, 0.70, n);\n }\n // Threshold the field so ~coverage of the ground is under cloud, with a\n // soft edge. coverage 0 -> no clouds, 1 -> overcast.\n float cloudMask(float field, float coverage) {\n float threshold = 1.0 - coverage;\n return smoothstep(threshold, threshold + 0.18, field);\n }\n\n \n uniform float uAtmoEnabled;\n uniform vec3 uAtmoColor;\n uniform float uAtmoNear;\n uniform float uAtmoFar;\n uniform float uAtmoDensity;\n\n /** Haze fraction 0–1 at a world-XZ point: 0 = untouched, 1 = pure atmosphere. */\n float atmosphereFactor(vec2 worldXZ) {\n if (uAtmoEnabled < 0.5) return 0.0;\n float d = distance(worldXZ, cameraPosition.xz);\n return smoothstep(uAtmoNear, uAtmoFar, d) * uAtmoDensity;\n }\n\n vec3 applyAtmosphere(vec3 color, vec2 worldXZ) {\n return mix(color, uAtmoColor, atmosphereFactor(worldXZ));\n }\n\n\n // Emissive is nearly exempt from fog dimming — a glowing surface should\n // punch through explored-but-unseen darkness at close to full strength\n // (hidden unexplored cells still vanish via the explored alpha term).\n // uLightTint is the scene-light tint (white by default; a day/night cycle\n // darkens it at night) — deliberately NOT applied to emissive, so lava and\n // acid keep glowing in the dark. Cloud shadows follow the same rule.\n // ice is how far this liquid has frozen at this cell, derived per material\n // from the climate temperature against its own freezePoint (see SeasonGLSL):\n // 0 open, 1 solid. Liquids with no freezePoint — lava, acid — always pass 0.\n vec4 liquidOutput(vec3 color, float visibility, float explored, vec2 worldXZ, float ice) {\n // Frozen liquid stops being liquid: it goes pale, its glow dies, and it\n // turns opaque enough to read as a surface you could stand on rather than\n // a window onto the bed below.\n color = mix(color, uIceColor, ice);\n\n vec3 tint = uLightTint;\n if (uCloudsEnabled > 0.5) {\n float cloud = cloudMask(cloudField(worldXZ, uCloudOffset, uCloudScale), uCloudCoverage);\n // Liquids are unlit, so scale the darkening by the ~55% of their light\n // budget that reads as direct sun — matching how the terrain keeps its\n // ambient share under full cloud.\n tint *= 1.0 - cloud * uCloudOpacity * 0.55;\n }\n vec3 lit = color * tint * visibility\n + uEmissive * uEmissiveStrength * mix(0.75, 1.0, visibility) * (1.0 - ice);\n // Distance haze last, and on color only: alpha is unchanged because the\n // terrain showing through a far lake is hazed to the same value anyway.\n return vec4(applyAtmosphere(lit, worldXZ), mix(uOpacity, uIceOpacity, ice) * explored);\n }\n\n // Pre-seasons signature, kept so custom liquid shaders built against the\n // exported GLSL keep compiling untouched.\n vec4 liquidOutput(vec3 color, float visibility, float explored, vec2 worldXZ) {\n return liquidOutput(color, visibility, explored, worldXZ, 0.0);\n }\n" = ...
Shared appearance uniform declarations + final-color helper for liquid shaders.