349 lines
12 KiB
HTML
349 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Three.js Visualization 5: Geometry Morphing</title>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 100%);
|
|
color: #ffffff;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
#container {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
position: relative;
|
|
}
|
|
|
|
#info {
|
|
position: absolute;
|
|
top: 20px;
|
|
left: 20px;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
max-width: 350px;
|
|
z-index: 100;
|
|
}
|
|
|
|
#info h1 {
|
|
margin: 0 0 15px 0;
|
|
font-size: 24px;
|
|
color: #00d9ff;
|
|
text-shadow: 0 0 10px rgba(0, 217, 255, 0.5);
|
|
}
|
|
|
|
#info p {
|
|
margin: 8px 0;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
#info strong {
|
|
color: #00d9ff;
|
|
}
|
|
|
|
canvas {
|
|
display: block;
|
|
}
|
|
|
|
footer {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
left: 20px;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
padding: 15px;
|
|
border-radius: 10px;
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
max-width: 400px;
|
|
font-size: 12px;
|
|
z-index: 100;
|
|
}
|
|
|
|
footer h3 {
|
|
margin: 0 0 10px 0;
|
|
font-size: 16px;
|
|
color: #00d9ff;
|
|
}
|
|
|
|
footer ul {
|
|
margin: 0;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
footer li {
|
|
margin: 5px 0;
|
|
line-height: 1.4;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="container"></div>
|
|
|
|
<div id="info">
|
|
<h1>Geometry Morphing</h1>
|
|
<p><strong>Technique:</strong> Dynamic geometry transformation and scaling</p>
|
|
<p><strong>Learning:</strong> Geometry properties, scale/rotation/position animation with easing</p>
|
|
<p><strong>Web Source:</strong> Three.js Animation Examples</p>
|
|
</div>
|
|
|
|
<footer>
|
|
<h3>Iteration 5 - Foundation Level</h3>
|
|
<ul>
|
|
<li><strong>Focus:</strong> Geometry transformation and morphing</li>
|
|
<li><strong>Technique:</strong> Smooth easing functions with synchronized transformations</li>
|
|
<li><strong>Enhancement:</strong> Organic motion through combined scale, rotation, and material changes</li>
|
|
</ul>
|
|
</footer>
|
|
|
|
<script>
|
|
// Scene setup
|
|
const scene = new THREE.Scene();
|
|
scene.fog = new THREE.Fog(0x0a0a0a, 10, 50);
|
|
|
|
const camera = new THREE.PerspectiveCamera(
|
|
75,
|
|
window.innerWidth / window.innerHeight,
|
|
0.1,
|
|
1000
|
|
);
|
|
camera.position.z = 25;
|
|
|
|
const renderer = new THREE.WebGLRenderer({
|
|
antialias: true,
|
|
alpha: true
|
|
});
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
document.getElementById('container').appendChild(renderer.domElement);
|
|
|
|
// Lighting
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
|
|
scene.add(ambientLight);
|
|
|
|
const pointLight1 = new THREE.PointLight(0x00d9ff, 1.5, 100);
|
|
pointLight1.position.set(10, 10, 10);
|
|
scene.add(pointLight1);
|
|
|
|
const pointLight2 = new THREE.PointLight(0xff00d9, 1.5, 100);
|
|
pointLight2.position.set(-10, -10, 10);
|
|
scene.add(pointLight2);
|
|
|
|
// Create morphing geometries
|
|
const morphingObjects = [];
|
|
|
|
// Central morphing sphere that transitions between shapes
|
|
const sphereGeometry = new THREE.SphereGeometry(3, 32, 32);
|
|
const sphereMaterial = new THREE.MeshPhongMaterial({
|
|
color: 0x00d9ff,
|
|
emissive: 0x001a2e,
|
|
shininess: 100,
|
|
transparent: true,
|
|
opacity: 0.9
|
|
});
|
|
const centralSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
|
|
scene.add(centralSphere);
|
|
|
|
morphingObjects.push({
|
|
mesh: centralSphere,
|
|
baseScale: 1,
|
|
phaseOffset: 0,
|
|
rotationSpeed: { x: 0.002, y: 0.003, z: 0.001 }
|
|
});
|
|
|
|
// Create orbiting morphing cubes
|
|
const cubeCount = 8;
|
|
for (let i = 0; i < cubeCount; i++) {
|
|
const angle = (i / cubeCount) * Math.PI * 2;
|
|
const radius = 12;
|
|
|
|
const geometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);
|
|
const material = new THREE.MeshPhongMaterial({
|
|
color: new THREE.Color().setHSL(i / cubeCount, 1, 0.5),
|
|
emissive: new THREE.Color().setHSL(i / cubeCount, 1, 0.2),
|
|
shininess: 80,
|
|
transparent: true,
|
|
opacity: 0.8
|
|
});
|
|
|
|
const cube = new THREE.Mesh(geometry, material);
|
|
cube.position.x = Math.cos(angle) * radius;
|
|
cube.position.y = Math.sin(angle) * radius;
|
|
cube.position.z = Math.sin(angle * 2) * 3;
|
|
|
|
scene.add(cube);
|
|
|
|
morphingObjects.push({
|
|
mesh: cube,
|
|
baseScale: 1,
|
|
angle: angle,
|
|
radius: radius,
|
|
orbitSpeed: 0.3,
|
|
phaseOffset: i * Math.PI / 4,
|
|
rotationSpeed: {
|
|
x: 0.01 + i * 0.002,
|
|
y: 0.015 + i * 0.001,
|
|
z: 0.008
|
|
}
|
|
});
|
|
}
|
|
|
|
// Create floating tetrahedrons with wave motion
|
|
const tetraCount = 12;
|
|
for (let i = 0; i < tetraCount; i++) {
|
|
const geometry = new THREE.TetrahedronGeometry(0.8, 0);
|
|
const material = new THREE.MeshPhongMaterial({
|
|
color: new THREE.Color().setHSL(0.6 + i * 0.03, 0.8, 0.6),
|
|
emissive: new THREE.Color().setHSL(0.6 + i * 0.03, 0.8, 0.3),
|
|
shininess: 60,
|
|
transparent: true,
|
|
opacity: 0.7,
|
|
wireframe: i % 2 === 0
|
|
});
|
|
|
|
const tetra = new THREE.Mesh(geometry, material);
|
|
tetra.position.x = (Math.random() - 0.5) * 30;
|
|
tetra.position.y = (Math.random() - 0.5) * 30;
|
|
tetra.position.z = (Math.random() - 0.5) * 20;
|
|
|
|
scene.add(tetra);
|
|
|
|
morphingObjects.push({
|
|
mesh: tetra,
|
|
baseScale: 1,
|
|
phaseOffset: Math.random() * Math.PI * 2,
|
|
waveSpeed: 0.5 + Math.random() * 0.5,
|
|
waveAmplitude: 0.5 + Math.random() * 0.5,
|
|
rotationSpeed: {
|
|
x: 0.02,
|
|
y: 0.03,
|
|
z: 0.01
|
|
}
|
|
});
|
|
}
|
|
|
|
// Animation variables
|
|
let time = 0;
|
|
|
|
// Easing functions
|
|
function easeInOutSine(t) {
|
|
return -(Math.cos(Math.PI * t) - 1) / 2;
|
|
}
|
|
|
|
function easeInOutQuad(t) {
|
|
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
|
|
}
|
|
|
|
function elasticPulse(t) {
|
|
return Math.sin(t * Math.PI * 2) * Math.exp(-t * 0.5);
|
|
}
|
|
|
|
// Animation loop
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
time += 0.016; // Approximate 60fps
|
|
|
|
// Animate each morphing object
|
|
morphingObjects.forEach((obj, index) => {
|
|
const mesh = obj.mesh;
|
|
|
|
// Apply rotations
|
|
mesh.rotation.x += obj.rotationSpeed.x;
|
|
mesh.rotation.y += obj.rotationSpeed.y;
|
|
mesh.rotation.z += obj.rotationSpeed.z;
|
|
|
|
// Calculate phase with offset
|
|
const phase = time * 0.5 + obj.phaseOffset;
|
|
|
|
// Pulsing scale with sine wave
|
|
const pulseScale = 1 + Math.sin(phase * 2) * 0.3;
|
|
|
|
// Secondary breathing effect
|
|
const breathScale = 1 + Math.sin(phase * 0.8) * 0.15;
|
|
|
|
// Combined scale with easing
|
|
const finalScale = obj.baseScale * pulseScale * breathScale;
|
|
mesh.scale.set(finalScale, finalScale, finalScale);
|
|
|
|
// Update material opacity with wave
|
|
if (mesh.material.transparent) {
|
|
const opacityWave = 0.5 + Math.sin(phase * 1.5) * 0.3;
|
|
mesh.material.opacity = Math.max(0.4, Math.min(1.0, opacityWave));
|
|
}
|
|
|
|
// Orbital motion for cubes
|
|
if (obj.angle !== undefined) {
|
|
const orbitPhase = time * obj.orbitSpeed + obj.angle;
|
|
mesh.position.x = Math.cos(orbitPhase) * obj.radius;
|
|
mesh.position.y = Math.sin(orbitPhase) * obj.radius;
|
|
mesh.position.z = Math.sin(orbitPhase * 2) * 3 + Math.cos(orbitPhase * 3) * 2;
|
|
}
|
|
|
|
// Wave motion for tetrahedrons
|
|
if (obj.waveSpeed !== undefined) {
|
|
const wavePhase = time * obj.waveSpeed + obj.phaseOffset;
|
|
const waveY = Math.sin(wavePhase) * obj.waveAmplitude * 5;
|
|
const waveZ = Math.cos(wavePhase * 1.3) * obj.waveAmplitude * 3;
|
|
mesh.position.y += waveY * 0.05;
|
|
mesh.position.z += waveZ * 0.05;
|
|
}
|
|
|
|
// Color shifting for central sphere
|
|
if (index === 0) {
|
|
const hue = (time * 0.1) % 1;
|
|
mesh.material.color.setHSL(hue, 0.8, 0.5);
|
|
mesh.material.emissive.setHSL(hue, 0.8, 0.2);
|
|
|
|
// Complex morphing: combine multiple sine waves
|
|
const morph1 = Math.sin(time * 0.7) * 0.4;
|
|
const morph2 = Math.cos(time * 1.3) * 0.3;
|
|
const morph3 = Math.sin(time * 0.5) * 0.2;
|
|
const morphScale = 1 + morph1 + morph2 + morph3;
|
|
|
|
mesh.scale.set(
|
|
morphScale,
|
|
morphScale * (1 + Math.sin(time * 1.1) * 0.2),
|
|
morphScale * (1 + Math.cos(time * 0.9) * 0.2)
|
|
);
|
|
}
|
|
});
|
|
|
|
// Animate lights for dynamic atmosphere
|
|
pointLight1.position.x = Math.cos(time * 0.5) * 15;
|
|
pointLight1.position.y = Math.sin(time * 0.5) * 15;
|
|
pointLight1.intensity = 1.5 + Math.sin(time * 2) * 0.5;
|
|
|
|
pointLight2.position.x = Math.cos(time * 0.3 + Math.PI) * 15;
|
|
pointLight2.position.y = Math.sin(time * 0.3 + Math.PI) * 15;
|
|
pointLight2.intensity = 1.5 + Math.cos(time * 1.5) * 0.5;
|
|
|
|
// Subtle camera movement for immersion
|
|
camera.position.x = Math.sin(time * 0.1) * 2;
|
|
camera.position.y = Math.cos(time * 0.15) * 1.5;
|
|
camera.lookAt(scene.position);
|
|
|
|
renderer.render(scene, camera);
|
|
}
|
|
|
|
// Handle window resize
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
|
|
// Start animation
|
|
animate();
|
|
</script>
|
|
</body>
|
|
</html>
|