Smooth periodic sky reprojection
This commit is contained in:
parent
3619d9ca0b
commit
8c1389557b
1 changed files with 47 additions and 8 deletions
|
|
@ -1576,22 +1576,31 @@ let constellationsFileVal = null;
|
|||
|
||||
/* ── Passo de projeção: recalcula posições de tela (a cada 30s o céu gira
|
||||
~0.125°, imperceptível). Camada estática (Via Láctea + estrelas fracas)
|
||||
é pré-renderizada em baseCanvas; só as brilhantes cintilam por frame ── */
|
||||
const baseCanvas = document.createElement('canvas');
|
||||
é pré-renderizada fora da tela; só as brilhantes cintilam por frame.
|
||||
Três buffers impedem que o compositor veja a textura sendo substituída. ── */
|
||||
let baseCanvas = document.createElement('canvas');
|
||||
let previousBaseCanvas = document.createElement('canvas');
|
||||
let renderCanvas = document.createElement('canvas');
|
||||
let hasSkyFrame = false;
|
||||
let skyTransitionStart = -Infinity;
|
||||
const SKY_TRANSITION_MS = 1400;
|
||||
let twinkList = [];
|
||||
let lastProjMs = -Infinity;
|
||||
|
||||
function projectSky() {
|
||||
const W = canvas.width, H = canvas.height;
|
||||
if (!W || !H) return;
|
||||
baseCanvas.width = W;
|
||||
baseCanvas.height = H;
|
||||
const bctx = baseCanvas.getContext('2d');
|
||||
if (renderCanvas.width !== W || renderCanvas.height !== H) {
|
||||
renderCanvas.width = W;
|
||||
renderCanvas.height = H;
|
||||
}
|
||||
const bctx = renderCanvas.getContext('2d');
|
||||
bctx.clearRect(0, 0, W, H);
|
||||
const horizonPx = H * HORIZON_F;
|
||||
const lst = localSiderealDeg(Date.now() + window.__skyTimeOffset);
|
||||
const glareOn = !document.body.classList.contains('balada') &&
|
||||
!document.body.classList.contains('chuva');
|
||||
twinkList = [];
|
||||
const nextTwinkList = [];
|
||||
|
||||
if (renderMilkyWay(lst, glareOn)) {
|
||||
bctx.imageSmoothingEnabled = true;
|
||||
|
|
@ -1621,7 +1630,7 @@ function projectSky() {
|
|||
const c = STAR_PALETTE[ci];
|
||||
if (mag <= TWINKLE_MAG) {
|
||||
/* fase/freq determinísticas por índice — cintilação estável */
|
||||
twinkList.push({
|
||||
nextTwinkList.push({
|
||||
x: sc.x, y: sc.y, r, a,
|
||||
cr: c[0], cg: c[1], cb: c[2],
|
||||
phase: i * 2.3999,
|
||||
|
|
@ -1638,6 +1647,22 @@ function projectSky() {
|
|||
bctx.fillRect(sc.x - r, sc.y - r, r * 2, r * 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* O quadro só se torna visível depois de estar completo. O antigo fica
|
||||
intacto para um crossfade que disfarça o pequeno avanço sideral. */
|
||||
if (hasSkyFrame) {
|
||||
const recycled = previousBaseCanvas;
|
||||
previousBaseCanvas = baseCanvas;
|
||||
baseCanvas = renderCanvas;
|
||||
renderCanvas = recycled;
|
||||
skyTransitionStart = performance.now();
|
||||
} else {
|
||||
const recycled = baseCanvas;
|
||||
baseCanvas = renderCanvas;
|
||||
renderCanvas = recycled;
|
||||
hasSkyFrame = true;
|
||||
}
|
||||
twinkList = nextTwinkList;
|
||||
}
|
||||
|
||||
function setObserverLocation(lat, lon) {
|
||||
|
|
@ -1660,7 +1685,21 @@ function resizeCanvas() {
|
|||
function drawStars(ts) {
|
||||
if (ts - lastProjMs > 30000) { lastProjMs = ts; projectSky(); }
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
const transition = Math.max(0, Math.min(1,
|
||||
(ts - skyTransitionStart) / SKY_TRANSITION_MS));
|
||||
if (transition < 1 && previousBaseCanvas.width === canvas.width &&
|
||||
previousBaseCanvas.height === canvas.height) {
|
||||
const blend = transition * transition * (3 - 2 * transition);
|
||||
ctx.globalAlpha = 1 - blend;
|
||||
ctx.drawImage(previousBaseCanvas, 0, 0);
|
||||
ctx.globalCompositeOperation = 'lighter';
|
||||
ctx.globalAlpha = blend;
|
||||
ctx.drawImage(baseCanvas, 0, 0);
|
||||
ctx.globalCompositeOperation = 'source-over';
|
||||
ctx.globalAlpha = 1;
|
||||
} else {
|
||||
ctx.drawImage(baseCanvas, 0, 0);
|
||||
}
|
||||
const t = ts * 0.001;
|
||||
for (const s of twinkList) {
|
||||
const tw = 1 - s.amp * (0.5 + 0.5 * Math.sin(t * s.freq + s.phase));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue