geo html

<!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>