Controla Animaciones 3D en apps de realidad aumentada WebAR

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

Animaciones con Three.js – A-Frame y MindAR post

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.

  1. Espera a que el modelo se cargue (model-loaded): No podemos acceder a las animaciones hasta que el archivo GLB estƩ completamente procesado.
  2. 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’).
  3. Crea el AnimationMixer: Esta es la herramienta fundamental de Three.js para gestionar y reproducir animaciones. Se asocia al modelo 3D.
  4. 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>

&nbsp;

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

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Scroll al inicio
0
Would love your thoughts, please comment.x
()
x