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
|
/* ── 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)
|
~0.125°, imperceptível). Camada estática (Via Láctea + estrelas fracas)
|
||||||
é pré-renderizada em baseCanvas; só as brilhantes cintilam por frame ── */
|
é pré-renderizada fora da tela; só as brilhantes cintilam por frame.
|
||||||
const baseCanvas = document.createElement('canvas');
|
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 twinkList = [];
|
||||||
let lastProjMs = -Infinity;
|
let lastProjMs = -Infinity;
|
||||||
|
|
||||||
function projectSky() {
|
function projectSky() {
|
||||||
const W = canvas.width, H = canvas.height;
|
const W = canvas.width, H = canvas.height;
|
||||||
if (!W || !H) return;
|
if (!W || !H) return;
|
||||||
baseCanvas.width = W;
|
if (renderCanvas.width !== W || renderCanvas.height !== H) {
|
||||||
baseCanvas.height = H;
|
renderCanvas.width = W;
|
||||||
const bctx = baseCanvas.getContext('2d');
|
renderCanvas.height = H;
|
||||||
|
}
|
||||||
|
const bctx = renderCanvas.getContext('2d');
|
||||||
|
bctx.clearRect(0, 0, W, H);
|
||||||
const horizonPx = H * HORIZON_F;
|
const horizonPx = H * HORIZON_F;
|
||||||
const lst = localSiderealDeg(Date.now() + window.__skyTimeOffset);
|
const lst = localSiderealDeg(Date.now() + window.__skyTimeOffset);
|
||||||
const glareOn = !document.body.classList.contains('balada') &&
|
const glareOn = !document.body.classList.contains('balada') &&
|
||||||
!document.body.classList.contains('chuva');
|
!document.body.classList.contains('chuva');
|
||||||
twinkList = [];
|
const nextTwinkList = [];
|
||||||
|
|
||||||
if (renderMilkyWay(lst, glareOn)) {
|
if (renderMilkyWay(lst, glareOn)) {
|
||||||
bctx.imageSmoothingEnabled = true;
|
bctx.imageSmoothingEnabled = true;
|
||||||
|
|
@ -1621,7 +1630,7 @@ function projectSky() {
|
||||||
const c = STAR_PALETTE[ci];
|
const c = STAR_PALETTE[ci];
|
||||||
if (mag <= TWINKLE_MAG) {
|
if (mag <= TWINKLE_MAG) {
|
||||||
/* fase/freq determinísticas por índice — cintilação estável */
|
/* fase/freq determinísticas por índice — cintilação estável */
|
||||||
twinkList.push({
|
nextTwinkList.push({
|
||||||
x: sc.x, y: sc.y, r, a,
|
x: sc.x, y: sc.y, r, a,
|
||||||
cr: c[0], cg: c[1], cb: c[2],
|
cr: c[0], cg: c[1], cb: c[2],
|
||||||
phase: i * 2.3999,
|
phase: i * 2.3999,
|
||||||
|
|
@ -1638,6 +1647,22 @@ function projectSky() {
|
||||||
bctx.fillRect(sc.x - r, sc.y - r, r * 2, r * 2);
|
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) {
|
function setObserverLocation(lat, lon) {
|
||||||
|
|
@ -1660,7 +1685,21 @@ function resizeCanvas() {
|
||||||
function drawStars(ts) {
|
function drawStars(ts) {
|
||||||
if (ts - lastProjMs > 30000) { lastProjMs = ts; projectSky(); }
|
if (ts - lastProjMs > 30000) { lastProjMs = ts; projectSky(); }
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
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.drawImage(baseCanvas, 0, 0);
|
||||||
|
ctx.globalCompositeOperation = 'source-over';
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(baseCanvas, 0, 0);
|
||||||
|
}
|
||||||
const t = ts * 0.001;
|
const t = ts * 0.001;
|
||||||
for (const s of twinkList) {
|
for (const s of twinkList) {
|
||||||
const tw = 1 - s.amp * (0.5 + 0.5 * Math.sin(t * s.freq + s.phase));
|
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