meteor showers: add debug shower and known radiant streaks
This commit is contained in:
1 parent
22e2bae5bd
commit
0f22aad650
2 files changed
+351
No files matched your search
@@ -147,6 +147,58 @@ window.CONFIG = {
|
|||||||
// emergencyVehicles: { colorVariation: 0.25, ledColor: [0.02, 0.18, 1.0], halogenColor: [0.70, 0.82, 1.0] }
|
// emergencyVehicles: { colorVariation: 0.25, ledColor: [0.02, 0.18, 1.0], halogenColor: [0.70, 0.82, 1.0] }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Meteor showers
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Known meteor showers are rendered as short night-sky streaks whose
|
||||||
|
// backward extension points toward the shower radiant. The debug shower
|
||||||
|
// is a temporary test shower for a fixed local start time.
|
||||||
|
meteorShowers: {
|
||||||
|
enabled: true,
|
||||||
|
brightness: 1.0,
|
||||||
|
maxActive: 12,
|
||||||
|
rate: 0.25, // meteors per second across all active showers
|
||||||
|
speedDegPerSec: 14, // apparent angular speed
|
||||||
|
lengthDeg: 18, // trail length in degrees
|
||||||
|
trailPoints: 14, // point sprites per trail
|
||||||
|
pointSize: 10, // base point size in pixels
|
||||||
|
nightOnly: true,
|
||||||
|
colors: [
|
||||||
|
[1.00, 0.96, 0.88],
|
||||||
|
[0.72, 1.00, 0.86],
|
||||||
|
[1.00, 0.72, 0.42]
|
||||||
|
],
|
||||||
|
debug: {
|
||||||
|
enabled: true,
|
||||||
|
name: 'Debug Shower',
|
||||||
|
startAt: '2026-10-13T07:00:00',
|
||||||
|
durationMin: 10,
|
||||||
|
rate: 1.2,
|
||||||
|
maxActive: 18,
|
||||||
|
radiant: { raH: 3.2, decDeg: 58 },
|
||||||
|
lengthDeg: 22,
|
||||||
|
speedDegPerSec: 18,
|
||||||
|
brightness: 2.4,
|
||||||
|
nightOnly: false
|
||||||
|
},
|
||||||
|
showers: [
|
||||||
|
{ name: 'Quadrantids', startMonth: 1, startDay: 1, peakMonth: 1, peakDay: 3, endMonth: 1, endDay: 15, zhr: 110, raH: 15.4, decDeg: 49 },
|
||||||
|
{ name: 'Lyrids', startMonth: 4, startDay: 16, peakMonth: 4, peakDay: 22, endMonth: 4, endDay: 25, zhr: 18, raH: 18.1, decDeg: 34 },
|
||||||
|
{ name: 'Eta Aquariids', startMonth: 4, startDay: 19, peakMonth: 5, peakDay: 6, endMonth: 5, endDay: 28, zhr: 50, raH: 22.5, decDeg: -1 },
|
||||||
|
{ name: 'Delta Aquariids', startMonth: 7, startDay: 15, peakMonth: 7, peakDay: 30, endMonth: 8, endDay: 18, zhr: 25, raH: 22.8, decDeg: -16 },
|
||||||
|
{ name: 'Perseids', startMonth: 7, startDay: 17, peakMonth: 8, peakDay: 12, endMonth: 8, endDay: 24, zhr: 100, raH: 3.2, decDeg: 58 },
|
||||||
|
{ name: 'Draconids', startMonth: 10, startDay: 6, peakMonth: 10, peakDay: 8, endMonth: 10, endDay: 10, zhr: 10, raH: 17.5, decDeg: 54 },
|
||||||
|
{ name: 'Orionids', startMonth: 10, startDay: 2, peakMonth: 10, peakDay: 21, endMonth: 11, endDay: 7, zhr: 20, raH: 6.4, decDeg: 15 },
|
||||||
|
{ name: 'Taurids', startMonth: 10, startDay: 10, peakMonth: 11, peakDay: 5, endMonth: 12, endDay: 10, zhr: 5, raH: 3.9, decDeg: 21 },
|
||||||
|
{ name: 'Leonids', startMonth: 11, startDay: 6, peakMonth: 11, peakDay: 17, endMonth: 11, endDay: 30, zhr: 15, raH: 10.2, decDeg: 22 },
|
||||||
|
{ name: 'Geminids', startMonth: 12, startDay: 4, peakMonth: 12, peakDay: 14, endMonth: 12, endDay: 17, zhr: 120, raH: 7.5, decDeg: 33 },
|
||||||
|
{ name: 'Ursids', startMonth: 12, startDay: 17, peakMonth: 12, peakDay: 22, endMonth: 12, endDay: 26, zhr: 10, raH: 14.5, decDeg: 75 }
|
||||||
|
]
|
||||||
|
// meteorShowers: { enabled: false }
|
||||||
|
// meteorShowers: { debug: { enabled: false } }
|
||||||
|
// meteorShowers: { debug: { startAt: '2026-10-13T07:00:00', durationMin: 10, rate: 2.0 } }
|
||||||
|
},
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// Weather effects
|
// Weather effects
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
|||||||
Vendored
+299
@@ -6255,6 +6255,300 @@ function starSidereal() {
|
|||||||
var _skyLat = ((window.CONFIG && window.CONFIG.location && window.CONFIG.location.lat) || 51.5074) * Math.PI / 180;
|
var _skyLat = ((window.CONFIG && window.CONFIG.location && window.CONFIG.location.lat) || 51.5074) * Math.PI / 180;
|
||||||
var SKY_SIN_LAT = Math.sin(_skyLat), SKY_COS_LAT = Math.cos(_skyLat);
|
var SKY_SIN_LAT = Math.sin(_skyLat), SKY_COS_LAT = Math.cos(_skyLat);
|
||||||
|
|
||||||
|
var METEOR_CFG = (window.CONFIG && window.CONFIG.meteorShowers) || {};
|
||||||
|
var METEORS_ON = METEOR_CFG.enabled !== false;
|
||||||
|
var meteorProg = null, meteorVbo = null, meteorUniforms = null, meteorCount = 0;
|
||||||
|
var meteorList = [], meteorSpawnAcc = 0, meteorLastT = null, meteorAllowDay = false;
|
||||||
|
var METEOR_BRIGHT = (typeof METEOR_CFG.brightness === 'number') ? Math.max(0, METEOR_CFG.brightness) : 1.0;
|
||||||
|
var METEOR_MAX_ACTIVE = (typeof METEOR_CFG.maxActive === 'number') ? Math.max(1, Math.min(80, METEOR_CFG.maxActive)) : 12;
|
||||||
|
var METEOR_RATE = (typeof METEOR_CFG.rate === 'number') ? Math.max(0, Math.min(8, METEOR_CFG.rate)) : 0.25;
|
||||||
|
var METEOR_SPEED = (typeof METEOR_CFG.speedDegPerSec === 'number') ? Math.max(1, Math.min(80, METEOR_CFG.speedDegPerSec)) : 14;
|
||||||
|
var METEOR_LENGTH = (typeof METEOR_CFG.lengthDeg === 'number') ? Math.max(2, Math.min(90, METEOR_CFG.lengthDeg)) : 18;
|
||||||
|
var METEOR_TRAIL_POINTS = (typeof METEOR_CFG.trailPoints === 'number') ? Math.max(2, Math.min(40, Math.round(METEOR_CFG.trailPoints))) : 14;
|
||||||
|
var METEOR_POINT_SIZE = (typeof METEOR_CFG.pointSize === 'number') ? Math.max(1, Math.min(40, METEOR_CFG.pointSize)) : 10;
|
||||||
|
var METEOR_NIGHT_ONLY = METEOR_CFG.nightOnly !== false;
|
||||||
|
var METEOR_COLORS = Array.isArray(METEOR_CFG.colors) && METEOR_CFG.colors.length ? METEOR_CFG.colors : [
|
||||||
|
[1.00, 0.96, 0.88], [0.72, 1.00, 0.86], [1.00, 0.72, 0.42]
|
||||||
|
];
|
||||||
|
var METEOR_DEBUG = METEOR_CFG.debug || {};
|
||||||
|
var METEOR_SHOWERS = Array.isArray(METEOR_CFG.showers) ? METEOR_CFG.showers : [];
|
||||||
|
|
||||||
|
var METEOR_VERT = [
|
||||||
|
'attribute vec4 aMpos;',
|
||||||
|
'attribute vec4 aMcol;',
|
||||||
|
'uniform mat4 uMVP;',
|
||||||
|
'uniform float uSizeScale;',
|
||||||
|
'varying vec4 vC;',
|
||||||
|
'varying float vSeed;',
|
||||||
|
'void main() {',
|
||||||
|
'gl_Position = uMVP * vec4(aMpos.xyz, 1.0);',
|
||||||
|
'gl_PointSize = clamp(aMcol.w * uSizeScale, 1.0, 34.0);',
|
||||||
|
'vC = aMcol;',
|
||||||
|
'vSeed = aMpos.w;',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
var METEOR_FRAG = [
|
||||||
|
'precision mediump float;',
|
||||||
|
'varying vec4 vC;',
|
||||||
|
'varying float vSeed;',
|
||||||
|
'void main() {',
|
||||||
|
'vec2 pc = gl_PointCoord - 0.5;',
|
||||||
|
'float d = length(pc) * 2.0;',
|
||||||
|
'float g = exp(-d * d * 3.1);',
|
||||||
|
'float flicker = 0.88 + 0.12 * sin(vSeed * 628.0);',
|
||||||
|
'float a = vC.w * g * flicker;',
|
||||||
|
'gl_FragColor = vec4(vC.rgb * a, 1.0);',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
function meteorNum(v, def, min, max) {
|
||||||
|
return (typeof v === 'number' && isFinite(v)) ? Math.max(min, Math.min(max, v)) : def;
|
||||||
|
}
|
||||||
|
function meteorDot(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
|
||||||
|
function meteorCross(a, b) {
|
||||||
|
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
||||||
|
}
|
||||||
|
function meteorNorm(a) {
|
||||||
|
var l = Math.sqrt(meteorDot(a, a)) || 1;
|
||||||
|
return [a[0] / l, a[1] / l, a[2] / l];
|
||||||
|
}
|
||||||
|
function meteorLocalDir(raH, decDeg, lst) {
|
||||||
|
var ra = (raH || 0) * 15 * DEG;
|
||||||
|
var dec = (decDeg || 0) * DEG;
|
||||||
|
var H = lst - ra;
|
||||||
|
var cd = Math.cos(dec), sd = Math.sin(dec);
|
||||||
|
var sh = Math.sin(H), ch = Math.cos(H);
|
||||||
|
return [cd * sh, SKY_SIN_LAT * sd + SKY_COS_LAT * cd * ch, SKY_COS_LAT * sd - SKY_SIN_LAT * cd * ch];
|
||||||
|
}
|
||||||
|
function meteorActiveOnDate(s, date) {
|
||||||
|
var key = (date.getMonth() + 1) * 100 + date.getDate();
|
||||||
|
var start = s.startMonth * 100 + s.startDay;
|
||||||
|
var end = s.endMonth * 100 + s.endDay;
|
||||||
|
if (start <= end) return key >= start && key <= end;
|
||||||
|
return key >= start || key <= end;
|
||||||
|
}
|
||||||
|
function meteorDayDiffToPeak(date, peakMonth, peakDay) {
|
||||||
|
var y = date.getFullYear();
|
||||||
|
var cur = Date.UTC(y, date.getMonth(), date.getDate());
|
||||||
|
var best = Infinity;
|
||||||
|
for (var yy = y - 1; yy <= y + 1; yy++) {
|
||||||
|
var peak = Date.UTC(yy, peakMonth - 1, peakDay);
|
||||||
|
var diff = (cur - peak) / 86400000;
|
||||||
|
if (Math.abs(diff) < Math.abs(best)) best = diff;
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
function meteorShowerIntensity(s, date) {
|
||||||
|
if (!meteorActiveOnDate(s, date)) return 0;
|
||||||
|
var diff = meteorDayDiffToPeak(date, s.peakMonth, s.peakDay);
|
||||||
|
var sigma = 4.0;
|
||||||
|
var g = Math.exp(-0.5 * (diff / sigma) * (diff / sigma));
|
||||||
|
var zhr = (typeof s.zhr === 'number') ? s.zhr : 10;
|
||||||
|
return Math.max(0, Math.min(1, g * Math.min(1.2, zhr / 100)));
|
||||||
|
}
|
||||||
|
function meteorDebugActive(sceneDate, realDate) {
|
||||||
|
if (!METEOR_DEBUG.enabled) return null;
|
||||||
|
var start = METEOR_DEBUG.startAt ? new Date(METEOR_DEBUG.startAt) : null;
|
||||||
|
var dur = (typeof METEOR_DEBUG.durationMin === 'number') ? METEOR_DEBUG.durationMin * 60000 : 600000;
|
||||||
|
var s = start ? start.getTime() : 0;
|
||||||
|
var e = start ? s + dur : Infinity;
|
||||||
|
var t1 = sceneDate ? sceneDate.getTime() : NaN;
|
||||||
|
var t2 = realDate ? realDate.getTime() : NaN;
|
||||||
|
var active = !start || (t1 >= s && t1 <= e) || (t2 >= s && t2 <= e);
|
||||||
|
if (!active) return null;
|
||||||
|
var rad = METEOR_DEBUG.radiant || {};
|
||||||
|
return {
|
||||||
|
name: METEOR_DEBUG.name || 'Debug Shower',
|
||||||
|
intensity: 1,
|
||||||
|
raH: (typeof rad.raH === 'number') ? rad.raH : 3.2,
|
||||||
|
decDeg: (typeof rad.decDeg === 'number') ? rad.decDeg : 58,
|
||||||
|
rate: meteorNum(METEOR_DEBUG.rate, 1.2, 0, 8),
|
||||||
|
maxActive: (typeof METEOR_DEBUG.maxActive === 'number') ? Math.max(1, Math.min(80, Math.round(METEOR_DEBUG.maxActive))) : 18,
|
||||||
|
lengthDeg: meteorNum(METEOR_DEBUG.lengthDeg, 22, 2, 90),
|
||||||
|
speedDegPerSec: meteorNum(METEOR_DEBUG.speedDegPerSec, 18, 1, 80),
|
||||||
|
brightness: meteorNum(METEOR_DEBUG.brightness, 2.4, 0, 8),
|
||||||
|
nightOnly: METEOR_DEBUG.nightOnly === true,
|
||||||
|
debug: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function meteorActiveShowers(sceneDate, realDate) {
|
||||||
|
var out = [];
|
||||||
|
var dbg = meteorDebugActive(sceneDate, realDate);
|
||||||
|
if (dbg) out.push(dbg);
|
||||||
|
for (var i = 0; i < METEOR_SHOWERS.length; i++) {
|
||||||
|
var s = METEOR_SHOWERS[i];
|
||||||
|
var intensity = meteorShowerIntensity(s, sceneDate || realDate || new Date());
|
||||||
|
if (intensity < 0.02) continue;
|
||||||
|
out.push({
|
||||||
|
name: s.name || 'Shower',
|
||||||
|
intensity: intensity,
|
||||||
|
raH: s.raH,
|
||||||
|
decDeg: s.decDeg,
|
||||||
|
rate: METEOR_RATE * intensity,
|
||||||
|
maxActive: METEOR_MAX_ACTIVE,
|
||||||
|
lengthDeg: METEOR_LENGTH,
|
||||||
|
speedDegPerSec: METEOR_SPEED,
|
||||||
|
brightness: METEOR_BRIGHT * intensity,
|
||||||
|
nightOnly: METEOR_NIGHT_ONLY,
|
||||||
|
debug: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
function meteorSpawn(shower, now) {
|
||||||
|
var lst = starSidereal();
|
||||||
|
if (lst === null) lst = ((window.__currentDayProgress || 0) * 6.2831853);
|
||||||
|
var R = meteorLocalDir(shower.raH, shower.decDeg, lst);
|
||||||
|
if (R[1] < -0.25) return null;
|
||||||
|
var helper = (Math.abs(R[1]) < 0.8) ? [0, 1, 0] : [1, 0, 0];
|
||||||
|
var u = meteorNorm(meteorCross(R, helper));
|
||||||
|
var v = meteorNorm(meteorCross(R, u));
|
||||||
|
var P = null;
|
||||||
|
for (var attempt = 0; attempt < 10; attempt++) {
|
||||||
|
var phi = Math.random() * 6.2831853;
|
||||||
|
var theta = 0.12 + Math.random() * 1.15;
|
||||||
|
var ct = Math.cos(theta), st = Math.sin(theta);
|
||||||
|
P = meteorNorm([
|
||||||
|
R[0] * ct + u[0] * st * Math.cos(phi) + v[0] * st * Math.sin(phi),
|
||||||
|
R[1] * ct + u[1] * st * Math.cos(phi) + v[1] * st * Math.sin(phi),
|
||||||
|
R[2] * ct + u[2] * st * Math.cos(phi) + v[2] * st * Math.sin(phi)
|
||||||
|
]);
|
||||||
|
if (P[1] > 0.06) break;
|
||||||
|
}
|
||||||
|
if (!P || P[1] <= 0.06) return null;
|
||||||
|
var radial = meteorDot(P, R);
|
||||||
|
var dir = meteorNorm([P[0] - R[0] * radial, P[1] - R[1] * radial, P[2] - R[2] * radial]);
|
||||||
|
if (Math.abs(dir[0]) + Math.abs(dir[1]) + Math.abs(dir[2]) < 1e-4) dir = u;
|
||||||
|
var angularLen = shower.lengthDeg * DEG * (0.7 + Math.random() * 0.6);
|
||||||
|
var speed = shower.speedDegPerSec * DEG * (0.7 + Math.random() * 0.6);
|
||||||
|
var seed = Math.random();
|
||||||
|
var color = METEOR_COLORS[Math.floor(seed * METEOR_COLORS.length) % METEOR_COLORS.length];
|
||||||
|
return {
|
||||||
|
start: now,
|
||||||
|
lifetime: angularLen / speed,
|
||||||
|
P: P,
|
||||||
|
dir: dir,
|
||||||
|
angularLen: angularLen,
|
||||||
|
speed: speed,
|
||||||
|
seed: seed,
|
||||||
|
color: color,
|
||||||
|
brightness: shower.brightness,
|
||||||
|
shower: shower.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function updateMeteorShower(gl, sceneDate, realDate, now) {
|
||||||
|
if (!gl || !METEORS_ON) return;
|
||||||
|
if (meteorLastT === null) meteorLastT = now;
|
||||||
|
var elapsed = Math.max(0, Math.min(0.25, now - meteorLastT));
|
||||||
|
meteorLastT = now;
|
||||||
|
var active = meteorActiveShowers(sceneDate, realDate);
|
||||||
|
var maxActive = METEOR_MAX_ACTIVE;
|
||||||
|
meteorAllowDay = !METEOR_NIGHT_ONLY;
|
||||||
|
for (var i = 0; i < active.length; i++) {
|
||||||
|
if (active[i].maxActive > maxActive) maxActive = active[i].maxActive;
|
||||||
|
if (active[i].nightOnly === false) meteorAllowDay = true;
|
||||||
|
}
|
||||||
|
for (var j = meteorList.length - 1; j >= 0; j--) {
|
||||||
|
if (now - meteorList[j].start > meteorList[j].lifetime) meteorList.splice(j, 1);
|
||||||
|
}
|
||||||
|
if (active.length && meteorList.length < maxActive) {
|
||||||
|
var totalRate = 0;
|
||||||
|
for (var k = 0; k < active.length; k++) totalRate += active[k].rate * active[k].intensity;
|
||||||
|
meteorSpawnAcc += elapsed * totalRate;
|
||||||
|
while (meteorSpawnAcc >= 1 && meteorList.length < maxActive) {
|
||||||
|
meteorSpawnAcc -= 1;
|
||||||
|
var r = Math.random() * totalRate;
|
||||||
|
var chosen = active[0];
|
||||||
|
for (var a = 0; a < active.length; a++) {
|
||||||
|
r -= active[a].rate * active[a].intensity;
|
||||||
|
if (r <= 0) { chosen = active[a]; break; }
|
||||||
|
}
|
||||||
|
var m = meteorSpawn(chosen, now);
|
||||||
|
if (m) meteorList.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var maxPoints = maxActive * METEOR_TRAIL_POINTS;
|
||||||
|
var data = new Float32Array(maxPoints * 8);
|
||||||
|
var di = 0, count = 0;
|
||||||
|
for (var mi = 0; mi < meteorList.length; mi++) {
|
||||||
|
var mm = meteorList[mi];
|
||||||
|
var age = now - mm.start;
|
||||||
|
if (age < 0) continue;
|
||||||
|
var progress = age / mm.lifetime;
|
||||||
|
if (progress > 1) continue;
|
||||||
|
var headAngle = age * mm.speed;
|
||||||
|
var trailLen = Math.min(headAngle, mm.angularLen);
|
||||||
|
for (var p = 0; p < METEOR_TRAIL_POINTS; p++) {
|
||||||
|
var s = (METEOR_TRAIL_POINTS === 1) ? 0 : p / (METEOR_TRAIL_POINTS - 1);
|
||||||
|
var angle = headAngle - s * trailLen;
|
||||||
|
var ca = Math.cos(angle), sa = Math.sin(angle);
|
||||||
|
var x = mm.P[0] * ca + mm.dir[0] * sa;
|
||||||
|
var y = mm.P[1] * ca + mm.dir[1] * sa;
|
||||||
|
var z = mm.P[2] * ca + mm.dir[2] * sa;
|
||||||
|
var fade = Math.pow(1 - s, 1.35) * Math.pow(1 - progress, 0.35);
|
||||||
|
var intensity = fade * mm.brightness * (0.72 + 0.28 * Math.sin(mm.seed * 628.0 + age * 7.0));
|
||||||
|
if (intensity <= 0.01) continue;
|
||||||
|
data[di++] = x * 88; data[di++] = y * 88; data[di++] = z * 88; data[di++] = mm.seed;
|
||||||
|
data[di++] = mm.color[0]; data[di++] = mm.color[1]; data[di++] = mm.color[2];
|
||||||
|
data[di++] = intensity * METEOR_POINT_SIZE;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!meteorVbo) meteorVbo = gl.createBuffer();
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, meteorVbo);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, data.subarray(0, di), gl.STREAM_DRAW);
|
||||||
|
meteorCount = count;
|
||||||
|
window.__meteorDbg = {
|
||||||
|
enabled: METEORS_ON,
|
||||||
|
active: active.map(function (x) { return { name: x.name, intensity: +x.intensity.toFixed(2), rate: +x.rate.toFixed(2), debug: !!x.debug }; }),
|
||||||
|
meteors: meteorList.length,
|
||||||
|
points: count,
|
||||||
|
spawnAcc: +meteorSpawnAcc.toFixed(2),
|
||||||
|
allowDay: meteorAllowDay
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function ensureMeteorProgram(gl) {
|
||||||
|
if (meteorProg) return true;
|
||||||
|
var prog = makeProgram(gl, METEOR_VERT, METEOR_FRAG);
|
||||||
|
if (!prog) return false;
|
||||||
|
meteorProg = prog;
|
||||||
|
meteorUniforms = {
|
||||||
|
mvp: gl.getUniformLocation(prog, 'uMVP'),
|
||||||
|
sizeScale: gl.getUniformLocation(prog, 'uSizeScale'),
|
||||||
|
a0: gl.getAttribLocation(prog, 'aMpos'),
|
||||||
|
a1: gl.getAttribLocation(prog, 'aMcol')
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function drawMeteorShower(gl, proj, view, sunAlt) {
|
||||||
|
if (!meteorCount || !meteorVbo || !ensureMeteorProgram(gl)) return;
|
||||||
|
var night = 1 - Math.max(0, Math.min(1, (sunAlt + 0.05) / 0.25));
|
||||||
|
if (METEOR_NIGHT_ONLY && !meteorAllowDay && night < 0.02) return;
|
||||||
|
var viewR = new Float32Array(16);
|
||||||
|
viewR[0] = view[0]; viewR[1] = view[1]; viewR[2] = view[2];
|
||||||
|
viewR[4] = view[4]; viewR[5] = view[5]; viewR[6] = view[6];
|
||||||
|
viewR[8] = view[8]; viewR[9] = view[9]; viewR[10] = view[10];
|
||||||
|
viewR[12] = 0; viewR[13] = 0; viewR[14] = 0; viewR[15] = 1;
|
||||||
|
var mvp = new Float32Array(16);
|
||||||
|
mat4Multiply(mvp, proj, viewR);
|
||||||
|
gl.useProgram(meteorProg);
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, meteorVbo);
|
||||||
|
gl.enableVertexAttribArray(meteorUniforms.a0);
|
||||||
|
gl.vertexAttribPointer(meteorUniforms.a0, 4, gl.FLOAT, false, 32, 0);
|
||||||
|
gl.enableVertexAttribArray(meteorUniforms.a1);
|
||||||
|
gl.vertexAttribPointer(meteorUniforms.a1, 4, gl.FLOAT, false, 32, 16);
|
||||||
|
gl.uniformMatrix4fv(meteorUniforms.mvp, false, mvp);
|
||||||
|
gl.uniform1f(meteorUniforms.sizeScale, canvas.height / 900);
|
||||||
|
gl.disable(gl.DEPTH_TEST);
|
||||||
|
gl.depthMask(false);
|
||||||
|
gl.enable(gl.BLEND);
|
||||||
|
gl.blendFunc(gl.ONE, gl.ONE);
|
||||||
|
gl.drawArrays(gl.POINTS, 0, meteorCount);
|
||||||
|
gl.disable(gl.BLEND);
|
||||||
|
gl.enable(gl.DEPTH_TEST);
|
||||||
|
gl.depthMask(true);
|
||||||
|
}
|
||||||
|
|
||||||
function kelvinToRGB(k) {
|
function kelvinToRGB(k) {
|
||||||
// crude blackbody tint
|
// crude blackbody tint
|
||||||
if (k < 3400) return [0.98, 0.84, 0.70];
|
if (k < 3400) return [0.98, 0.84, 0.70];
|
||||||
@@ -7262,6 +7556,11 @@ window.clouds3dRender = function(target) {
|
|||||||
gl.depthMask(true);
|
gl.depthMask(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (METEORS_ON) {
|
||||||
|
updateMeteorShower(gl, window.__currentSceneDate || new Date(), new Date(), now);
|
||||||
|
drawMeteorShower(gl, proj, view, sunAlt);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Terrain mesh pass (real 3D heightfield) ---------------------
|
// ---- Terrain mesh pass (real 3D heightfield) ---------------------
|
||||||
// The mesh VBO is built once by buildTerrainMesh() after the
|
// The mesh VBO is built once by buildTerrainMesh() after the
|
||||||
// elevation grid finishes loading. Depth test+write on so it
|
// elevation grid finishes loading. Depth test+write on so it
|
||||||
|
|||||||
Reference in new issue
Block a user