weather: add wet road glow and light halos

This commit is contained in:
Richard Zink committed 2026-10-12 23:06:53 +02:00
1 parent fb113e2e2d
commit 92dd812072
2 files changed
+52 -5

No files matched your search

+8
View File
@@ -143,10 +143,18 @@ window.CONFIG = {
minDelayS: 8, // minimum delay between storm flashes minDelayS: 8, // minimum delay between storm flashes
maxDelayS: 45, // maximum delay between storm flashes maxDelayS: 45, // maximum delay between storm flashes
doubleFlash: true // some flashes flicker twice doubleFlash: true // some flashes flicker twice
},
wetRoads: {
enabled: true,
dryMinutes: 30, // roads dry out after this many dry minutes
roadGain: 0.8, // extra road-ribbon glow when wet
lightGain: 0.35 // extra halo on vehicle lights when wet
} }
// weatherEffects: { enabled: false } // weatherEffects: { enabled: false }
// weatherEffects: { rain: { maxAlpha: 0.55, speed: 1.4 }, snow: { enabled: false } } // weatherEffects: { rain: { maxAlpha: 0.55, speed: 1.4 }, snow: { enabled: false } }
// weatherEffects: { lightning: { gain: 2.0, minDelayS: 3, maxDelayS: 12 } } // weatherEffects: { lightning: { gain: 2.0, minDelayS: 3, maxDelayS: 12 } }
// weatherEffects: { wetRoads: { enabled: false } }
// weatherEffects: { wetRoads: { dryMinutes: 10, roadGain: 1.4, lightGain: 0.7 } }
}, },
// ------------------------------------------------------------------ // ------------------------------------------------------------------
+44 -5
View File
@@ -3870,6 +3870,8 @@ var CAR_FRAG_SRC = [
'uniform float uBright;', // band brightness (CONFIG.carLights.bandBrightness)', 'uniform float uBright;', // band brightness (CONFIG.carLights.bandBrightness)',
'uniform float uFadeA;', // distance where the fade starts', 'uniform float uFadeA;', // distance where the fade starts',
'uniform float uFadeB;', // distance where it is fully faded', 'uniform float uFadeB;', // distance where it is fully faded',
'uniform float uWet;', // wet-road factor 0..1',
'uniform float uWetGain;', // extra road glow when wet',
'varying float vLen;', 'varying float vLen;',
'varying float vSide;', 'varying float vSide;',
'varying float vDens;', 'varying float vDens;',
@@ -3886,9 +3888,13 @@ var CAR_FRAG_SRC = [
// what a road looks like when the individual vehicle points are', // what a road looks like when the individual vehicle points are',
// closer together than one light - the "bar" along the A-net.', // closer together than one light - the "bar" along the A-net.',
'float body = 1.0 - smoothstep(0.0, 1.0, abs(vSide));', 'float body = 1.0 - smoothstep(0.0, 1.0, abs(vSide));',
'float a = dens * body * uNight * df * uBright;', 'float wet = clamp(uWet, 0.0, 1.0);',
'float a = dens * body * uNight * df * uBright * (1.0 + wet * uWetGain);',
'if (a <= 0.002) discard;', 'if (a <= 0.002) discard;',
'gl_FragColor = vec4(vec3(1.0, 0.82, 0.58) * a, 1.0);', 'vec3 dryCol = vec3(1.0, 0.82, 0.58);',
'vec3 wetCol = vec3(0.72, 0.82, 1.0);',
'vec3 col = mix(dryCol, wetCol, wet * 0.55);',
'gl_FragColor = vec4(col * a, 1.0);',
'}' '}'
].join('\n'); ].join('\n');
@@ -3975,6 +3981,8 @@ var CARP_FRAG_SRC = [
'uniform float uBright;', // overall light intensity', 'uniform float uBright;', // overall light intensity',
'uniform float uFadeA;', // distance where the fade starts', 'uniform float uFadeA;', // distance where the fade starts',
'uniform float uFadeB;', // distance where it is fully faded', 'uniform float uFadeB;', // distance where it is fully faded',
'uniform float uWet;', // wet-road factor 0..1',
'uniform float uWetGain;', // extra halo on lights when wet',
'varying float vApproach;', 'varying float vApproach;',
'varying float vDist;', 'varying float vDist;',
'varying float vSeed;', 'varying float vSeed;',
@@ -3989,8 +3997,9 @@ var CARP_FRAG_SRC = [
'vec2 pc = gl_PointCoord - 0.5;', 'vec2 pc = gl_PointCoord - 0.5;',
'float d = length(pc) * 2.0;', 'float d = length(pc) * 2.0;',
// hot core plus a wide faint halo: a round light at any size', // hot core plus a wide faint halo: a round light at any size',
'float wet = clamp(uWet, 0.0, 1.0);',
'float core = exp(-d * d * 4.0);', 'float core = exp(-d * d * 4.0);',
'float halo = (1.0 - smoothstep(0.0, 1.0, d)) * 0.30;', 'float halo = (1.0 - smoothstep(0.0, 1.0, d)) * (0.30 + wet * uWetGain);',
'float g = max(core, halo);', 'float g = max(core, halo);',
'if (g <= 0.004) discard;', 'if (g <= 0.004) discard;',
// soft occupancy + per-vehicle life cycle: a car fades in as the', // soft occupancy + per-vehicle life cycle: a car fades in as the',
@@ -4894,7 +4903,9 @@ function applyCarBuild(gl, res) {
halfW: gl.getUniformLocation(carProg, 'uHalfW'), halfW: gl.getUniformLocation(carProg, 'uHalfW'),
bright: gl.getUniformLocation(carProg, 'uBright'), bright: gl.getUniformLocation(carProg, 'uBright'),
fadeA: gl.getUniformLocation(carProg, 'uFadeA'), fadeA: gl.getUniformLocation(carProg, 'uFadeA'),
fadeB: gl.getUniformLocation(carProg, 'uFadeB') fadeB: gl.getUniformLocation(carProg, 'uFadeB'),
wet: gl.getUniformLocation(carProg, 'uWet'),
wetGain: gl.getUniformLocation(carProg, 'uWetGain')
}; };
} }
} }
@@ -6041,6 +6052,26 @@ var WEATHER_LIGHTNING_MIN_DELAY = (typeof WEATHER_LIGHTNING_CFG.minDelayS === 'n
var WEATHER_LIGHTNING_MAX_DELAY = (typeof WEATHER_LIGHTNING_CFG.maxDelayS === 'number') ? Math.max(WEATHER_LIGHTNING_MIN_DELAY, WEATHER_LIGHTNING_CFG.maxDelayS) : 45; var WEATHER_LIGHTNING_MAX_DELAY = (typeof WEATHER_LIGHTNING_CFG.maxDelayS === 'number') ? Math.max(WEATHER_LIGHTNING_MIN_DELAY, WEATHER_LIGHTNING_CFG.maxDelayS) : 45;
var WEATHER_LIGHTNING_DOUBLE = WEATHER_LIGHTNING_CFG.doubleFlash !== false; var WEATHER_LIGHTNING_DOUBLE = WEATHER_LIGHTNING_CFG.doubleFlash !== false;
var lightningNextT = 0, lightningSeq = 0, lightningSeqT = 0, lightningFlash = 0; var lightningNextT = 0, lightningSeq = 0, lightningSeqT = 0, lightningFlash = 0;
var WET_ROADS_CFG = WEATHER_FX_CFG.wetRoads || {};
var WET_ROADS_ON = WET_ROADS_CFG.enabled !== false;
var WET_ROADS_DRY_MINUTES = (typeof WET_ROADS_CFG.dryMinutes === 'number') ? Math.max(1, WET_ROADS_CFG.dryMinutes) : 30;
var WET_ROADS_GAIN = (typeof WET_ROADS_CFG.roadGain === 'number') ? Math.max(0, WET_ROADS_CFG.roadGain) : 0.8;
var WET_LIGHT_GAIN = (typeof WET_ROADS_CFG.lightGain === 'number') ? Math.max(0, WET_ROADS_CFG.lightGain) : 0.35;
var roadWetness = 0, lastWetT = null;
function updateRoadWetness(now) {
if (!WET_ROADS_ON) { roadWetness = 0; lastWetT = null; return 0; }
if (lastWetT === null) { lastWetT = now; return roadWetness; }
var dt = Math.max(0, Math.min(5, now - lastWetT));
lastWetT = now;
var wx = window.__currentWeather || {};
var precip = Number(wx.precipMm || 0);
var snow = Number(wx.snowMm || 0);
var input = Math.min(1.0, (precip + snow * 0.55) * 0.75);
var dry = dt / (WET_ROADS_DRY_MINUTES * 60.0);
roadWetness = Math.max(0, Math.min(1, roadWetness + input * dt * 0.25 - dry));
return roadWetness;
}
function updateLightning(now) { function updateLightning(now) {
if (!WEATHER_LIGHTNING_ON) { if (!WEATHER_LIGHTNING_ON) {
@@ -6795,6 +6826,7 @@ window.clouds3dRender = function(target) {
// Aggregate artificial-light level (road ribbon, density curves, // Aggregate artificial-light level (road ribbon, density curves,
// aircraft): the mean over the per-light thresholds. // aircraft): the mean over the per-light thresholds.
var carNight = lightFactor(window.__currentSunAlt || 0); var carNight = lightFactor(window.__currentSunAlt || 0);
var _roadWet = updateRoadWetness(now);
if (carProg && (carVboM || carVboB) && carNight > 0.01) { if (carProg && (carVboM || carVboB) && carNight > 0.01) {
var carPxScale = Math.max(0.45, Math.min(1.25, gl.drawingBufferHeight / 1080)); var carPxScale = Math.max(0.45, Math.min(1.25, gl.drawingBufferHeight / 1080));
var tCurve = Math.max(0, Math.min(1, (carNight - 0.10) / 0.40)); var tCurve = Math.max(0, Math.min(1, (carNight - 0.10) / 0.40));
@@ -6821,6 +6853,8 @@ window.clouds3dRender = function(target) {
gl.uniform1f(carUniforms.bright, CAR_BAND_BRIGHT); gl.uniform1f(carUniforms.bright, CAR_BAND_BRIGHT);
gl.uniform1f(carUniforms.fadeA, CAR_FADE_A); gl.uniform1f(carUniforms.fadeA, CAR_FADE_A);
gl.uniform1f(carUniforms.fadeB, CAR_FADE_B); gl.uniform1f(carUniforms.fadeB, CAR_FADE_B);
gl.uniform1f(carUniforms.wet, _roadWet);
gl.uniform1f(carUniforms.wetGain, WET_ROADS_GAIN);
gl.enable(gl.DEPTH_TEST); gl.enable(gl.DEPTH_TEST);
gl.depthMask(false); gl.depthMask(false);
gl.enable(gl.BLEND); gl.enable(gl.BLEND);
@@ -6876,7 +6910,9 @@ window.clouds3dRender = function(target) {
sunAlt: gl.getUniformLocation(cpp, 'uSunAlt'), sunAlt: gl.getUniformLocation(cpp, 'uSunAlt'),
lightHi: gl.getUniformLocation(cpp, 'uLightHi'), lightHi: gl.getUniformLocation(cpp, 'uLightHi'),
lightLo: gl.getUniformLocation(cpp, 'uLightLo'), lightLo: gl.getUniformLocation(cpp, 'uLightLo'),
lightSoft: gl.getUniformLocation(cpp, 'uLightSoft') lightSoft: gl.getUniformLocation(cpp, 'uLightSoft'),
wet: gl.getUniformLocation(cpp, 'uWet'),
wetGain: gl.getUniformLocation(cpp, 'uWetGain')
}; };
} }
} }
@@ -6907,6 +6943,8 @@ window.clouds3dRender = function(target) {
gl.uniform1f(carPtsUniforms.bright, CAR_BRIGHT); gl.uniform1f(carPtsUniforms.bright, CAR_BRIGHT);
gl.uniform1f(carPtsUniforms.fadeA, CAR_FADE_A); gl.uniform1f(carPtsUniforms.fadeA, CAR_FADE_A);
gl.uniform1f(carPtsUniforms.fadeB, CAR_FADE_B); gl.uniform1f(carPtsUniforms.fadeB, CAR_FADE_B);
gl.uniform1f(carPtsUniforms.wet, _roadWet);
gl.uniform1f(carPtsUniforms.wetGain, WET_LIGHT_GAIN);
var carPtsGroup = function (vbo, count, dash, speed, maxDens) { var carPtsGroup = function (vbo, count, dash, speed, maxDens) {
if (!vbo || !count) return; if (!vbo || !count) return;
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
@@ -7318,6 +7356,7 @@ window.clouds3dRender = function(target) {
rain: +_weather[0].toFixed(2), rain: +_weather[0].toFixed(2),
snow: +_weather[1].toFixed(2), snow: +_weather[1].toFixed(2),
lightning: +lightningFlash.toFixed(2), lightning: +lightningFlash.toFixed(2),
wet: +roadWetness.toFixed(2),
code: Number((window.__currentWeather || {}).code || 0) code: Number((window.__currentWeather || {}).code || 0)
}; };
var _rainI = _weather[0]; var _rainI = _weather[0];