almost final

This commit is contained in:
Anuj K 2025-09-04 09:06:21 +05:30
parent 8d52d89024
commit 6bd4a96e2d
4 changed files with 142 additions and 32 deletions

View file

@ -8,7 +8,7 @@ const FluidSimShader = {
iResolution: { value: new THREE.Vector2() }, iResolution: { value: new THREE.Vector2() },
iTime: { value: 0.0 }, iTime: { value: 0.0 },
mouse: { value: new THREE.Vector3(-1, -1, 0.0) }, mouse: { value: new THREE.Vector3(-1, -1, 0.0) },
dissipation: { value: 0.950 }, // Slightly more persistent for trails dissipation: { value: 0.95 }, // Slightly more persistent for trails
tension: { value: 2.2 }, // Higher tension for stronger ripples tension: { value: 2.2 }, // Higher tension for stronger ripples
radius: { value: 20.0 }, // Larger splat radius radius: { value: 20.0 }, // Larger splat radius
trailLength: { value: 5 }, // Number of trailing ripples trailLength: { value: 5 }, // Number of trailing ripples
@ -80,7 +80,7 @@ const FluidSimShader = {
// Gaussian with sine wave for ripple pattern // Gaussian with sine wave for ripple pattern
float g = exp(-pow(dist - offset, 2.0) / (r * r * 0.5)); float g = exp(-pow(dist - offset, 2.0) / (r * r * 0.5));
float ripple = sin(dist * 0.2 - iTime * 8.0 + timeOffset) * g; float ripple = sin(dist * 0.2 - iTime * 16.0 + timeOffset) * g;
// Diminishing strength for trailing ripples // Diminishing strength for trailing ripples
float strength = mouse.z * (1.0 - i * 0.2) * 0.8; float strength = mouse.z * (1.0 - i * 0.2) * 0.8;

View file

@ -12,7 +12,8 @@ import {
updateTransition, updateTransition,
onMouseScroll, onMouseScroll,
setCurrentModel, setCurrentModel,
setMixer setMixer,
setGLBRepulsionSystem
} from './transitionManager.js'; } from './transitionManager.js';
import { import {
startBoldRoughnessAnimation, startBoldRoughnessAnimation,
@ -48,7 +49,6 @@ let preloadedModels = {};
// Enhanced fluid simulation + distortion pass // Enhanced fluid simulation + distortion pass
const dpr = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2); const dpr = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2);
const fluid = createFluidSimulation(renderer, dpr); const fluid = createFluidSimulation(renderer, dpr);
const distortionPass = new ShaderPass(FluidDistortionShader); const distortionPass = new ShaderPass(FluidDistortionShader);
distortionPass.material.uniforms.tSim.value = fluid.getTexture(); distortionPass.material.uniforms.tSim.value = fluid.getTexture();
distortionPass.material.uniforms.iResolution.value.set(window.innerWidth * dpr, window.innerHeight * dpr); distortionPass.material.uniforms.iResolution.value.set(window.innerWidth * dpr, window.innerHeight * dpr);
@ -64,7 +64,6 @@ distortionPass.material.uniforms.ambientLight.value = 1;
// New ripple whiteness parameters // New ripple whiteness parameters
distortionPass.material.uniforms.rippleWhiteness.value = 0.025; // Amount of white tint distortionPass.material.uniforms.rippleWhiteness.value = 0.025; // Amount of white tint
distortionPass.material.uniforms.rippleBrightness.value = 1; // Brightness boost for ripples distortionPass.material.uniforms.rippleBrightness.value = 1; // Brightness boost for ripples
composer.addPass(distortionPass); composer.addPass(distortionPass);
// Enhanced pointer tracking // Enhanced pointer tracking
@ -78,8 +77,22 @@ const pointer = {
maxTrailLength: 5 maxTrailLength: 5
}; };
// Mouse coordinates for starfield // Mouse coordinates for starfield and GLB interaction
const mouse = new THREE.Vector2(); const mouse = new THREE.Vector2();
const raycaster = new THREE.Raycaster();
// GLB cursor repulsion parameters
const glbRepulsion = {
radius: 30, // Repulsion radius
maxDistance: 2, // Maximum distance GLB can move from original position
strength: 8, // Repulsion strength
originalPositions: new Map(), // Store original positions for each model
currentTargets: new Map(), // Store current target positions
interpolationSpeed: 3 // Speed of position interpolation
};
// Connect GLB repulsion system to transition manager
setGLBRepulsionSystem(glbRepulsion);
function toSimPixels(e) { function toSimPixels(e) {
const rect = renderer.domElement.getBoundingClientRect(); const rect = renderer.domElement.getBoundingClientRect();
@ -93,7 +106,6 @@ renderer.domElement.addEventListener('pointermove', (e) => {
const dx = (pointer.prevX < 0) ? 0 : Math.abs(x - pointer.prevX); const dx = (pointer.prevX < 0) ? 0 : Math.abs(x - pointer.prevX);
const dy = (pointer.prevY < 0) ? 0 : Math.abs(y - pointer.prevY); const dy = (pointer.prevY < 0) ? 0 : Math.abs(y - pointer.prevY);
const speed = Math.min(Math.sqrt(dx * dx + dy * dy) / (6.0 * dpr), 1.0); // More sensitive const speed = Math.min(Math.sqrt(dx * dx + dy * dy) / (6.0 * dpr), 1.0); // More sensitive
pointer.x = x; pointer.x = x;
pointer.y = y; pointer.y = y;
pointer.strength = speed * 1.2; // Enhanced strength pointer.strength = speed * 1.2; // Enhanced strength
@ -106,7 +118,7 @@ renderer.domElement.addEventListener('pointermove', (e) => {
const normalizedY = 1.0 - (e.clientY - rect.top) / rect.height; // Flip Y const normalizedY = 1.0 - (e.clientY - rect.top) / rect.height; // Flip Y
distortionPass.material.uniforms.lightPosition.value.set(normalizedX, normalizedY, 1.0); distortionPass.material.uniforms.lightPosition.value.set(normalizedX, normalizedY, 1.0);
// Update mouse coordinates for starfield // Update mouse coordinates for starfield and GLB interaction
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
}, { passive: true }); }, { passive: true });
@ -117,11 +129,119 @@ renderer.domElement.addEventListener('pointerleave', () => {
pointer.strength = 0.0; pointer.strength = 0.0;
mouse.x = -999; mouse.x = -999;
mouse.y = -999; mouse.y = -999;
// Reset light to center when mouse leaves // Reset light to center when mouse leaves
distortionPass.material.uniforms.lightPosition.value.set(0.5, 0.5, 1.0); distortionPass.material.uniforms.lightPosition.value.set(0.5, 0.5, 1.0);
}, { passive: true }); }, { passive: true });
// GLB cursor repulsion function
function updateGLBRepulsion(camera, mouse, deltaTime) {
if (!mouse || mouse.x === -999 || mouse.y === -999) {
// Mouse is not active, return models to original positions
if (currentModel) {
const originalPos = glbRepulsion.originalPositions.get(currentModel);
if (originalPos) {
const currentTarget = glbRepulsion.currentTargets.get(currentModel) || new THREE.Vector3();
currentTarget.copy(originalPos);
glbRepulsion.currentTargets.set(currentModel, currentTarget);
// Interpolate to original position
const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0);
currentModel.position.lerp(currentTarget, lerpFactor);
}
}
if (nextModel) {
const originalPos = glbRepulsion.originalPositions.get(nextModel);
if (originalPos) {
const currentTarget = glbRepulsion.currentTargets.get(nextModel) || new THREE.Vector3();
currentTarget.copy(originalPos);
glbRepulsion.currentTargets.set(nextModel, currentTarget);
// Interpolate to original position
const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0);
nextModel.position.lerp(currentTarget, lerpFactor);
}
}
return;
}
// Get mouse position in 3D world space
raycaster.setFromCamera(mouse, camera);
const distance = 50; // Project to a plane at fixed distance
const mouseWorldPos = raycaster.ray.direction.clone().multiplyScalar(distance).add(raycaster.ray.origin);
// Handle current model repulsion
if (currentModel) {
let originalPos = glbRepulsion.originalPositions.get(currentModel);
if (!originalPos) {
originalPos = currentModel.position.clone();
glbRepulsion.originalPositions.set(currentModel, originalPos);
}
// Calculate repulsion
const modelPos = currentModel.position.clone();
const dx = modelPos.x - mouseWorldPos.x;
const dy = modelPos.y - mouseWorldPos.y;
const dz = modelPos.z - mouseWorldPos.z;
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
let targetPos = originalPos.clone();
if (dist < glbRepulsion.radius && dist > 0) {
const force = (1 - dist / glbRepulsion.radius) * glbRepulsion.strength;
const nx = dx / dist;
const ny = dy / dist;
const nz = dz / dist;
const repulsionVector = new THREE.Vector3(nx * force, ny * force, nz * force);
targetPos.add(repulsionVector);
// Limit maximum distance from original position
const offsetFromOriginal = targetPos.clone().sub(originalPos);
if (offsetFromOriginal.length() > glbRepulsion.maxDistance) {
offsetFromOriginal.normalize().multiplyScalar(glbRepulsion.maxDistance);
targetPos = originalPos.clone().add(offsetFromOriginal);
}
}
// Store and interpolate to target position
glbRepulsion.currentTargets.set(currentModel, targetPos);
const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0);
currentModel.position.lerp(targetPos, lerpFactor);
}
// Handle next model repulsion during transitions
if (nextModel) {
let originalPos = glbRepulsion.originalPositions.get(nextModel);
if (!originalPos) {
originalPos = nextModel.position.clone();
glbRepulsion.originalPositions.set(nextModel, originalPos);
}
// Calculate repulsion
const modelPos = nextModel.position.clone();
const dx = modelPos.x - mouseWorldPos.x;
const dy = modelPos.y - mouseWorldPos.y;
const dz = modelPos.z - mouseWorldPos.z;
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
let targetPos = originalPos.clone();
if (dist < glbRepulsion.radius && dist > 0) {
const force = (1 - dist / glbRepulsion.radius) * glbRepulsion.strength;
const nx = dx / dist;
const ny = dy / dist;
const nz = dz / dist;
const repulsionVector = new THREE.Vector3(nx * force, ny * force, nz * force);
targetPos.add(repulsionVector);
// Limit maximum distance from original position
const offsetFromOriginal = targetPos.clone().sub(originalPos);
if (offsetFromOriginal.length() > glbRepulsion.maxDistance) {
offsetFromOriginal.normalize().multiplyScalar(glbRepulsion.maxDistance);
targetPos = originalPos.clone().add(offsetFromOriginal);
}
}
// Store and interpolate to target position
glbRepulsion.currentTargets.set(nextModel, targetPos);
const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0);
nextModel.position.lerp(targetPos, lerpFactor);
}
}
// Initialize first scene // Initialize first scene
function initializeScene() { function initializeScene() {
console.log('Initializing first scene (bold)'); console.log('Initializing first scene (bold)');
@ -129,13 +249,14 @@ function initializeScene() {
setCurrentModel(model); setCurrentModel(model);
setMixer(animMixer); setMixer(animMixer);
scene.add(currentModel); scene.add(currentModel);
// Store original position for repulsion
glbRepulsion.originalPositions.set(currentModel, currentModel.position.clone());
startBoldRoughnessAnimation(true); startBoldRoughnessAnimation(true);
console.log('Bold scene initialized'); console.log('Bold scene initialized');
} }
// Animation loop // Animation loop
const clock = new THREE.Clock(); const clock = new THREE.Clock();
function animate() { function animate() {
requestAnimationFrame(animate); requestAnimationFrame(animate);
const delta = clock.getDelta(); const delta = clock.getDelta();
@ -149,6 +270,11 @@ function animate() {
updateTransition(delta, scene); updateTransition(delta, scene);
} }
// GLB cursor repulsion (only when not transitioning to avoid conflicts)
if (!isTransitioning) {
updateGLBRepulsion(camera, mouse, delta);
}
// Turntable rotation // Turntable rotation
if (currentModel) { if (currentModel) {
currentModel.rotation.y += turntableSpeed * delta; currentModel.rotation.y += turntableSpeed * delta;
@ -179,16 +305,13 @@ async function init() {
console.log('Starting application initialization'); console.log('Starting application initialization');
preloadedModels = await sceneLoader.loadAllModels(); preloadedModels = await sceneLoader.loadAllModels();
console.log('All models loaded successfully'); console.log('All models loaded successfully');
initializeScene(); initializeScene();
animate(); animate();
console.log('Animation loop started'); console.log('Animation loop started');
window.addEventListener('wheel', (event) => { window.addEventListener('wheel', (event) => {
onMouseScroll(event, preloadedModels, scene, camera, controls); onMouseScroll(event, preloadedModels, scene, camera, controls);
}, { passive: true }); }, { passive: true });
console.log('Scroll event listener attached'); console.log('Scroll event listener attached');
} catch (error) { } catch (error) {
console.error('Failed to initialize scene:', error); console.error('Failed to initialize scene:', error);
sceneLoader.setLoadingMessage('Error loading experience. Please refresh.'); sceneLoader.setLoadingMessage('Error loading experience. Please refresh.');
@ -200,12 +323,10 @@ window.addEventListener('resize', () => {
console.log('Window resized'); console.log('Window resized');
const w = window.innerWidth; const w = window.innerWidth;
const h = window.innerHeight; const h = window.innerHeight;
camera.aspect = w / h; camera.aspect = w / h;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
renderer.setSize(w, h); renderer.setSize(w, h);
composer.setSize(w, h); composer.setSize(w, h);
const pixelRatio = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2); const pixelRatio = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2);
distortionPass.material.uniforms.iResolution.value.set(w * pixelRatio, h * pixelRatio); distortionPass.material.uniforms.iResolution.value.set(w * pixelRatio, h * pixelRatio);
fluid.resize(w, h, pixelRatio); fluid.resize(w, h, pixelRatio);

View file

@ -30,7 +30,6 @@ export function createScene() {
const composer = new EffectComposer(renderer); const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera); const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass); composer.addPass(renderPass);
const bloomPass = new UnrealBloomPass( const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight), new THREE.Vector2(window.innerWidth, window.innerHeight),
1.0, // strength 1.0, // strength
@ -54,55 +53,45 @@ export function setupLighting(scene, camera) {
// Consistent Lighting Setup // Consistent Lighting Setup
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight); scene.add(ambientLight);
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x666666, 1.5); const hemiLight = new THREE.HemisphereLight(0xffffff, 0x666666, 1.5);
hemiLight.position.set(0, 20, 0); hemiLight.position.set(0, 20, 0);
scene.add(hemiLight); scene.add(hemiLight);
const fillLight = new THREE.DirectionalLight(0xffffff, 1.2); const fillLight = new THREE.DirectionalLight(0xffffff, 1.2);
fillLight.position.set(-12, 6, -8); fillLight.position.set(-12, 6, -8);
scene.add(fillLight); scene.add(fillLight);
const topLight = new THREE.DirectionalLight(0xffffff, 1.5); const topLight = new THREE.DirectionalLight(0xffffff, 1.5);
topLight.position.set(5, 15, 5); topLight.position.set(5, 15, 5);
scene.add(topLight); scene.add(topLight);
const bottomLight = new THREE.DirectionalLight(0xffffff, 0.8); const bottomLight = new THREE.DirectionalLight(0xffffff, 0.8);
bottomLight.position.set(-3, -8, 3); bottomLight.position.set(-3, -8, 3);
scene.add(bottomLight); scene.add(bottomLight);
const leftLight = new THREE.DirectionalLight(0xffffff, 1.0); const leftLight = new THREE.DirectionalLight(0xffffff, 1.0);
leftLight.position.set(-12, 2, 5); leftLight.position.set(-12, 2, 5);
scene.add(leftLight); scene.add(leftLight);
const rightLight = new THREE.DirectionalLight(0xffffff, 1.0); const rightLight = new THREE.DirectionalLight(0xffffff, 1.0);
rightLight.position.set(12, 2, -5); rightLight.position.set(12, 2, -5);
scene.add(rightLight); scene.add(rightLight);
const frontLight = new THREE.DirectionalLight(0xffffff, 0.8); const frontLight = new THREE.DirectionalLight(0xffffff, 0.8);
frontLight.position.set(8, 4, 12); frontLight.position.set(8, 4, 12);
scene.add(frontLight); scene.add(frontLight);
const backLight = new THREE.DirectionalLight(0xffffff, 0.8); const backLight = new THREE.DirectionalLight(0xffffff, 0.8);
backLight.position.set(-8, 4, -12); backLight.position.set(-8, 4, -12);
scene.add(backLight); scene.add(backLight);
const cameraLight = new THREE.PointLight(0xffffff, 0.8, 0, 2); const cameraLight = new THREE.PointLight(0xffffff, 0.8, 0, 2);
camera.add(cameraLight); camera.add(cameraLight);
scene.add(camera); scene.add(camera);
} }
export function setupControls(camera, renderer) { export function setupControls(camera, renderer) {
// Controls with zoom disabled and camera constraints // Controls with zoom and pan disabled and camera constraints
const controls = new OrbitControls(camera, renderer.domElement); const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; controls.enableDamping = true;
controls.dampingFactor = 0.25; controls.dampingFactor = 0.25;
controls.enableZoom = false; // Disable zoom controls.enableZoom = false; // Disable zoom
controls.enablePan = false; // Disable panning
// Add camera constraints to prevent extreme angles // Add camera constraints to prevent extreme angles
controls.maxPolarAngle = Math.PI * 0.8; // Prevent looking too far up controls.maxPolarAngle = Math.PI * 0.8; // Prevent looking too far up
controls.minPolarAngle = Math.PI * 0.2; // Prevent looking too far down controls.minPolarAngle = Math.PI * 0.2; // Prevent looking too far down
console.log('Orbit controls initialized with camera constraints and pan disabled');
console.log('Orbit controls initialized with camera constraints');
return controls; return controls;
} }

View file

@ -1,7 +1,7 @@
import * as THREE from 'three'; import * as THREE from 'three';
export function createStarfield(scene) { export function createStarfield(scene) {
const starCount = 12000; const starCount = 16000;
const starDistance = 300; const starDistance = 300;
// Create geometry for stars // Create geometry for stars