One commited on
Commit
d66fa33
·
verified ·
1 Parent(s): 16ad637

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +54 -11
index.html CHANGED
@@ -544,15 +544,22 @@ function resize() {
544
  buildScene();
545
  }
546
 
547
- // --- slow light cycle: dark -> blue -> orange -> dark over CYCLE seconds ---
548
- const CYCLE = 240; // seconds per full day/night cycle (night now lasts ~2x as long)
 
 
 
 
 
 
549
  // [t, waterTop, waterMid, waterBot, rayTint, ambientWashRGB, ambientWashAlpha]
550
  const LIGHT_KEYS = [
551
- [0.00, [13,88,118], [8,58,82], [3,25,43], [190,235,255], [190,235,255], 0.00],
552
- [0.35, [224,122,52], [128,60,36], [34,22,26], [255,176,96], [255,150,70], 0.14],
553
- [0.62, [7,15,32], [4,10,22], [2,6,14], [96,146,206], [3,8,20], 0.40],
554
- [0.90, [7,15,32], [4,10,22], [2,6,14], [96,146,206], [3,8,20], 0.40],
555
- [1.00, [13,88,118], [8,58,82], [3,25,43], [190,235,255], [190,235,255], 0.00]
 
556
  ];
557
 
558
  function sampleLight(t) {
@@ -572,10 +579,10 @@ function rgbStr(c) { return `rgb(${c[0]},${c[1]},${c[2]})`; }
572
  // Drives the sleepier night-time behaviour. Cheap: pure math on one number.
573
  function nightFactor(t) {
574
  const p = ((t / CYCLE) % 1 + 1) % 1;
575
- if (p < 0.35 || p >= 0.92) return 0; // bright day
576
- if (p < 0.62) return (p - 0.35) / 0.27; // dusk: fade to night
577
- if (p < 0.90) return 1; // full night
578
- return 1 - (p - 0.90) / 0.02; // pre-dawn: fade to day
579
  }
580
 
581
  function drawBackground(light) {
@@ -860,6 +867,40 @@ function drawVignette() {
860
  ctx.fillRect(0, 0, W, H);
861
  }
862
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
863
  // Precomputed static layers — the sand and rocks never move, so they are
864
  // rendered once to an offscreen canvas and blitted each frame (v6) instead of
865
  // re-drawing their gradients and paths every single frame.
@@ -936,12 +977,14 @@ function frame(now) {
936
  }
937
 
938
  drawVignette();
 
939
  requestAnimationFrame(frame);
940
  }
941
 
942
  function doResize() {
943
  resize();
944
  buildStaticLayer();
 
945
  }
946
  window.addEventListener("resize", doResize);
947
  doResize();
 
544
  buildScene();
545
  }
546
 
547
+ // --- slow light cycle: bright day -> dusk -> night -> dawn, over CYCLE seconds ---
548
+ const CYCLE = 180; // seconds per full day/night cycle (v9: exactly 3 minutes)
549
+ // Phase durations in seconds: daytime blue 90s, dusk 15s, night 60s, dawn 15s. Total = 180s.
550
+ // The cycle starts at the beginning of the bright daytime-blue phase.
551
+ const DUSK_START = 90 / CYCLE; // p where day ends and dusk begins (0.5)
552
+ const NIGHT_START = (90 + 15) / CYCLE; // p where dusk ends and night begins (~0.5833)
553
+ const DAWN_START = (90 + 15 + 60) / CYCLE; // p where night ends and dawn begins (~0.9167)
554
+ const MID_DUSK = (DUSK_START + NIGHT_START) / 2; // sunset-glow point, mid-dusk
555
  // [t, waterTop, waterMid, waterBot, rayTint, ambientWashRGB, ambientWashAlpha]
556
  const LIGHT_KEYS = [
557
+ [0.00, [13,88,118], [8,58,82], [3,25,43], [190,235,255], [190,235,255], 0.00],
558
+ [DUSK_START, [13,88,118], [8,58,82], [3,25,43], [190,235,255], [190,235,255], 0.00],
559
+ [MID_DUSK, [224,122,52], [128,60,36], [34,22,26], [255,176,96], [255,150,70], 0.14],
560
+ [NIGHT_START,[7,15,32], [4,10,22], [2,6,14], [96,146,206], [3,8,20], 0.40],
561
+ [DAWN_START, [7,15,32], [4,10,22], [2,6,14], [96,146,206], [3,8,20], 0.40],
562
+ [1.00, [13,88,118], [8,58,82], [3,25,43], [190,235,255], [190,235,255], 0.00]
563
  ];
564
 
565
  function sampleLight(t) {
 
579
  // Drives the sleepier night-time behaviour. Cheap: pure math on one number.
580
  function nightFactor(t) {
581
  const p = ((t / CYCLE) % 1 + 1) % 1;
582
+ if (p < DUSK_START) return 0; // bright day
583
+ if (p < NIGHT_START) return (p - DUSK_START) / (NIGHT_START - DUSK_START); // dusk: fade to night
584
+ if (p < DAWN_START) return 1; // full night
585
+ return 1 - (p - DAWN_START) / (1 - DAWN_START); // dawn: fade to day
586
  }
587
 
588
  function drawBackground(light) {
 
867
  ctx.fillRect(0, 0, W, H);
868
  }
869
 
870
+ // --- v8: mild film grain over the whole scene.
871
+ // Efficient: a small noise tile is generated ONCE and turned into a cached pattern.
872
+ // Each frame we just blit that pattern with 'overlay' blending at low opacity and a
873
+ // random sub-tile offset, so it shimmers like real film. Cost = one pattern fillRect.
874
+ let grainPattern = null;
875
+ const GRAIN_TILE = 160; // tile size in px (kept small so the pattern is cheap to repeat)
876
+ function buildGrainTile() {
877
+ const tile = document.createElement("canvas");
878
+ tile.width = GRAIN_TILE; tile.height = GRAIN_TILE;
879
+ const g = tile.getContext("2d");
880
+ const img = g.createImageData(GRAIN_TILE, GRAIN_TILE);
881
+ const d = img.data;
882
+ for (let i = 0; i < d.length; i += 4) {
883
+ // grayscale noise centred on mid-grey (~128): with 'overlay' blend, values near
884
+ // 128 are neutral while higher/lower ones gently brighten/darken -> filmic grain.
885
+ const v = 96 + Math.floor(Math.random() * 64); // 96..159, centred on 128
886
+ d[i] = v; d[i+1] = v; d[i+2] = v; d[i+3] = 255;
887
+ }
888
+ g.putImageData(img, 0, 0);
889
+ grainPattern = ctx.createPattern(tile, "repeat"); // cached once, reused every frame
890
+ }
891
+ function drawGrain() {
892
+ if (!grainPattern) return;
893
+ const ox = (Math.random() * GRAIN_TILE) | 0; // random offset each frame -> animated grain
894
+ const oy = (Math.random() * GRAIN_TILE) | 0;
895
+ ctx.save();
896
+ ctx.globalCompositeOperation = "overlay";
897
+ ctx.globalAlpha = 0.7; // mild: subtle shimmer, not heavy noise
898
+ ctx.translate(-ox, -oy);
899
+ ctx.fillStyle = grainPattern;
900
+ ctx.fillRect(0, 0, W + GRAIN_TILE, H + GRAIN_TILE); // cover full screen even with offset
901
+ ctx.restore();
902
+ }
903
+
904
  // Precomputed static layers — the sand and rocks never move, so they are
905
  // rendered once to an offscreen canvas and blitted each frame (v6) instead of
906
  // re-drawing their gradients and paths every single frame.
 
977
  }
978
 
979
  drawVignette();
980
+ drawGrain(); // v8: mild film grain on top of everything
981
  requestAnimationFrame(frame);
982
  }
983
 
984
  function doResize() {
985
  resize();
986
  buildStaticLayer();
987
+ if (!grainPattern) buildGrainTile(); // v8: grain tile is size-independent, build once
988
  }
989
  window.addEventListener("resize", doResize);
990
  doResize();