初版成品
This commit is contained in:
+306
-76
@@ -61,7 +61,13 @@ const SMOKE_RESET_Z = APP_CONFIG.smoke.resetZ
|
||||
const SMOKE_BOUNDS_X = APP_CONFIG.smoke.boundsX
|
||||
const SMOKE_BOUNDS_Y = APP_CONFIG.smoke.boundsY
|
||||
|
||||
const STAR_COLORS = ['#ffffff', '#f3f8ff', '#e8f4ff', '#d7eeff', '#fff7ea'] as const
|
||||
const STAR_COLORS = [
|
||||
'#00e5ff', // 亮青
|
||||
'#ff00ea', // 亮粉
|
||||
'#ffcc00', // 亮金
|
||||
'#00ff66', // 亮绿
|
||||
'#ff3333', // 亮红
|
||||
] as const
|
||||
const SMOKE_COLORS = ['#8dd8ff', '#adc4ff', '#d6b5ff', '#ffe7ff'] as const
|
||||
|
||||
const STAR_VERTEX_SHADER = `
|
||||
@@ -78,11 +84,13 @@ varying vec2 vDir;
|
||||
void main() {
|
||||
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
|
||||
float scaledProgress = max(aProgress, 0.0);
|
||||
float pointSize = aSizeBase * (0.95 + scaledProgress * 2.85) * ${APP_CONFIG.starfield.pointScale.toFixed(1)} / max(1.0, -mvPosition.z);
|
||||
// 放大精灵尺寸以容纳更长的拖尾
|
||||
float pointSize = aSizeBase * (0.95 + scaledProgress * 2.85) * ${APP_CONFIG.starfield.pointScale.toFixed(1)} / max(1.0, -mvPosition.z) * 2.5;
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
gl_PointSize = clamp(pointSize, ${APP_CONFIG.starfield.pointMinSize.toFixed(1)}, ${APP_CONFIG.starfield.pointMaxSize.toFixed(1)});
|
||||
gl_PointSize = clamp(pointSize, ${APP_CONFIG.starfield.pointMinSize.toFixed(1)} * 2.5, ${APP_CONFIG.starfield.pointMaxSize.toFixed(1)} * 2.5);
|
||||
vColor = color;
|
||||
vAlpha = aAlphaBase * (0.32 + scaledProgress * 0.68);
|
||||
// 取消原本透明度的距离衰减,直接用最高的 baseAlpha 甚至更高,保持全局最亮
|
||||
vAlpha = aAlphaBase * 1.5;
|
||||
vTail = aTail;
|
||||
vDir = aDir;
|
||||
}
|
||||
@@ -93,27 +101,246 @@ varying vec3 vColor;
|
||||
varying float vAlpha;
|
||||
varying float vTail;
|
||||
varying vec2 vDir;
|
||||
uniform float uSpeed;
|
||||
|
||||
void main() {
|
||||
vec2 centered = gl_PointCoord - vec2(0.5);
|
||||
// 放大坐标系,这样中心点画得相对较小,留出极大空间画长尾巴
|
||||
vec2 centered = (gl_PointCoord - vec2(0.5)) * 2.5;
|
||||
float distanceToCenter = length(centered);
|
||||
float core = 1.0 - smoothstep(0.0, 0.22, distanceToCenter);
|
||||
float halo = 1.0 - smoothstep(0.12, 0.5, distanceToCenter);
|
||||
vec2 dir = length(vDir) > 0.0001 ? normalize(vDir) : vec2(0.0, 1.0);
|
||||
float hotspot = 1.0 - smoothstep(0.0, 0.06, distanceToCenter);
|
||||
hotspot = pow(hotspot, 0.32);
|
||||
float core = 1.0 - smoothstep(0.0, 0.13, distanceToCenter);
|
||||
core = pow(core, 0.44);
|
||||
float halo = 1.0 - smoothstep(0.015, 0.22, distanceToCenter);
|
||||
halo = pow(halo, 1.95);
|
||||
|
||||
// 翻转 Y 轴以匹配 WebGL 纹理坐标系
|
||||
vec2 dir = length(vDir) > 0.0001 ? normalize(vec2(vDir.x, -vDir.y)) : vec2(0.0, -1.0);
|
||||
vec2 side = vec2(-dir.y, dir.x);
|
||||
float along = dot(centered, dir);
|
||||
float across = dot(centered, side);
|
||||
float tailLong = 1.0 - smoothstep(-${(0.18 + APP_CONFIG.starfield.trailLength).toFixed(2)}, 0.05, along);
|
||||
float tailThin = 1.0 - smoothstep(${(APP_CONFIG.starfield.trailWidth * 0.35).toFixed(2)}, ${(APP_CONFIG.starfield.trailWidth).toFixed(2)}, abs(across));
|
||||
float tailFade = smoothstep(0.01, 0.52, -along);
|
||||
float tail = tailLong * tailThin * tailFade * vTail;
|
||||
float alpha = (core * 0.95 + halo * 0.45 + tail * 0.85) * vAlpha;
|
||||
|
||||
float back = -along;
|
||||
// 拖尾长度根据当前速度动态变化
|
||||
float speedFactor = max(uSpeed, 0.1);
|
||||
float tailLength = clamp(${(APP_CONFIG.starfield.trailLength).toFixed(2)} * 2.5 * speedFactor, 0.05, 2.5);
|
||||
float tailFade = 1.0 - smoothstep(0.0, tailLength, back);
|
||||
float tailMask = smoothstep(-0.02, 0.05, back);
|
||||
|
||||
float maxWidth = ${(APP_CONFIG.starfield.trailWidth).toFixed(2)};
|
||||
float width = maxWidth * mix(0.1, 1.0, pow(tailFade, 0.8));
|
||||
|
||||
float tailProfile = 1.0 - smoothstep(0.0, width, abs(across));
|
||||
float tailHotspot = 1.0 - smoothstep(0.0, width * 0.3, abs(across));
|
||||
// 速度越快,尾巴亮度也适当增强
|
||||
float tail = (tailProfile * 0.8 + tailHotspot * 1.5) * tailFade * tailMask * vTail * (0.4 + 0.6 * speedFactor);
|
||||
|
||||
// 极高的能量累加,不丢弃任何 alpha,强制发光
|
||||
float alpha = (hotspot * 3.5 + core * 2.5 + halo * 0.5 + tail * 2.8) * vAlpha;
|
||||
|
||||
if (alpha < 0.01) {
|
||||
discard;
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(vColor, alpha);
|
||||
vec3 color = vColor;
|
||||
// 让高光点保持自身颜色的同时极度明亮,而不仅是变白
|
||||
vec3 hotColor = mix(vColor, vec3(1.0), 0.5);
|
||||
color = mix(color, hotColor, hotspot * 0.8 + core * 0.4);
|
||||
color += hotColor * hotspot * 2.0;
|
||||
color += vColor * core * 1.5;
|
||||
color += vColor * tail * 1.8;
|
||||
|
||||
// 用极其暴力的颜色叠加
|
||||
gl_FragColor = vec4(min(color, vec3(1.0)), min(alpha, 1.0));
|
||||
}
|
||||
`
|
||||
|
||||
const GALAXY_VERTEX_SHADER = `
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
`
|
||||
|
||||
const GALAXY_FRAGMENT_SHADER = `
|
||||
varying vec2 vUv;
|
||||
|
||||
uniform float uTime;
|
||||
uniform float uOpacity;
|
||||
uniform float uArms;
|
||||
uniform float uTwist;
|
||||
uniform float uArmSharpness;
|
||||
uniform float uNoiseScale;
|
||||
uniform float uNoiseStrength;
|
||||
uniform float uCoreRadius;
|
||||
uniform float uCoreGlow;
|
||||
uniform float uGlowStrength;
|
||||
uniform float uSeed;
|
||||
uniform vec3 uCoreWhite;
|
||||
uniform vec3 uCoreGold;
|
||||
uniform vec3 uWarmOrange;
|
||||
uniform vec3 uWarmMagenta;
|
||||
uniform vec3 uOuterBlue;
|
||||
uniform vec3 uDeepBlue;
|
||||
uniform vec3 uInnerColor;
|
||||
uniform vec3 uOuterColor;
|
||||
uniform vec3 uDustColor;
|
||||
|
||||
float hash(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
||||
}
|
||||
|
||||
float noise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
|
||||
return mix(
|
||||
mix(hash(i + vec2(0.0, 0.0)), hash(i + vec2(1.0, 0.0)), u.x),
|
||||
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),
|
||||
u.y
|
||||
);
|
||||
}
|
||||
|
||||
float fbm(vec2 p) {
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
value += amplitude * noise(p);
|
||||
p = p * 2.03 + vec2(17.3, 9.2);
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
vec3 sampleGalaxyRamp(float t) {
|
||||
float value = clamp(t, 0.0, 1.0);
|
||||
vec3 color = uCoreWhite;
|
||||
|
||||
color = mix(color, uCoreGold, smoothstep(0.03, 0.18, value));
|
||||
color = mix(color, uWarmOrange, smoothstep(0.12, 0.32, value));
|
||||
color = mix(color, uWarmMagenta, smoothstep(0.24, 0.58, value));
|
||||
color = mix(color, uOuterBlue, smoothstep(0.5, 0.84, value));
|
||||
color = mix(color, uDeepBlue, smoothstep(0.76, 1.0, value));
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 p = vUv * 2.0 - 1.0;
|
||||
p.x *= 1.08;
|
||||
|
||||
float radius = length(p);
|
||||
float angle = atan(p.y, p.x);
|
||||
float time = uTime * 0.045;
|
||||
vec2 flowUv = p * uNoiseScale;
|
||||
|
||||
float lowNoise = fbm(flowUv + vec2(time * 0.35 + uSeed * 5.1, -time * 0.18));
|
||||
float highNoise = fbm(flowUv * 2.1 - vec2(time * 0.42, time * 0.26 + uSeed * 3.7));
|
||||
float veilNoise = fbm(p * (uNoiseScale * 0.55) + vec2(-time * 0.12, time * 0.08 + uSeed * 1.9));
|
||||
float filamentNoise = fbm(flowUv * 3.4 + vec2(time * 0.18 - uSeed * 2.7, time * 0.24));
|
||||
float clusterNoise = fbm(flowUv * 5.1 + vec2(-time * 0.32 + uSeed * 8.4, time * 0.21));
|
||||
float grainNoise = noise(flowUv * 13.5 + vec2(time * 1.8 + uSeed * 14.0, -time * 1.3));
|
||||
float fineNoise = fbm(flowUv * 8.2 - vec2(time * 0.65 - uSeed * 6.2, time * 0.52));
|
||||
float warpedAngle = angle - radius * uTwist + (lowNoise - 0.5) * uNoiseStrength * 3.2;
|
||||
vec2 swirlDir = vec2(cos(warpedAngle), sin(warpedAngle));
|
||||
vec2 swirlUv = vec2(dot(p, swirlDir), dot(p, vec2(-swirlDir.y, swirlDir.x)));
|
||||
float streakNoise = fbm(swirlUv * vec2(24.0, 3.2) + vec2(time * 0.12 + uSeed * 4.6, -time * 0.06));
|
||||
float mottledNoise = fbm(swirlUv * vec2(10.0, 6.5) - vec2(time * 0.08 - uSeed * 5.2, time * 0.04));
|
||||
float fineFiberNoise = fbm(swirlUv * vec2(46.0, 1.8) + vec2(time * 0.06 + uSeed * 11.4, -time * 0.03));
|
||||
float occlusionNoise = fbm(swirlUv * vec2(5.4, 4.1) - vec2(time * 0.04 - uSeed * 2.6, time * 0.02));
|
||||
float spiralWave = cos(warpedAngle * uArms);
|
||||
float armBase = spiralWave * 0.5 + 0.5;
|
||||
float armMask = smoothstep(1.0 - uArmSharpness, 1.0, armBase);
|
||||
float softArms = smoothstep(0.2, 0.92, armBase + (lowNoise - 0.5) * 0.18);
|
||||
float halo = 1.0 - smoothstep(0.12, 1.45, radius);
|
||||
float outerHalo = 1.0 - smoothstep(0.4, 1.6, radius);
|
||||
float core = 1.0 - smoothstep(0.0, uCoreRadius, radius);
|
||||
float coreBloom = 1.0 - smoothstep(0.0, uCoreRadius * 1.6, radius);
|
||||
float hotCore = 1.0 - smoothstep(0.0, uCoreRadius * 0.95, radius);
|
||||
float dust = smoothstep(0.24, 0.9, highNoise);
|
||||
float wisps = smoothstep(0.44, 0.9, lowNoise) * (1.0 - smoothstep(0.38, 1.08, radius));
|
||||
float outerVeil = smoothstep(0.34, 0.88, veilNoise) * outerHalo;
|
||||
float rim = smoothstep(0.3, 0.98, radius) * (1.0 - smoothstep(0.92, 1.3, radius));
|
||||
float armRidge = pow(armMask, 1.45) * halo;
|
||||
float lanePattern = 1.0 - smoothstep(0.2, 0.72, abs(sin(warpedAngle * (uArms * 0.55) + filamentNoise * 4.4)));
|
||||
float dustLane = lanePattern * smoothstep(0.12, 0.88, radius) * halo * (0.35 + (1.0 - armMask) * 0.9);
|
||||
float clumps = smoothstep(0.62, 0.9, filamentNoise) * softArms * rim;
|
||||
float pulse = 0.5 + 0.5 * sin(uTime * 1.6 + uSeed * 9.0);
|
||||
float shockRadius = uCoreRadius * (1.08 + pulse * 0.42);
|
||||
float shockRing = smoothstep(shockRadius - 0.17, shockRadius - 0.04, radius) *
|
||||
(1.0 - smoothstep(shockRadius + 0.03, shockRadius + 0.11, radius)) *
|
||||
mix(0.55, 1.0, armMask);
|
||||
float clusterMask = smoothstep(0.72, 0.9, clusterNoise) * pow(softArms, 1.45) * rim;
|
||||
float clusterTwinkle = 0.55 + 0.45 * sin(uTime * 2.6 + angle * uArms * 3.2 + radius * 16.0 + uSeed * 12.0);
|
||||
float armSpark = clusterMask * clusterTwinkle;
|
||||
float edgeBreak = smoothstep(0.72, 0.95, veilNoise) * smoothstep(0.55, 1.08, radius) * outerHalo;
|
||||
float grainMask = smoothstep(0.36, 0.84, grainNoise) * mix(0.32, 1.0, softArms) * halo;
|
||||
float dustTexture = smoothstep(0.28, 0.78, fineNoise) * halo * (0.45 + (1.0 - armMask) * 0.8);
|
||||
float fiberBands = smoothstep(0.48, 0.88, streakNoise) * halo * smoothstep(0.12, 0.92, radius);
|
||||
float mottledDust = smoothstep(0.34, 0.82, mottledNoise) * halo;
|
||||
float fineFibers = smoothstep(0.54, 0.9, fineFiberNoise) * halo * smoothstep(0.1, 0.96, radius);
|
||||
float deepOcclusion = smoothstep(0.38, 0.8, occlusionNoise) * halo * smoothstep(0.18, 1.0, radius);
|
||||
float dustFilaments = fiberBands * (0.4 + (1.0 - armMask) * 0.9) * (0.6 + dustLane * 0.8);
|
||||
float outerSparseFibers = smoothstep(0.44, 0.88, streakNoise) *
|
||||
smoothstep(0.48, 1.08, radius) *
|
||||
outerHalo *
|
||||
(0.45 + veilNoise * 0.8);
|
||||
float microSpeckles = smoothstep(0.955, 0.992, grainNoise) * (0.18 + clusterTwinkle * 0.32) * softArms * rim;
|
||||
float density = halo * mix(0.44, 1.0, armMask) * mix(0.88, 1.12, dust);
|
||||
density = mix(density, halo * 0.96, 0.34);
|
||||
float coreGlow = pow(core, uCoreGlow) * uGlowStrength;
|
||||
float bloomGlow = pow(coreBloom, 1.8) * (uGlowStrength * 0.92);
|
||||
float pulseCore = pow(coreBloom, 1.28) * (0.24 + pulse * 0.34);
|
||||
float innerCloud = pow(core, 1.15) * 0.58 + pow(coreBloom, 1.3) * 0.38;
|
||||
float starDust = smoothstep(0.9, 0.985, highNoise) * softArms * rim * 0.07;
|
||||
|
||||
density += wisps * 0.4 + outerVeil * 0.42 + innerCloud + coreGlow + bloomGlow + starDust;
|
||||
density += armRidge * 0.2 + clumps * 0.24 + edgeBreak * 0.18 + shockRing * 0.28 + armSpark * 0.1 + pulseCore;
|
||||
density += grainMask * 0.04 + microSpeckles * 0.018;
|
||||
density *= 1.0 - dustLane * 0.34;
|
||||
density *= 0.94 + grainNoise * 0.11;
|
||||
density *= 1.0 - dustTexture * 0.1;
|
||||
density *= 1.0 - dustFilaments * 0.26;
|
||||
density *= 1.0 - fineFibers * 0.18;
|
||||
density *= 1.0 - outerSparseFibers * 0.13;
|
||||
density *= 1.0 - deepOcclusion * (0.12 + dustLane * 0.1);
|
||||
density *= 0.9 + (1.0 - mottledDust) * 0.12;
|
||||
density *= exp(-radius * 0.85);
|
||||
|
||||
float rampT = clamp(pow(radius, 0.72) + dustLane * 0.05 - coreBloom * 0.14 - armRidge * 0.03 + (1.0 - softArms) * 0.04, 0.0, 1.0);
|
||||
vec3 rampColor = sampleGalaxyRamp(rampT);
|
||||
vec3 layerTint = mix(uOuterColor, uInnerColor, clamp(core * 0.75 + armRidge * 0.24 + softArms * 0.12, 0.0, 1.0));
|
||||
vec3 color = mix(rampColor, layerTint, 0.25); // 增加 layerTint 的比重,提亮
|
||||
|
||||
// 提亮灰尘的颜色,防止在纯黑背景下太暗
|
||||
vec3 brightDust = mix(uDustColor, vec3(0.3, 0.5, 0.8), 0.3);
|
||||
color = mix(color, brightDust, wisps * 0.12 + dustLane * 0.5 + (1.0 - softArms) * 0.1);
|
||||
color = mix(color, brightDust, dustTexture * 0.15 + dustFilaments * 0.25 + fineFibers * 0.15 + mottledDust * 0.12 + deepOcclusion * 0.1);
|
||||
color = mix(color, uWarmOrange, armRidge * 0.16 + shockRing * 0.1);
|
||||
color = mix(color, uWarmMagenta, smoothstep(0.18, 0.62, radius) * (1.0 - armMask) * 0.24);
|
||||
color = mix(color, uOuterBlue, outerVeil * 0.18 + edgeBreak * 0.12);
|
||||
color = mix(color, mix(uOuterBlue, uDustColor, 0.4), outerSparseFibers * 0.36);
|
||||
color += mix(rampColor, vec3(1.0, 0.98, 0.95), 0.4) * grainMask * 0.018;
|
||||
color += mix(uCoreGold, vec3(1.0, 0.96, 0.86), 0.45) * armRidge * 0.18;
|
||||
color += mix(uOuterBlue, vec3(0.9, 0.97, 1.0), 0.35) * clumps * 0.12;
|
||||
color += mix(uCoreGold, uWarmOrange, 0.45) * shockRing * 0.34;
|
||||
color += mix(uCoreWhite, uOuterBlue, 0.28) * armSpark * 0.12;
|
||||
color += mix(uCoreWhite, uCoreGold, 0.4) * microSpeckles * 0.04;
|
||||
color += uCoreWhite * pow(coreBloom, 1.8) * 0.65;
|
||||
color += mix(uCoreWhite, uCoreGold, 0.22) * pow(hotCore, 1.2) * 1.0;
|
||||
|
||||
float alpha = density * uOpacity * (1.0 - smoothstep(1.25, 1.6, radius));
|
||||
|
||||
if (alpha < 0.01) {
|
||||
discard;
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(color, clamp(alpha, 0.0, 1.0));
|
||||
}
|
||||
`
|
||||
|
||||
@@ -134,6 +361,10 @@ function randomRange(min: number, max: number) {
|
||||
return min + Math.random() * (max - min)
|
||||
}
|
||||
|
||||
function getGalaxyBlending(blendMode: 'normal' | 'additive') {
|
||||
return blendMode === 'additive' ? THREE.AdditiveBlending : THREE.NormalBlending
|
||||
}
|
||||
|
||||
function sampleDisk(radius: number) {
|
||||
const angle = Math.random() * Math.PI * 2
|
||||
const distance = Math.pow(Math.random(), APP_CONFIG.starfield.radialExponent) * radius
|
||||
@@ -236,14 +467,13 @@ export function createTravelScene(container: HTMLElement): TravelSceneController
|
||||
|
||||
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
|
||||
galaxyGroup.rotation.x = APP_CONFIG.galaxy.tiltX
|
||||
galaxyGroup.rotation.y = APP_CONFIG.galaxy.tiltY
|
||||
scene.add(galaxyGroup)
|
||||
|
||||
// Create an inner group for the galaxy rotation to preserve the tilt
|
||||
const galaxyInnerGroup = new THREE.Group()
|
||||
galaxyGroup.add(galaxyInnerGroup)
|
||||
const galaxyGeometry = new THREE.PlaneGeometry(APP_CONFIG.galaxy.width, APP_CONFIG.galaxy.height, 1, 1)
|
||||
const galaxyLayers: THREE.Mesh<THREE.PlaneGeometry, THREE.ShaderMaterial>[] = []
|
||||
const galaxyMaterials: THREE.ShaderMaterial[] = []
|
||||
|
||||
const starsGeometry = new THREE.BufferGeometry()
|
||||
const starPositions = new Float32Array(STAR_COUNT * 3)
|
||||
@@ -264,7 +494,7 @@ export function createTravelScene(container: HTMLElement): TravelSceneController
|
||||
const pointerCurrent = { x: 0, y: 0 }
|
||||
|
||||
let started = false
|
||||
let targetSpeed = 1
|
||||
let targetSpeed: number = APP_CONFIG.animation.defaultSpeed
|
||||
let currentSpeed = 0
|
||||
let animationFrameId = 0
|
||||
let disposed = false
|
||||
@@ -276,6 +506,9 @@ export function createTravelScene(container: HTMLElement): TravelSceneController
|
||||
depthWrite: false,
|
||||
vertexColors: true,
|
||||
blending: THREE.AdditiveBlending,
|
||||
uniforms: {
|
||||
uSpeed: { value: 1.0 },
|
||||
},
|
||||
})
|
||||
|
||||
function spawnStar(index: number, seededProgress?: number) {
|
||||
@@ -314,7 +547,11 @@ export function createTravelScene(container: HTMLElement): TravelSceneController
|
||||
starDirections[index * 2] = direction.x
|
||||
starDirections[index * 2 + 1] = direction.y
|
||||
|
||||
starColor.set(STAR_COLORS[Math.floor(Math.random() * STAR_COLORS.length)])
|
||||
if (Math.random() < 0.9) {
|
||||
starColor.set('#ffffff')
|
||||
} else {
|
||||
starColor.set(STAR_COLORS[Math.floor(Math.random() * STAR_COLORS.length)])
|
||||
}
|
||||
starColors[index * 3] = starColor.r
|
||||
starColors[index * 3 + 1] = starColor.g
|
||||
starColors[index * 3 + 2] = starColor.b
|
||||
@@ -393,61 +630,47 @@ 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)
|
||||
APP_CONFIG.galaxy.layers.forEach((layerConfig, index) => {
|
||||
const galaxyMaterial = new THREE.ShaderMaterial({
|
||||
vertexShader: GALAXY_VERTEX_SHADER,
|
||||
fragmentShader: GALAXY_FRAGMENT_SHADER,
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
side: THREE.DoubleSide,
|
||||
blending: getGalaxyBlending(layerConfig.blendMode),
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uOpacity: { value: layerConfig.opacity },
|
||||
uArms: { value: layerConfig.armCount },
|
||||
uTwist: { value: layerConfig.twist },
|
||||
uArmSharpness: { value: layerConfig.armSharpness },
|
||||
uNoiseScale: { value: layerConfig.noiseScale },
|
||||
uNoiseStrength: { value: layerConfig.noiseStrength },
|
||||
uCoreRadius: { value: layerConfig.coreRadius },
|
||||
uCoreGlow: { value: layerConfig.coreGlow },
|
||||
uGlowStrength: { value: layerConfig.glowStrength },
|
||||
uSeed: { value: index * 0.173 + 0.31 },
|
||||
uCoreWhite: { value: new THREE.Color(APP_CONFIG.galaxy.palette.coreWhite) },
|
||||
uCoreGold: { value: new THREE.Color(APP_CONFIG.galaxy.palette.coreGold) },
|
||||
uWarmOrange: { value: new THREE.Color(APP_CONFIG.galaxy.palette.warmOrange) },
|
||||
uWarmMagenta: { value: new THREE.Color(APP_CONFIG.galaxy.palette.warmMagenta) },
|
||||
uOuterBlue: { value: new THREE.Color(APP_CONFIG.galaxy.palette.outerBlue) },
|
||||
uDeepBlue: { value: new THREE.Color(APP_CONFIG.galaxy.palette.deepBlue) },
|
||||
uInnerColor: { value: new THREE.Color(layerConfig.innerColor) },
|
||||
uOuterColor: { value: new THREE.Color(layerConfig.outerColor) },
|
||||
uDustColor: { value: new THREE.Color(layerConfig.dustColor) },
|
||||
},
|
||||
})
|
||||
|
||||
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 galaxyMesh = new THREE.Mesh(galaxyGeometry, galaxyMaterial)
|
||||
galaxyMesh.scale.setScalar(layerConfig.scale)
|
||||
galaxyMesh.position.z = layerConfig.zOffset
|
||||
galaxyMesh.rotation.z = index * 0.8
|
||||
galaxyGroup.add(galaxyMesh)
|
||||
galaxyLayers.push(galaxyMesh)
|
||||
galaxyMaterials.push(galaxyMaterial)
|
||||
})
|
||||
|
||||
const galaxyPoints = new THREE.Points(galaxyGeometry, galaxyMaterial)
|
||||
galaxyInnerGroup.add(galaxyPoints)
|
||||
|
||||
const clock = new THREE.Clock()
|
||||
|
||||
const onResize = () => {
|
||||
@@ -588,8 +811,15 @@ export function createTravelScene(container: HTMLElement): TravelSceneController
|
||||
const motionDelta = deltaSeconds * currentSpeed
|
||||
updateStars(motionDelta)
|
||||
updateSmoke(motionDelta * 0.62, elapsed)
|
||||
|
||||
starMaterial.uniforms.uSpeed.value = currentSpeed
|
||||
|
||||
galaxyInnerGroup.rotation.z += APP_CONFIG.galaxy.rotationSpeed * currentSpeed
|
||||
galaxyLayers.forEach((galaxyLayer, index) => {
|
||||
galaxyLayer.rotation.z -= APP_CONFIG.galaxy.layers[index].rotationSpeed * currentSpeed
|
||||
})
|
||||
galaxyMaterials.forEach((material, index) => {
|
||||
material.uniforms.uTime.value = elapsed + index * 0.37
|
||||
})
|
||||
|
||||
travelRoot.rotation.z = pointerCurrent.x * 0.03
|
||||
travelRoot.position.x = -pointerCurrent.x * 0.8
|
||||
@@ -631,8 +861,8 @@ export function createTravelScene(container: HTMLElement): TravelSceneController
|
||||
travelRoot.remove(smokeState.mesh)
|
||||
}
|
||||
|
||||
galaxyMaterials.forEach((material) => material.dispose())
|
||||
galaxyGeometry.dispose()
|
||||
galaxyMaterial.dispose()
|
||||
|
||||
starsGeometry.dispose()
|
||||
starMaterial.dispose()
|
||||
|
||||
Reference in New Issue
Block a user