weather: add rain and snow screen overlays
This commit is contained in:
1 parent
65e31a5302
commit
e3ce9119fe
2 files changed
+191
No files matched your search
@@ -118,6 +118,29 @@ window.CONFIG = {
|
|||||||
// weatherSimProfile: { rain: { code: 80, tempC: 9, precipMm: 2.0, windKmh: 28 } }
|
// weatherSimProfile: { rain: { code: 80, tempC: 9, precipMm: 2.0, windKmh: 28 } }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Weather effects
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Visual precipitation overlays driven by window.__currentWeather.
|
||||||
|
// intensity multiplies Open-Meteo values; maxAlpha caps the overlay.
|
||||||
|
weatherEffects: {
|
||||||
|
enabled: true,
|
||||||
|
rain: {
|
||||||
|
enabled: true,
|
||||||
|
intensity: 1.0, // 0.5 subtle, 2.0 heavy
|
||||||
|
maxAlpha: 0.35, // maximum screen overlay opacity
|
||||||
|
speed: 1.0 // fall speed multiplier
|
||||||
|
},
|
||||||
|
snow: {
|
||||||
|
enabled: true,
|
||||||
|
intensity: 1.0,
|
||||||
|
maxAlpha: 0.45,
|
||||||
|
speed: 0.35 // slow drifting flakes
|
||||||
|
}
|
||||||
|
// weatherEffects: { enabled: false }
|
||||||
|
// weatherEffects: { rain: { maxAlpha: 0.55, speed: 1.4 }, snow: { enabled: false } }
|
||||||
|
},
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// Weather data
|
// Weather data
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
|||||||
Vendored
+168
@@ -6021,6 +6021,114 @@ function loadMoonTexture(gl) {
|
|||||||
img.src = MOON_TEX_SRC;
|
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) {
|
window.clouds3dRender = function(target) {
|
||||||
if (!target || typeof target !== 'object') {
|
if (!target || typeof target !== 'object') {
|
||||||
console.error('clouds3d: invalid target');
|
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.
|
// Restore default state for the next frame.
|
||||||
gl.disable(gl.BLEND);
|
gl.disable(gl.BLEND);
|
||||||
gl.enable(gl.DEPTH_TEST);
|
gl.enable(gl.DEPTH_TEST);
|
||||||
|
|||||||
Reference in new issue
Block a user