Upload index.html
Browse files- index.html +36 -15
index.html
CHANGED
|
@@ -1013,7 +1013,7 @@ function buildScene() {
|
|
| 1013 |
const nGrassTufts = Math.max(50, Math.floor(W / 9));
|
| 1014 |
for (let i = 0; i < nGrassTufts; i++) {
|
| 1015 |
const gx = rand(8, W - 8);
|
| 1016 |
-
const gy = sandAt(gx) + rand(
|
| 1017 |
const baseHue = rand(95, 155);
|
| 1018 |
const blades = 2 + ((Math.random() * 4) | 0); // 2..5 blades per tuft
|
| 1019 |
for (let b = 0; b < blades; b++) {
|
|
@@ -1051,7 +1051,7 @@ function buildScene() {
|
|
| 1051 |
const nFgClusters = Math.max(8, Math.floor(W / 120));
|
| 1052 |
for (let i = 0; i < nFgClusters; i++) {
|
| 1053 |
const cx = rand(-10, W + 10);
|
| 1054 |
-
const clusterBaseY = sandAt(cx) + rand(
|
| 1055 |
const baseHue = rand(46, 76); // v12.1: yellowish-green — pops against the mid-ground green grass
|
| 1056 |
const nBlades = 3 + ((Math.random() * 4) | 0); // 3..6 blades per clump
|
| 1057 |
for (let b = 0; b < nBlades; b++) {
|
|
@@ -1064,7 +1064,7 @@ function buildScene() {
|
|
| 1064 |
phase: rand(0, Math.PI * 2),
|
| 1065 |
speed: rand(0.8, 1.6), // slower, heavier sway than the tiny mid-ground grass
|
| 1066 |
hue: baseHue + rand(-10, 10),
|
| 1067 |
-
width: rand(
|
| 1068 |
// v16: organic rest-curve + segmented blade outline (like seaweed)
|
| 1069 |
cA1: rand(-8, 8), // primary bend amplitude (larger for foreground)
|
| 1070 |
cF1: rand(2, 4), // frequency along blade length
|
|
@@ -1288,21 +1288,41 @@ function buildRaySprites() {
|
|
| 1288 |
const sc = spr.getContext("2d");
|
| 1289 |
sc.drawImage(full, 0, 0, bw, bh, 0, 0, spr.width, spr.height);
|
| 1290 |
|
| 1291 |
-
// PERF:
|
| 1292 |
-
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
sc.fillStyle = `rgb(${tr},${tg},${tb})`;
|
| 1296 |
-
sc.fillRect(0, 0, spr.width, spr.height);
|
| 1297 |
-
}
|
| 1298 |
-
|
| 1299 |
-
r.sprite = spr;
|
| 1300 |
r.tw = spr.width; r.th = spr.height; // sprite pixel size
|
| 1301 |
r.bw = bw; // beam width in CSS px (dest size)
|
| 1302 |
r.xMinL = xMinL; // leftmost local x of the beam body
|
| 1303 |
}
|
| 1304 |
|
| 1305 |
raySpritesBuiltFor = W + "x" + H;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1306 |
}
|
| 1307 |
|
| 1308 |
function drawRays(t, light) {
|
|
@@ -1310,11 +1330,12 @@ function drawRays(t, light) {
|
|
| 1310 |
const [rr, rg, rb] = light.ray;
|
| 1311 |
const yTop0 = -10, ySpan = H * 0.95 + 10;
|
| 1312 |
|
| 1313 |
-
// PERF:
|
| 1314 |
-
//
|
|
|
|
| 1315 |
if (!rayTintRGB || Math.abs(rayTintRGB[0] - rr) + Math.abs(rayTintRGB[1] - rg) + Math.abs(rayTintRGB[2] - rb) > 35) {
|
| 1316 |
rayTintRGB = [rr, rg, rb];
|
| 1317 |
-
|
| 1318 |
}
|
| 1319 |
|
| 1320 |
for (const r of rays) {
|
|
|
|
| 1013 |
const nGrassTufts = Math.max(50, Math.floor(W / 9));
|
| 1014 |
for (let i = 0; i < nGrassTufts; i++) {
|
| 1015 |
const gx = rand(8, W - 8);
|
| 1016 |
+
const gy = sandAt(gx) + rand(2, 9); // moved DOWN so every blade roots at/below the sand line — no floating bits, closes the gap fish wedged into
|
| 1017 |
const baseHue = rand(95, 155);
|
| 1018 |
const blades = 2 + ((Math.random() * 4) | 0); // 2..5 blades per tuft
|
| 1019 |
for (let b = 0; b < blades; b++) {
|
|
|
|
| 1051 |
const nFgClusters = Math.max(8, Math.floor(W / 120));
|
| 1052 |
for (let i = 0; i < nFgClusters; i++) {
|
| 1053 |
const cx = rand(-10, W + 10);
|
| 1054 |
+
const clusterBaseY = sandAt(cx) + rand(8, H * 0.06); // moved down (a touch lower than the mid-ground grass) so it sits planted in front
|
| 1055 |
const baseHue = rand(46, 76); // v12.1: yellowish-green — pops against the mid-ground green grass
|
| 1056 |
const nBlades = 3 + ((Math.random() * 4) | 0); // 3..6 blades per clump
|
| 1057 |
for (let b = 0; b < nBlades; b++) {
|
|
|
|
| 1064 |
phase: rand(0, Math.PI * 2),
|
| 1065 |
speed: rand(0.8, 1.6), // slower, heavier sway than the tiny mid-ground grass
|
| 1066 |
hue: baseHue + rand(-10, 10),
|
| 1067 |
+
width: rand(2, 4), // kept slightly thicker than the mid-ground grass (1.4..2.8)
|
| 1068 |
// v16: organic rest-curve + segmented blade outline (like seaweed)
|
| 1069 |
cA1: rand(-8, 8), // primary bend amplitude (larger for foreground)
|
| 1070 |
cF1: rand(2, 4), // frequency along blade length
|
|
|
|
| 1288 |
const sc = spr.getContext("2d");
|
| 1289 |
sc.drawImage(full, 0, 0, bw, bh, 0, 0, spr.width, spr.height);
|
| 1290 |
|
| 1291 |
+
// PERF: store the blurred WHITE beam. Tinting is done separately in applyRayTint()
|
| 1292 |
+
// so that a light-color change only needs a cheap source-in fill instead of
|
| 1293 |
+
// re-running this expensive blur rasterization (which caused transition hitching).
|
| 1294 |
+
r.baseSprite = spr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1295 |
r.tw = spr.width; r.th = spr.height; // sprite pixel size
|
| 1296 |
r.bw = bw; // beam width in CSS px (dest size)
|
| 1297 |
r.xMinL = xMinL; // leftmost local x of the beam body
|
| 1298 |
}
|
| 1299 |
|
| 1300 |
raySpritesBuiltFor = W + "x" + H;
|
| 1301 |
+
applyRayTint(); // ensure every ray has a blittable sprite immediately
|
| 1302 |
+
}
|
| 1303 |
+
|
| 1304 |
+
// Cheaply re-tint the cached blurred sprites with the current light.ray color.
|
| 1305 |
+
// No blur / no shape rasterization — just a source-in fill over the stored beam,
|
| 1306 |
+
// so this stays fast even during slow dusk/dawn transitions.
|
| 1307 |
+
function applyRayTint() {
|
| 1308 |
+
for (const r of rays) {
|
| 1309 |
+
if (!r.baseSprite) continue;
|
| 1310 |
+
if (!r.tintCanvas) {
|
| 1311 |
+
r.tintCanvas = document.createElement("canvas");
|
| 1312 |
+
r.tintCanvas.width = r.tw; r.tintCanvas.height = r.th;
|
| 1313 |
+
}
|
| 1314 |
+
const tc = r.tintCanvas.getContext("2d");
|
| 1315 |
+
tc.clearRect(0, 0, r.tw, r.th);
|
| 1316 |
+
tc.drawImage(r.baseSprite, 0, 0);
|
| 1317 |
+
if (rayTintRGB) {
|
| 1318 |
+
const [tr, tg, tb] = rayTintRGB;
|
| 1319 |
+
tc.globalCompositeOperation = "source-in";
|
| 1320 |
+
tc.fillStyle = `rgb(${tr},${tg},${tb})`;
|
| 1321 |
+
tc.fillRect(0, 0, r.tw, r.th);
|
| 1322 |
+
tc.globalCompositeOperation = "source-over";
|
| 1323 |
+
}
|
| 1324 |
+
r.sprite = r.tintCanvas;
|
| 1325 |
+
}
|
| 1326 |
}
|
| 1327 |
|
| 1328 |
function drawRays(t, light) {
|
|
|
|
| 1330 |
const [rr, rg, rb] = light.ray;
|
| 1331 |
const yTop0 = -10, ySpan = H * 0.95 + 10;
|
| 1332 |
|
| 1333 |
+
// PERF: re-tint the cached blurred sprites only when the light color shifts enough.
|
| 1334 |
+
// Re-tinting is cheap (a source-in fill) — no blur re-rasterization — so this runs
|
| 1335 |
+
// smoothly even during slow dusk/dawn transitions instead of hitching on rebuilds.
|
| 1336 |
if (!rayTintRGB || Math.abs(rayTintRGB[0] - rr) + Math.abs(rayTintRGB[1] - rg) + Math.abs(rayTintRGB[2] - rb) > 35) {
|
| 1337 |
rayTintRGB = [rr, rg, rb];
|
| 1338 |
+
applyRayTint(); // cheap re-tint of the cached blurred sprites (no blur re-run)
|
| 1339 |
}
|
| 1340 |
|
| 1341 |
for (const r of rays) {
|