weather: add rain and snow screen overlays

This commit is contained in:
Richard Zink committed 2026-10-12 22:37:45 +02:00
1 parent 65e31a5302
commit e3ce9119fe
2 files changed
+191

No files matched your search

+168
View File
@@ -6021,6 +6021,114 @@ function loadMoonTexture(gl) {
img.src = MOON_TEX_SRC;
}
// ---- Weather overlays: rain and snow -----------------------------------
var WEATHER_FX_CFG = (window.CONFIG && window.CONFIG.weatherEffects) || {};
var WEATHER_FX_ON = WEATHER_FX_CFG.enabled !== false;
var WEATHER_RAIN_CFG = WEATHER_FX_CFG.rain || {};
var WEATHER_SNOW_CFG = WEATHER_FX_CFG.snow || {};
var WEATHER_RAIN_ON = WEATHER_RAIN_CFG.enabled !== false;
var WEATHER_SNOW_ON = WEATHER_SNOW_CFG.enabled !== false;
var WEATHER_RAIN_INTENSITY = (typeof WEATHER_RAIN_CFG.intensity === 'number') ? Math.max(0, WEATHER_RAIN_CFG.intensity) : 1.0;
var WEATHER_SNOW_INTENSITY = (typeof WEATHER_SNOW_CFG.intensity === 'number') ? Math.max(0, WEATHER_SNOW_CFG.intensity) : 1.0;
var WEATHER_RAIN_ALPHA = (typeof WEATHER_RAIN_CFG.maxAlpha === 'number') ? Math.max(0, Math.min(1, WEATHER_RAIN_CFG.maxAlpha)) : 0.35;
var WEATHER_SNOW_ALPHA = (typeof WEATHER_SNOW_CFG.maxAlpha === 'number') ? Math.max(0, Math.min(1, WEATHER_SNOW_CFG.maxAlpha)) : 0.45;
var WEATHER_RAIN_SPEED = (typeof WEATHER_RAIN_CFG.speed === 'number') ? Math.max(0.1, WEATHER_RAIN_CFG.speed) : 1.0;
var WEATHER_SNOW_SPEED = (typeof WEATHER_SNOW_CFG.speed === 'number') ? Math.max(0.05, WEATHER_SNOW_CFG.speed) : 0.35;
var weatherProg = null, weatherU = null;
var WEATHER_VERT_SRC = [
'attribute vec2 aPos;',
'varying vec2 vUv;',
'void main() {',
'vUv = aPos * 0.5 + 0.5;',
'gl_Position = vec4(aPos, 0.0, 1.0);',
'}'
].join('\n');
var WEATHER_FRAG_SRC = [
'precision mediump float;',
'uniform vec2 uResolution;',
'uniform float uTime;',
'uniform float uRain;',
'uniform float uSnow;',
'uniform vec2 uWind;',
'uniform float uRainSpeed;',
'uniform float uSnowSpeed;',
'uniform float uRainAlpha;',
'uniform float uSnowAlpha;',
'uniform float uDay;',
'uniform float uNight;',
'varying vec2 vUv;',
'float hash2(vec2 p) {',
'return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);',
'}',
'float rainLayer(vec2 uv, float scale, float speed, float seed) {',
'vec2 p = uv * vec2(scale * 1.7, scale);',
'p += uWind * uTime * 0.035;',
'p.y -= uTime * speed;',
'vec2 ip = floor(p);',
'vec2 fp = fract(p);',
'float h = hash2(ip + seed);',
'float x = fp.x - (0.15 + 0.70 * h);',
'float y = fract(fp.y + h * 7.31);',
'float width = 0.018 + 0.022 * hash2(ip + seed + 1.7);',
'float len = 0.12 + 0.34 * hash2(ip + seed + 3.1);',
'float line = 1.0 - smoothstep(0.0, width, abs(x));',
'line *= smoothstep(0.0, 0.06, y) * (1.0 - smoothstep(len, len + 0.14, y));',
'return line;',
'}',
'float snowLayer(vec2 uv, float scale, float speed, float seed) {',
'vec2 p = uv * vec2(scale * 1.25, scale);',
'p.x += uWind.x * uTime * 0.018 + sin(uTime * 0.35 + uv.y * 4.0 + seed) * 0.18;',
'p.y += uWind.y * uTime * 0.012 - uTime * speed;',
'vec2 ip = floor(p);',
'vec2 fp = fract(p);',
'float h = hash2(ip + seed);',
'vec2 c = vec2(0.20 + 0.60 * h, 0.20 + 0.60 * hash2(ip + seed + 5.3));',
'float d = length(fp - c);',
'float r = 0.055 + 0.055 * hash2(ip + seed + 8.1);',
'return 1.0 - smoothstep(0.0, r, d);',
'}',
'void main() {',
'float vis = clamp(uDay + uNight, 0.18, 1.0);',
'float rain = 0.0;',
'if (uRain > 0.001) {',
'rain += rainLayer(vUv, 18.0, 1.35 * uRainSpeed, 0.0) * 0.75;',
'rain += rainLayer(vUv, 28.0, 1.05 * uRainSpeed, 11.0) * 0.55;',
'rain += rainLayer(vUv, 44.0, 0.75 * uRainSpeed, 27.0) * 0.35;',
'rain *= uRain * uRainAlpha * vis;',
'}',
'float snow = 0.0;',
'if (uSnow > 0.001) {',
'snow += snowLayer(vUv, 12.0, 0.22 * uSnowSpeed, 0.0) * 0.70;',
'snow += snowLayer(vUv, 20.0, 0.16 * uSnowSpeed, 17.0) * 0.50;',
'snow += snowLayer(vUv, 34.0, 0.11 * uSnowSpeed, 39.0) * 0.30;',
'snow *= uSnow * uSnowAlpha * vis;',
'}',
'float a = clamp(rain + snow, 0.0, 0.95);',
'if (a <= 0.002) discard;',
'vec3 rainCol = vec3(0.62, 0.74, 0.92);',
'vec3 snowCol = vec3(0.92, 0.96, 1.0);',
'vec3 col = mix(snowCol, rainCol, clamp(rain / max(a, 0.001), 0.0, 1.0));',
'gl_FragColor = vec4(col * a, a);',
'}'
].join('\n');
function weatherOverlayIntensities() {
var wx = window.__currentWeather || {};
var precip = Number(wx.precipMm || 0);
var snow = Number(wx.snowMm || 0);
var temp = Number(wx.tempC !== undefined ? wx.tempC : 10);
var rain = 0, snowI = 0;
if (WEATHER_RAIN_ON) rain = Math.max(0, Math.min(1, precip * 0.55 * WEATHER_RAIN_INTENSITY));
if (WEATHER_SNOW_ON) snowI = Math.max(0, Math.min(1, snow * 1.25 * WEATHER_SNOW_INTENSITY));
if (temp < 1.0 && precip > 0 && snowI < 0.18) {
snowI = Math.max(snowI, Math.max(0, Math.min(1, precip * 0.85 * WEATHER_SNOW_INTENSITY)));
rain = 0;
}
return [rain, snowI];
}
window.clouds3dRender = function(target) {
if (!target || typeof target !== 'object') {
console.error('clouds3d: invalid target');
@@ -7156,6 +7264,66 @@ window.clouds3dRender = function(target) {
}
}
// ---- Weather overlay: rain/snow ------------------------------------
if (WEATHER_FX_ON) {
var _weather = weatherOverlayIntensities();
var _rainI = _weather[0];
var _snowI = _weather[1];
if (_rainI > 0.001 || _snowI > 0.001) {
if (!weatherProg) {
var wp = makeProgram(gl, WEATHER_VERT_SRC, WEATHER_FRAG_SRC);
if (wp) {
weatherProg = wp;
weatherU = {
aPos: gl.getAttribLocation(wp, 'aPos'),
res: gl.getUniformLocation(wp, 'uResolution'),
time: gl.getUniformLocation(wp, 'uTime'),
rain: gl.getUniformLocation(wp, 'uRain'),
snow: gl.getUniformLocation(wp, 'uSnow'),
wind: gl.getUniformLocation(wp, 'uWind'),
rainSpeed: gl.getUniformLocation(wp, 'uRainSpeed'),
snowSpeed: gl.getUniformLocation(wp, 'uSnowSpeed'),
rainAlpha: gl.getUniformLocation(wp, 'uRainAlpha'),
snowAlpha: gl.getUniformLocation(wp, 'uSnowAlpha'),
day: gl.getUniformLocation(wp, 'uDay'),
night: gl.getUniformLocation(wp, 'uNight')
};
}
}
if (weatherProg) {
var _wx = (_cloudWind && _cloudWind[0]) || 0;
var _wz = (_cloudWind && _cloudWind[2]) || 0;
var _screenWindX = _wx * hdZ - _wz * hdX;
var _screenWindY = _wx * hdX + _wz * hdZ;
var _weatherDay = 1 - Math.min(1, Math.max(0, -sunAlt / 0.30));
var _weatherNight = lightFactor(sunAlt);
gl.useProgram(weatherProg);
gl.bindBuffer(gl.ARRAY_BUFFER, s.buf);
gl.enableVertexAttribArray(weatherU.aPos);
gl.vertexAttribPointer(weatherU.aPos, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(weatherU.res, canvas.width, canvas.height);
gl.uniform1f(weatherU.time, now);
gl.uniform1f(weatherU.rain, _rainI);
gl.uniform1f(weatherU.snow, _snowI);
gl.uniform2f(weatherU.wind, _screenWindX, _screenWindY);
gl.uniform1f(weatherU.rainSpeed, WEATHER_RAIN_SPEED);
gl.uniform1f(weatherU.snowSpeed, WEATHER_SNOW_SPEED);
gl.uniform1f(weatherU.rainAlpha, WEATHER_RAIN_ALPHA);
gl.uniform1f(weatherU.snowAlpha, WEATHER_SNOW_ALPHA);
gl.uniform1f(weatherU.day, _weatherDay);
gl.uniform1f(weatherU.night, _weatherNight);
gl.disable(gl.DEPTH_TEST);
gl.depthMask(false);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.disable(gl.BLEND);
gl.depthMask(true);
gl.enable(gl.DEPTH_TEST);
}
}
}
// Restore default state for the next frame.
gl.disable(gl.BLEND);
gl.enable(gl.DEPTH_TEST);