<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Morfing de Poliedros Platónicos a Esfera - WebGL</title>
<style>
body { margin: 0; overflow: hidden; background: #000; }
canvas { display: block; }
#info {
position: absolute; top: 10px; left: 10px; color: white;
font-family: Arial, sans-serif; background: rgba(0,0,0,0.5);
padding: 10px; border-radius: 5px;
}
</style>
</head>
<body>
<div id="info">Tetraedro → Cubo → Octaedro → Dodecaedro → Icosaedro → Esfera</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.168.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.168.0/examples/js/controls/OrbitControls.js"></script>
<script>
// Escena básica
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111122);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(3, 2, 4);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// Luz
const light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(5, 5, 5);
scene.add(light);
scene.add(new THREE.AmbientLight(0x404060, 1));
// Geometrías de los poliedros platónicos
const geometries = [
new THREE.TetrahedronGeometry(1, 0), // Tetraedro
new THREE.BoxGeometry(1.3, 1.3, 1.3), // Cubo (ajustado para tamaño similar)
new THREE.OctahedronGeometry(1, 0), // Octaedro
new THREE.DodecahedronGeometry(1, 0), // Dodecaedro
new THREE.IcosahedronGeometry(1, 0), // Icosaedro
];
// Esfera geodesica con muchas subdivisiones (muy cercana a esfera perfecta)
const sphere = new THREE.IcosahedronGeometry(1, 8); // 8 subdivisiones → 5120 triángulos
geometries.push(sphere);
// Material
const material = new THREE.MeshPhongMaterial({
color: 0x00aaff,
emissive: 0x003366,
shininess: 100,
wireframe: false,
flatShading: true
});
let currentMesh;
let targetGeometry;
let morphProgress = 0;
let currentIndex = 0;
function createMesh(geometry) {
const mesh = new THREE.Mesh(geometry, material);
mesh.scale.set(1.2, 1.2, 1.2);
return mesh;
}
function startMorphTo(index) {
if (currentMesh) scene.remove(currentMesh);
currentMesh = createMesh(geometries[currentIndex]);
targetGeometry = geometries[index];
morphProgress = 0;
currentIndex = index;
scene.add(currentMesh);
}
// Iniciar con tetraedro
startMorphTo(0);
// Animación de morphing suave
function animate() {
requestAnimationFrame(animate);
if (morphProgress < 1) {
morphProgress += 0.008; // velocidad del morphing
if (morphProgress > 1) morphProgress = 1;
// Interpolar vértices
const positionAttribute = currentMesh.geometry.attributes.position;
const targetPosition = targetGeometry.attributes.position;
for (let i = 0; i < positionAttribute.count; i++) {
const v1 = new THREE.Vector3().fromBufferAttribute(positionAttribute, i);
const v2 = new THREE.Vector3().fromBufferAttribute(targetPosition, i);
v1.lerp(v2, morphProgress);
positionAttribute.setXYZ(i, v1.x, v1.y, v1.z);
}
positionAttribute.needsUpdate = true;
// Recalcular normales para buena iluminación
currentMesh.geometry.computeVertexNormals();
} else if (currentIndex < geometries.length - 1) {
// Cuando termina una transición, preparar la siguiente
setTimeout(() => startMorphTo(currentIndex + 1), 1500);
}
currentMesh.rotation.y += 0.005;
currentMesh.rotation.x += 0.003;
controls.update();
renderer.render(scene, camera);
}
animate();
// Adaptar al tamaño de ventana
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
Comentarios recientes