From 43e510341eedc94f9173446bc3e01d9229942742 Mon Sep 17 00:00:00 2001 From: LucaP Date: Sat, 5 Sep 2026 12:36:34 +0300 Subject: [PATCH] feat: weave a flowing multicolor palette through the terrain grid --- src/scene/world/terrain.ts | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/scene/world/terrain.ts b/src/scene/world/terrain.ts index 5b4e2a9..9761801 100644 --- a/src/scene/world/terrain.ts +++ b/src/scene/world/terrain.ts @@ -84,6 +84,19 @@ export function createTerrain( varying float vDepthFade; varying float vFlow; varying vec3 vTerrainPosition; + varying vec3 vGridColor; + + // A closed palette ribbon: its ends meet in cyan so travel never + // produces a color seam. Compute it per vertex, not per pixel. + vec3 gridSpectrum(float phase) { + float p = fract(phase); + vec3 cyan = vec3(0.025, 0.70, 0.90); + vec3 color = mix(cyan, vec3(0.075, 0.18, 0.80), smoothstep(0.04, 0.22, p)); + color = mix(color, vec3(0.39, 0.085, 0.80), smoothstep(0.22, 0.40, p)); + color = mix(color, vec3(0.88, 0.035, 0.28), smoothstep(0.40, 0.60, p)); + color = mix(color, vec3(0.95, 0.21, 0.08), smoothstep(0.60, 0.78, p)); + return mix(color, cyan, smoothstep(0.78, 1.0, p)); + } float hash21(vec2 point) { point = mod(point, 256.0); @@ -127,6 +140,12 @@ export function createTerrain( vHeight = clamp(height / max(1.0, uRelief), 0.0, 1.0); vRoadMask = roadMask; + // Signed lateral variation gives each bank its own color rhythm. + // Whole longitudinal cycles preserve the existing terrain wrap. + float chroma = world.y * ${(31 / NOISE_PERIOD).toFixed(10)} + + position.x * 0.014 + height * 0.025 + + 0.12 * sin(world.y * ${((Math.PI * 2 * 17) / NOISE_PERIOD).toFixed(10)} + position.x * 0.028); + vGridColor = gridSpectrum(chroma); vec4 viewPosition = modelViewMatrix * vec4(displaced, 1.0); float viewDistance = -viewPosition.z; vDepthFade = smoothstep(4.0, 18.0, viewDistance) @@ -148,13 +167,15 @@ export function createTerrain( varying float vDepthFade; varying float vFlow; varying vec3 vTerrainPosition; + varying vec3 vGridColor; void main() { float ridgeMix = smoothstep(0.025, 0.29, vHeight); - vec3 valley = mix(uLowColor, vec3(0.10, 0.64, 1.0), uOverdrive * 0.55); - vec3 crest = mix(vec3(0.7, 0.025, 0.22), vec3(1.0, 0.12, 0.065), uOverdrive * 0.75 + vFlow * 0.18); - vec3 color = mix(valley, mix(vec3(0.28, 0.06, 0.52), crest, ridgeMix), ridgeMix); - color = mix(color, uHighColor, 0.13); + vec3 color = mix(vGridColor, uLowColor, 0.06); + // Speed warms passing accents, while the full palette stays present. + float hotAccent = uOverdrive * smoothstep(0.62, 1.0, vFlow) * 0.36; + color = mix(color, vec3(1.0, 0.28, 0.10), hotAccent); + color = mix(color, uHighColor, ridgeMix * 0.055); color = mix(color, mix(uFogColor, uHazeColor, 0.2), 1.0 - vDepthFade); float pulse = 0.88 + 0.12 * smoothstep(0.16, 0.55, vFlow); float routeCalm = mix(1.0, 0.9, smoothstep(0.82, 1.0, uRouteProgress));