Skip to content

Commit 460658f

Browse files
author
b
committed
tune: depth-scale circuit lines and synchronize pulse flow
1 parent 37bf85e commit 460658f

File tree

1 file changed

+95
-35
lines changed

1 file changed

+95
-35
lines changed

src/components/CircuitBackground.vue

Lines changed: 95 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@ interface PulseMarker {
1818
core: THREE.Mesh;
1919
aura: THREE.Mesh;
2020
phase: number;
21-
speed: number;
2221
}
2322
2423
interface CircuitLine {
25-
line: THREE.Line;
26-
material: THREE.LineDashedMaterial;
24+
group: THREE.Group;
25+
segments: THREE.Mesh[];
26+
material: THREE.MeshBasicMaterial;
27+
baseColor: THREE.Color;
2728
sampler: PathSampler;
2829
pulses: PulseMarker[];
29-
dashSpeed: number;
30+
speedFactor: number;
31+
depthOffset: number;
32+
baseThickness: number;
3033
}
3134
3235
let scene: THREE.Scene | null = null;
@@ -41,9 +44,18 @@ const circuits: CircuitLine[] = [];
4144
const pointerParallax = new THREE.Vector2(0, 0);
4245
const pointerTarget = new THREE.Vector2(0, 0);
4346
const clock = new THREE.Clock();
44-
const FLOW_SPEED_START = 1.45;
47+
const FLOW_SPEED_START = 11.5;
4548
const FLOW_ACCEL_PER_SECOND = 0.018;
46-
const FLOW_SPEED_MAX = 2.35;
49+
const FLOW_SPEED_MAX = 15.5;
50+
const FLOW_DEPTH_SPAN = 320;
51+
const FLOW_NEAR_LIMIT = 48;
52+
const PULSE_SPEED_BASE = 0.032;
53+
const PULSE_SPEED_FLOW_GAIN = 0.0022;
54+
55+
const SEGMENT_AXIS = new THREE.Vector3(1, 0, 0);
56+
const cameraToGroup = new THREE.Vector3();
57+
const colorNear = new THREE.Color(0xc8ffff);
58+
let flowTravel = 0;
4759
4860
function rand(min: number, max: number) {
4961
return min + Math.random() * (max - min);
@@ -142,6 +154,19 @@ function samplePath(sampler: PathSampler, t: number) {
142154
return sampler.points[sampler.points.length - 1].clone();
143155
}
144156
157+
function createWireSegment(start: THREE.Vector3, end: THREE.Vector3, material: THREE.MeshBasicMaterial, baseThickness: number) {
158+
const direction = end.clone().sub(start);
159+
const length = direction.length();
160+
if (length <= 0.001) return null;
161+
162+
const geometry = new THREE.BoxGeometry(length, 1, 1);
163+
const mesh = new THREE.Mesh(geometry, material);
164+
mesh.position.copy(start).add(end).multiplyScalar(0.5);
165+
mesh.quaternion.setFromUnitVectors(SEGMENT_AXIS, direction.normalize());
166+
mesh.scale.set(1, baseThickness, baseThickness);
167+
return mesh;
168+
}
169+
145170
function addCenterHole() {
146171
if (!scene) return;
147172
@@ -179,26 +204,31 @@ function addCenterHole() {
179204
function createCircuitFlow() {
180205
if (!scene) return;
181206
182-
const wireColor = new THREE.Color(0x45c8ff);
183-
184-
for (let i = 0; i < 40; i += 1) {
207+
for (let i = 0; i < 42; i += 1) {
185208
const pathPoints = createOrthogonalPath();
186-
const geometry = new THREE.BufferGeometry().setFromPoints(pathPoints);
187-
const material = new THREE.LineDashedMaterial({
188-
color: wireColor,
209+
const sampler = createSampler(pathPoints);
210+
const group = new THREE.Group();
211+
const hue = rand(0.51, 0.57);
212+
const baseColor = new THREE.Color().setHSL(hue, rand(0.78, 0.94), rand(0.5, 0.62));
213+
const material = new THREE.MeshBasicMaterial({
214+
color: baseColor,
189215
transparent: true,
190-
opacity: rand(0.22, 0.5),
191-
dashSize: rand(1.4, 3.8),
192-
gapSize: rand(0.8, 2.3),
216+
opacity: 0.34,
217+
depthWrite: false,
218+
blending: THREE.AdditiveBlending,
193219
});
194220
195-
const line = new THREE.Line(geometry, material);
196-
line.computeLineDistances();
197-
scene.add(line);
221+
const baseThickness = rand(0.2, 0.36);
222+
const segments: THREE.Mesh[] = [];
223+
for (let j = 1; j < pathPoints.length; j += 1) {
224+
const segment = createWireSegment(pathPoints[j - 1], pathPoints[j], material, baseThickness);
225+
if (!segment) continue;
226+
group.add(segment);
227+
segments.push(segment);
228+
}
198229
199-
const sampler = createSampler(pathPoints);
200230
const pulses: PulseMarker[] = [];
201-
const pulseCount = 3;
231+
const pulseCount = 2 + Math.floor(Math.random() * 2);
202232
203233
for (let j = 0; j < pulseCount; j += 1) {
204234
const core = new THREE.Mesh(
@@ -213,7 +243,7 @@ function createCircuitFlow() {
213243
);
214244
215245
const aura = new THREE.Mesh(
216-
new THREE.SphereGeometry(rand(0.55, 0.92), 14, 14),
246+
new THREE.SphereGeometry(rand(0.55, 1.0), 14, 14),
217247
new THREE.MeshBasicMaterial({
218248
color: 0x34b6ff,
219249
transparent: true,
@@ -223,22 +253,29 @@ function createCircuitFlow() {
223253
}),
224254
);
225255
226-
scene.add(core);
227-
scene.add(aura);
256+
group.add(core);
257+
group.add(aura);
228258
pulses.push({
229259
core,
230260
aura,
231261
phase: j / pulseCount + Math.random() * 0.12,
232-
speed: rand(0.07, 0.17),
233262
});
234263
}
235264
265+
const depthOffset = rand(0, FLOW_DEPTH_SPAN);
266+
group.position.z = depthOffset - FLOW_DEPTH_SPAN + FLOW_NEAR_LIMIT;
267+
scene.add(group);
268+
236269
circuits.push({
237-
line,
270+
group,
271+
segments,
238272
material,
273+
baseColor,
239274
sampler,
240275
pulses,
241-
dashSpeed: rand(0.22, 0.65),
276+
speedFactor: rand(0.94, 1.08),
277+
depthOffset,
278+
baseThickness,
242279
});
243280
}
244281
@@ -277,19 +314,39 @@ function animate() {
277314
278315
const delta = clock.getDelta();
279316
const elapsed = clock.elapsedTime;
280-
const flowMultiplier = Math.min(FLOW_SPEED_MAX, FLOW_SPEED_START + elapsed * FLOW_ACCEL_PER_SECOND);
317+
const flowSpeed = Math.min(FLOW_SPEED_MAX, FLOW_SPEED_START + elapsed * FLOW_ACCEL_PER_SECOND);
318+
flowTravel += delta * flowSpeed;
281319
282320
pointerParallax.lerp(pointerTarget, 0.035);
283321
camera.position.x = pointerParallax.x * 2.4;
284322
camera.position.y = pointerParallax.y * 1.8;
285-
camera.position.z = 72 + Math.sin(elapsed * 0.25) * 1.8;
323+
camera.position.z = 72;
286324
camera.lookAt(0, 0, 0);
287325
288326
if (BACKGROUND_MODE === 'circuit') {
289327
for (const circuit of circuits) {
290-
circuit.material.dashOffset -= delta * circuit.dashSpeed * flowMultiplier;
328+
const wrappedDepth = (circuit.depthOffset + flowTravel * circuit.speedFactor) % FLOW_DEPTH_SPAN;
329+
circuit.group.position.z = wrappedDepth - FLOW_DEPTH_SPAN + FLOW_NEAR_LIMIT;
330+
circuit.group.updateMatrixWorld();
331+
cameraToGroup.setFromMatrixPosition(circuit.group.matrixWorld);
332+
const distanceToCamera = camera.position.distanceTo(cameraToGroup);
333+
const proximity = THREE.MathUtils.clamp(1 - distanceToCamera / 250, 0, 1);
334+
const thickness = circuit.baseThickness * (0.62 + proximity * 2.15);
335+
336+
for (const segment of circuit.segments) {
337+
segment.scale.y = thickness;
338+
segment.scale.z = thickness;
339+
}
340+
circuit.material.opacity = 0.15 + proximity * 0.7;
341+
circuit.material.color.copy(circuit.baseColor).lerp(colorNear, proximity * 0.75);
342+
343+
const linePulseSpeed = (PULSE_SPEED_BASE + flowSpeed * PULSE_SPEED_FLOW_GAIN) * circuit.speedFactor;
291344
for (const pulse of circuit.pulses) {
292-
const progress = (elapsed * pulse.speed * flowMultiplier + pulse.phase) % 1;
345+
let progress = (elapsed * linePulseSpeed + pulse.phase) % 1;
346+
if (proximity > 0.58) {
347+
const jumpStep = THREE.MathUtils.lerp(0.028, 0.11, (proximity - 0.58) / 0.42);
348+
progress = Math.round(progress / jumpStep) * jumpStep;
349+
}
293350
const flowT = Math.pow(progress, 0.82);
294351
const position = samplePath(circuit.sampler, flowT);
295352
pulse.core.position.copy(position);
@@ -299,10 +356,10 @@ function animate() {
299356
const pulseWave = 0.88 + Math.sin((elapsed + pulse.phase) * 9) * 0.12;
300357
const coreMaterial = pulse.core.material as THREE.MeshBasicMaterial;
301358
const auraMaterial = pulse.aura.material as THREE.MeshBasicMaterial;
302-
coreMaterial.opacity = 0.12 + distanceFade * 0.9;
303-
auraMaterial.opacity = 0.08 + distanceFade * 0.28;
304-
pulse.core.scale.setScalar(pulseWave);
305-
pulse.aura.scale.setScalar(0.9 + pulseWave * 0.28);
359+
coreMaterial.opacity = 0.08 + proximity * 0.42 + distanceFade * 0.46;
360+
auraMaterial.opacity = 0.04 + proximity * 0.19 + distanceFade * 0.16;
361+
pulse.core.scale.setScalar((0.76 + proximity * 0.36) * pulseWave);
362+
pulse.aura.scale.setScalar((0.84 + proximity * 0.38) + pulseWave * 0.22);
306363
}
307364
}
308365
@@ -343,6 +400,7 @@ function handleResize() {
343400
function initScene() {
344401
if (!canvasRef.value) return;
345402
403+
flowTravel = 0;
346404
scene = new THREE.Scene();
347405
scene.fog = new THREE.FogExp2(0x05070b, 0.01);
348406
@@ -381,7 +439,9 @@ function disposeScene() {
381439
window.removeEventListener('mousemove', handleMouseMove);
382440
383441
for (const circuit of circuits) {
384-
circuit.line.geometry.dispose();
442+
for (const segment of circuit.segments) {
443+
segment.geometry.dispose();
444+
}
385445
circuit.material.dispose();
386446
for (const pulse of circuit.pulses) {
387447
pulse.core.geometry.dispose();

0 commit comments

Comments
 (0)