Cómo Controlar Animaciones 3D en Realidad Aumentada Web con MindAR, A-Frame y Three.js

La Realidad Aumentada (AR) ha transformado la manera en que interactuamos con el contenido digital, permitiƩndonos superponerlo en el mundo real. Sin embargo, la verdadera magia ocurre cuando estos objetos 3D no son estƔticos, sino que cobran vida.
En este artĆculo, desglosaremos un código completo que crea una experiencia de AR interactiva, permitiendo al usuario controlar todas las animaciones de un modelo 3D directamente desde el navegador.
- MindAR: Un motor de AR web ligero y eficiente, especializado en el reconocimiento de imƔgenes.
- A-Frame: Un framework de Mozilla que simplifica enormemente la creación de escenas 3D y de Realidad Virtual/Aumentada usando HTML.
- Three.js: La librerĆa de grĆ”ficos 3D por excelencia en la web, que funciona como el motor de renderizado bajo el capó de A-Frame.
El Objetivo del Proyecto
El código que analizaremos logra lo siguiente:
- Inicia una vista de cƔmara a travƩs del navegador.
- Utiliza MindAR para detectar un marcador de imagen especĆfico (targets.mind – Puedes utilizar el nombre que quieras solo debes de cambiarlo en el archivo y en el codigo).
- Al detectar el marcador, muestra un modelo 3D en formato GLB (RobotExpressive.glb) – Ruta de la libreria Three.js si la descargas (models/gltf/RobotExpressive/RobotExpressive.glb).
- DinÔmicamente, lee todas las pistas de animación contenidas en el archivo del modelo.
- Crea una interfaz de usuario con un botón para cada animación encontrada.
- Permite al usuario cambiar entre las animaciones del modelo con un solo clic, con transiciones suaves.
AnÔlisis Detallado del Código
El proyecto se estructura en un Ćŗnico archivo HTML, lo que facilita su despliegue. Vamos a dividirlo en sus partes fundamentales.
Estructura HTML y Configuración de la Escena.
La base de la aplicación es una escena de A-Frame configurada para MindAR.
<a-scene mindar-image="imageTargetSrc: ./targets.mind;" ...> <a-assets> <a-asset-item id="robotModel" src="./RobotExpressive.glb"></a-asset-item> </a-assets> <a-entity mindar-image-target="targetIndex: 0"> <a-gltf-model src="#robotModel" animation-controller></a-gltf-model> </a-entity> </a-scene>
- <a-scene>: Es el contenedor principal. El atributo mindar-image lo inicializa como una escena de MindAR y le indica dónde encontrar el archivo del marcador (imageTargetSrc).
- <a-assets>: Es una buena prƔctica para precargar recursos pesados como los modelos 3D, asegurando que estƩn listos antes de que la escena intente mostrarlos.
- <a-entity mindar-image-target>: Esta entidad actúa como un «ancla». Su contenido solo se harÔ visible cuando el marcador con targetIndex: 0 sea detectado por la cÔmara.
- <a-gltf-model>: Carga y muestra nuestro modelo 3D. El atributo clave aquĆ es animation-controller, que invoca nuestro componente de JavaScript personalizado.
La Interfaz de Usuario (HTML y CSS).
Para controlar las animaciones, necesitamos botones. La UI se define con un simple div y se estiliza con CSS para que aparezca en la parte inferior de la pantalla.
<div id="animation-buttons"></div>
El CSS asegura que los botones sean visibles, accesibles y no interfieran con la vista de AR. Lo interesante es que este div estĆ” vacĆo inicialmente; los botones se crearĆ”n y aƱadirĆ”n con JavaScript.
El Cerebro: El Componente animation-controller
Aquà reside toda la lógica interactiva. Creamos un componente de A-Frame llamado animation-controller.
AFRAME.registerComponent('animation-controller', {
init: function () { /* ... */ },
playAnimation: function (name) { /* ... */ },
tick: function (time, timeDelta) { /* ... */ }
});init(): Esta función se ejecuta una vez cuando el componente se carga.
- Espera a que el modelo se cargue (model-loaded): No podemos acceder a las animaciones hasta que el archivo GLB estƩ completamente procesado.
- Obtiene el objeto de Three.js: A-Frame nos permite bajar a un nivel mĆ”s profundo y acceder al objeto de Three.js con model.getObject3D(‘mesh’).
- Crea el AnimationMixer: Esta es la herramienta fundamental de Three.js para gestionar y reproducir animaciones. Se asocia al modelo 3D.
- Itera y crea botones: El código recorre el array gltf.animations (que contiene todos los clips de animación del modelo), y por cada uno, crea un botón en la UI y guarda la «acción» de animación correspondiente para poder activarla después.
- playAnimation(name): Esta función se llama cuando un usuario hace clic en un botón. Realiza una transición suave: detiene la animación activa con un fadeOut y comienza la nueva con un fadeIn y play.
- tick(): Esta función es el corazón del bucle de renderizado. Se ejecuta en cada fotograma. Su Ćŗnica, pero crucial, tarea es actualizar el AnimationMixer (mixer.update()). Sin esto, las animaciones se quedarĆan congeladas en su primer fotograma.
El Flujo de la Animación: ĀæCómo trabajan juntas las librerĆas?
Es importante entender el rol de cada tecnologĆa en el manejo de las animaciones:
- A-Frame y el Formato GLTF/GLB: A-Frame sabe cómo cargar modelos gltf o glb. Este formato es el estĆ”ndar para la web y tiene la capacidad de empaquetar en un solo archivo la geometrĆa del modelo, los materiales, las texturas y, por supuesto, mĆŗltiples pistas de animación.
- Three.js y AnimationMixer: Aunque A-Frame tiene un componente bÔsico de animación, para un control avanzado (como tener múltiples clips, mezclarlos o hacer transiciones suaves), necesitamos acceder directamente a las capacidades de Three.js. El AnimationMixer es el objeto que recibe los clips de animación y los procesa. Nosotros solo le decimos qué clip reproducir y él se encarga del resto.
- MindAR y A-Frame como Orquestadores: MindAR no gestiona las animaciones directamente. Su trabajo es proporcionar el contexto de Realidad Aumentada. A-Frame actúa como el pegamento: utiliza MindAR para saber dónde y cuÔndo mostrar la entidad, y luego nuestro componente personalizado utiliza la potencia de Three.js para darle vida al modelo dentro de esa entidad.
Información Clave para Realizar este tipo de Aplicaciones
Si quieres empezar a crear tus propias experiencias de AR con animaciones, ten en cuenta lo siguiente:
- El Modelo es la Clave: Tu archivo gltf o glb debe tener las animaciones ya integradas. Estas se crean en software de modelado 3D como Blender, Maya o 3ds Max.
- La Necesidad de un Servidor Local: Por razones de seguridad del navegador (polĆticas CORS), no puedes simplemente abrir el archivo index.html en tu navegador. Debes servirlo a travĆ©s de un servidor web local. Herramientas como la extensión Ā«Live ServerĀ» para Visual Studio Code son perfectas para esto.
- Calidad del Marcador: El rendimiento del seguimiento de MindAR depende en gran medida de tu marcador. Usa imƔgenes con buen contraste, muchos detalles y patrones no repetitivos.
- Optimización: Los dispositivos móviles tienen recursos limitados. Usa modelos 3D con un nĆŗmero de polĆgonos razonable (low-poly) y texturas optimizadas para la web para asegurar una experiencia fluida.
Conclusión
Este proyecto es un ejemplo fantĆ”stico de cómo las librerĆas web modernas pueden colaborar para crear experiencias de Realidad Aumentada ricas e interactivas. Al combinar la simplicidad declarativa de A-Frame con el poder de bajo nivel de Three.js, hemos construido un sistema reutilizable que puede dar vida a cualquier modelo 3D con animaciones, directamente en el navegador de cualquier smartphone.
Ahora te toca a ti. ”Intenta cambiar el modelo, crea tus propios marcadores y experimenta con el fascinante mundo de la Realidad Aumentada web!
Quieres el código completo de la aplicación, aquà lo tienes:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.jsdelivr.net/gh/aframevr/aframe@1.5.0/dist/aframe-master.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mind-ar@1.2.5/dist/mindar-image-aframe.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.138.0/build/three.min.js"></script>
<style>
/* Estilos para los botones de animación */
#animation-buttons {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
#animation-buttons button {
padding: 10px 15px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
border: 2px solid white;
border-radius: 5px;
cursor: pointer;
font-family: sans-serif;
}
#animation-buttons button:hover {
background-color: rgba(255, 255, 255, 0.9);
color: black;
}
</style>
<script>
// Componente de A-Frame para controlar las animaciones
AFRAME.registerComponent('animation-controller', {
init: function () {
this.mixer = null;
this.actions = {};
this.activeAction = null;
const model = this.el;
const uiContainer = document.querySelector('#animation-buttons');
// 1. Esperar a que el modelo 3D se cargue
model.addEventListener('model-loaded', (e) => {
const gltf = model.getObject3D('mesh');
if (!gltf) return;
// 2. Crear el AnimationMixer de Three.js
this.mixer = new THREE.AnimationMixer(gltf);
const animations = gltf.animations;
// 3. Crear un botón y una acción para cada animación
animations.forEach((clip) => {
const action = this.mixer.clipAction(clip);
this.actions[clip.name] = action;
// Crear el botón en la UI
const button = document.createElement('button');
button.innerText = clip.name;
button.addEventListener('click', () => {
this.playAnimation(clip.name);
});
uiContainer.appendChild(button);
});
// Opcional: Iniciar la primera animación por defecto
if (animations.length > 0) {
this.playAnimation(animations[0].name);
}
});
},
// Función para cambiar de animación
playAnimation: function (name) {
const newAction = this.actions[name];
if (this.activeAction === newAction) return; // No hacer nada si ya se estĆ” reproduciendo
// Detener suavemente la animación anterior
if (this.activeAction) {
this.activeAction.fadeOut(0.5);
}
// Reproducir la nueva animación
newAction.reset().fadeIn(0.5).play();
this.activeAction = newAction;
},
// 4. Actualizar el mixer en cada frame
tick: function (time, timeDelta) {
if (this.mixer) {
this.mixer.update(timeDelta / 1000); // delta estĆ” en milisegundos
}
},
});
</script>
</head>
<body>
<div id="animation-buttons"></div>
<a-scene mindar-image="imageTargetSrc: ./targets.mind;" color-space="sRGB" renderer="colorManagement: true, physicallyCorrectLights" vr-mode-ui="enabled: false" device-orientation-permission-ui="enabled: false">
<a-assets>
<a-asset-item id="robotModel" src="./RobotExpressive.glb"></a-asset-item>
</a-assets>
<a-camera position="0 0 0" look-controls="enabled: false"></a-camera>
<a-entity mindar-image-target="targetIndex: 0">
<a-gltf-model
src="#robotModel"
position="0 0 0"
scale="0.4 0.4 0.4"
rotation="0 0 0"
animation-controller>
</a-gltf-model>
</a-entity>
</a-scene>
</body>
</html>TambiƩn te puede interesar: