From f56f8d919172b928bb04912e2dce282353926763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A0=81=E5=86=9C=E9=98=BF=E6=A5=A0?= Date: Mon, 25 May 2026 23:06:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=93=B6=E6=B2=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/config.ts | 14 +++++++ src/utils/createTravelScene.ts | 71 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/constants/config.ts b/src/constants/config.ts index 4b3db9e..725d300 100644 --- a/src/constants/config.ts +++ b/src/constants/config.ts @@ -64,6 +64,20 @@ export const APP_CONFIG = { pointerSmoothing: 0.08, maxOffset: 0.4, }, + // Galaxy parameters for background spiral galaxy layer. + galaxy: { + count: 50000, + radius: 15, + branches: 20, + spin: 0.3, + randomness: 0.15, + randomnessPower: 6, + size: 0.008, + rotationSpeed: 0.001, + zPosition: -35, + insideColor: '#ff6030', + outsideColor: '#3861cbff', + }, // Shared UI timings used by overlays and toolbars. ui: { overlayFadeMs: 320, diff --git a/src/utils/createTravelScene.ts b/src/utils/createTravelScene.ts index ed86bf5..21d8896 100644 --- a/src/utils/createTravelScene.ts +++ b/src/utils/createTravelScene.ts @@ -234,6 +234,17 @@ export function createTravelScene(container: HTMLElement): TravelSceneController const ambientLight = new THREE.AmbientLight('#d9efff', 0.45) scene.add(ambientLight) + const galaxyGroup = new THREE.Group() + galaxyGroup.position.z = APP_CONFIG.galaxy.zPosition + // Tilt the galaxy to give it a perspective view + galaxyGroup.rotation.x = Math.PI * 0.35 + galaxyGroup.rotation.y = Math.PI * 0.15 + scene.add(galaxyGroup) + + // Create an inner group for the galaxy rotation to preserve the tilt + const galaxyInnerGroup = new THREE.Group() + galaxyGroup.add(galaxyInnerGroup) + const starsGeometry = new THREE.BufferGeometry() const starPositions = new Float32Array(STAR_COUNT * 3) const starColors = new Float32Array(STAR_COUNT * 3) @@ -382,6 +393,61 @@ export function createTravelScene(container: HTMLElement): TravelSceneController travelRoot.add(state.mesh) } + const galaxyGeometry = new THREE.BufferGeometry() + const galaxyPositions = new Float32Array(APP_CONFIG.galaxy.count * 3) + const galaxyColors = new Float32Array(APP_CONFIG.galaxy.count * 3) + const colorInside = new THREE.Color(APP_CONFIG.galaxy.insideColor) + const colorOutside = new THREE.Color(APP_CONFIG.galaxy.outsideColor) + + for (let i = 0; i < APP_CONFIG.galaxy.count; i += 1) { + const i3 = i * 3 + const radius = Math.random() * APP_CONFIG.galaxy.radius + const branchesAngle = + ((i % APP_CONFIG.galaxy.branches) / APP_CONFIG.galaxy.branches) * Math.PI * 2 + const spinAngle = radius * APP_CONFIG.galaxy.spin + + const mixedColor = colorInside.clone() + mixedColor.lerp(colorOutside, radius / APP_CONFIG.galaxy.radius) + + const randomX = + Math.random() ** APP_CONFIG.galaxy.randomnessPower * + (Math.random() < 0.5 ? 1 : -1) * + APP_CONFIG.galaxy.randomness * + radius + const randomY = + Math.random() ** APP_CONFIG.galaxy.randomnessPower * + (Math.random() < 0.5 ? 1 : -1) * + APP_CONFIG.galaxy.randomness * + radius + const randomZ = + Math.random() ** APP_CONFIG.galaxy.randomnessPower * + (Math.random() < 0.5 ? 1 : -1) * + APP_CONFIG.galaxy.randomness * + radius + + galaxyPositions[i3] = Math.cos(branchesAngle + spinAngle) * radius + randomX + galaxyPositions[i3 + 1] = Math.sin(branchesAngle + spinAngle) * radius + randomY + galaxyPositions[i3 + 2] = randomZ + + galaxyColors[i3] = mixedColor.r + galaxyColors[i3 + 1] = mixedColor.g + galaxyColors[i3 + 2] = mixedColor.b + } + + galaxyGeometry.setAttribute('position', new THREE.BufferAttribute(galaxyPositions, 3)) + galaxyGeometry.setAttribute('color', new THREE.BufferAttribute(galaxyColors, 3)) + + const galaxyMaterial = new THREE.PointsMaterial({ + size: APP_CONFIG.galaxy.size, + sizeAttenuation: true, + depthWrite: false, + blending: THREE.AdditiveBlending, + vertexColors: true, + }) + + const galaxyPoints = new THREE.Points(galaxyGeometry, galaxyMaterial) + galaxyInnerGroup.add(galaxyPoints) + const clock = new THREE.Clock() const onResize = () => { @@ -523,6 +589,8 @@ export function createTravelScene(container: HTMLElement): TravelSceneController updateStars(motionDelta) updateSmoke(motionDelta * 0.62, elapsed) + galaxyInnerGroup.rotation.z += APP_CONFIG.galaxy.rotationSpeed * currentSpeed + travelRoot.rotation.z = pointerCurrent.x * 0.03 travelRoot.position.x = -pointerCurrent.x * 0.8 travelRoot.position.y = -pointerCurrent.y * 0.45 @@ -563,6 +631,9 @@ export function createTravelScene(container: HTMLElement): TravelSceneController travelRoot.remove(smokeState.mesh) } + galaxyGeometry.dispose() + galaxyMaterial.dispose() + starsGeometry.dispose() starMaterial.dispose() smokeGeometry.dispose()