Realidad Aumentada Interactiva: Haz que los Objetos Virtuales Reaccionen a la Proximidad Física con MindAR y A-Frame


La Realidad Aumentada (RA) va más allá de superponer objetos virtuales en el mundo real; se trata de hacer que esos objetos sean inteligentes e interactivos. Imagina una experiencia de RA donde los elementos virtuales responden a tus acciones físicas. Hoy, nos sumergiremos en la creación de una fascinante aplicación de RA interactiva utilizando MindAR.js y A-Frame, donde una esfera virtual cambia de color basándose en la proximidad de dos marcadores físicos. Esto abre un mundo de posibilidades para nuevas experiencias, herramientas educativas e interfaces de RA dinámicas.
Antes de comenzar con este tutorial te recomiendo este post sobre como aprender detectar la distancia de un marcador en AR: Crea una app WebAR – Detecta distancia con HTML y Javascript
¿Qué Necesitarás?
Antes de comenzar, asegúrate de tener lo siguiente:
- Conocimientos Básicos de MindAR.js: Familiaridad con el funcionamiento de MindAR.js con marcadores de imagen.
- Fundamentos de A-Frame: Soltura con la sintaxis similar a HTML de A-Frame para escenas 3D.
- Dos Marcadores de Imagen: Imágenes de alta calidad y distintivas que usarás como tus marcadores de RA.
- Compilador de imagenes de MindAR: Acceso al Compilador de MindAR para generar tu archivo de marcadores .mind.
Prepara Tus Marcadores de Imagen para MindAR
Para este proyecto, necesitarás dos marcadores de imagen distintos (por ejemplo, marcador1.png y marcador2.png). Estas serán las imágenes físicas que tu cámara reconocerá.
Paso Crucial: Ve al Compilador de MindAR. Sube ambas imágenes de marcador simultáneamente y compílalas en un único archivo .mind (por ejemplo, targets.mind). Este archivo combinado permite a MindAR rastrear múltiples marcadores de manera eficiente dentro de la misma escena. Descarga este archivo targets.mind y colócalo en el mismo directorio que tu archivo HTML.
Configuración del HTML y A-Frame
Crea un archivo HTML (por ejemplo, index.html) y pega el siguiente código. Este código configura tu escena de RA, incluye las bibliotecas necesarias y define tus objetos virtuales (esferas) asociados a tus marcadores de imagen.
<div>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mind-ar@1.2.1/dist/mindar-image-aframe.prod.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
#info-panel {
position: absolute;
top: 10px;
left: 10px;
background-color: rgba(255, 255, 255, 0.8);
padding: 10px;
border-radius: 5px;
font-family: monospace;
z-index: 100;
}
</style>
</head>
<body>
<div id="info-panel">
<p>Target 0 Position: <span id="target0-pos">Loading...</span></p>
<p>Target 1 Position: <span id="target1-pos">Loading...</span></p>
<p>Distance: <span id="distance">Loading...</span></p>
<p>Threshold: <span id="threshold-display">0.03 m</span></p> </div>
<a-scene
mindar-image="imageTargetSrc: targets.mind; maxTrack: 2; uiLoading: yes; uiError: yes; uiScanning: yes;"
vr-mode-ui="enabled: false"
device-orientation-permission-ui="enabled: false"
>
<a-camera
position="0 0 0"
look-controls="enabled: false"
></a-camera>
<a-entity mindar-image-target="targetIndex: 0" id="target0">
<a-sphere
id="sphere0"
position="0 0 0"
radius="0.5"
color="red"
></a-sphere>
</a-entity>
<a-entity mindar-image-target="targetIndex: 1" id="target1">
<a-sphere
id="sphere1"
position="0 0 0"
radius="0.5"
color="blue"
></a-sphere>
</a-entity>
</a-scene>
<script>
AFRAME.registerComponent('mindar-target-tracker', {
init: function () {
this.target0 = document.querySelector('#target0');
this.target1 = document.querySelector('#target1');
this.sphere1 = document.querySelector('#sphere1');
this.target0PosDisplay = document.querySelector('#target0-pos');
this.target1PosDisplay = document.querySelector('#target1-pos');
this.distanceText = document.querySelector('#distance');
this.thresholdDisplay = document.querySelector('#threshold-display'); // Get threshold display
this.proximityThreshold = 700; // Distance
this.thresholdDisplay.textContent = `${this.proximityThreshold.toFixed(3)} m`; // Display the threshold
this.target0Visible = false;
this.target1Visible = false;
// Event listeners for found/lost
this.target0.addEventListener('targetFound', (event) => {
console.log('Target 0 FOUND!');
this.target0Visible = true;
});
this.target0.addEventListener('targetLost', (event) => {
console.log('Target 0 LOST!');
this.target0Visible = false;
this.target0PosDisplay.textContent = 'Not Visible';
this.sphere1.setAttribute('color', 'blue');
});
this.target1.addEventListener('targetFound', (event) => {
console.log('Target 1 FOUND!');
this.target1Visible = true;
});
this.target1.addEventListener('targetLost', (event) => {
console.log('Target 1 LOST!');
this.target1Visible = false;
this.target1PosDisplay.textContent = 'Not Visible';
this.sphere1.setAttribute('color', 'blue');
});
console.log('MindAR Target Tracker component initialized.');
},
tick: function () {
this.updatePositionsAndDistance();
},
updatePositionsAndDistance: function () {
const worldPos0 = new THREE.Vector3();
const worldPos1 = new THREE.Vector3();
this.target0Visible = this.target0.object3D.visible;
this.target1Visible = this.target1.object3D.visible;
if (this.target0Visible) {
this.target0.object3D.getWorldPosition(worldPos0);
this.target0PosDisplay.textContent = `${worldPos0.x.toFixed(
3
)}, ${worldPos0.y.toFixed(3)}, ${worldPos0.z.toFixed(3)}`;
} else {
this.target0PosDisplay.textContent = 'Not Visible';
}
if (this.target1Visible) {
this.target1.object3D.getWorldPosition(worldPos1);
this.target1PosDisplay.textContent = `${worldPos1.x.toFixed(
3
)}, ${worldPos1.y.toFixed(3)}, ${worldPos1.z.toFixed(3)}`;
} else {
this.target1PosDisplay.textContent = 'Not Visible';
}
if (this.target0Visible && this.target1Visible) {
const distance = worldPos0.distanceTo(worldPos1);
this.distanceText.textContent = `${distance.toFixed(3)} m`;
// --- DEBUGGING OUTPUT ---
console.log(`Current Distance: ${distance.toFixed(4)}m`);
console.log(`Threshold: ${this.proximityThreshold.toFixed(4)}m`);
console.log(`Condition (distance < threshold): ${distance < this.proximityThreshold}`);
if (distance < this.proximityThreshold) {
this.sphere1.setAttribute('color', 'red');
console.log('COLOR CHANGED TO RED!');
} else {
this.sphere1.setAttribute('color', 'blue');
// console.log('COLOR REMAINS BLUE (or reset to blue).'); // Keep this commented unless you want a lot of console spam
}
} else {
this.distanceText.textContent = 'N/A (One or both targets not visible)';
this.sphere1.setAttribute('color', 'blue');
// console.log('One or both targets not visible. Sphere color reset to blue.');
}
},
});
document.querySelector('a-scene').setAttribute('mindar-target-tracker', '');
</script>
</body>
</html>
</div>
¿Cómo probar tu aplicación de Realidad Aumentada?
- Guarda el código: Guarda el código HTML anterior como index.html.
- Coloca el archivo .mind: Asegúrate de que tu targets.mind (el archivo compilado con ambos marcadores) esté en la misma carpeta que tu index.html.
- Imprime tus marcadores: Obtén copias físicas de alta calidad de las dos imágenes que usaste para crear targets.mind.
- Abre en el navegador: Abre index.html en un navegador web moderno en tu teléfono o computadora con cámara web. ¡Asegúrate de conceder permisos a la cámara!
- Escanea los marcadores: Apunta la cámara hacia tus marcadores impresos. Deberías ver las esferas virtuales superpuestas en tus marcadores físicos.
- Experimenta la proximidad: Acerca lentamente el segundo marcador al primero. Cuando la distancia entre ellos sea inferior una de las esfera cambiara de color.
Consejos Esenciales para un Rastreo AR Preciso (¡Especialmente en la Proximidad!)
- Aunque el código está perfecto, la realidad aumentada en el navegador (WebAR) depende mucho del entorno físico y la calidad de tus marcadores. Para lograr una detección de manera confiable, la precisión del rastreo es crítica.
- Calidad del Marcador Físico: Este es el factor más importante.
- Imprime tus marcadores en papel mate (no brillante) para evitar reflejos.
- Asegúrate de que no haya arrugas, dobleces o curvaturas en el papel. Un marcador perfectamente plano es vital. Considera pegarlos a un cartón rígido si es necesario.
- Iluminación del Entorno: Una iluminación uniforme y difusa es tu mejor amiga.
- Evita la luz solar directa o los focos que creen sombras duras o brillos excesivos en los marcadores.
- Fondo Simple: Coloca tus marcadores sobre una superficie plana y de color uniforme que contraste bien con el marcador. Los fondos con patrones o mucha «información» visual pueden confundir el algoritmo de rastreo.
- Estabilidad de la Cámara: Intenta mantener el dispositivo lo más estable posible. Pequeños movimientos de la mano pueden causar «jitters» o fluctuaciones en la posición rastreada, lo que impactará directamente en la precisión de la distancia.
Te dejo el short de Youtube para que veas como funciona.
Gracias por la información y vocación. Tengo un proyecto de AR con MindAR para la enseñanza de la matemática con 6 módulos distintos funcional, acabo de descubrir tu blog. Donde dice «Distance» ¿Qué tipo de distancia es? no creo que sean metros o sí.
Hola Samuel, muchas gracias por escribir. La distancia puede ser medida en la unidad que tu quieras solo tienes que hacer la modificacion pertinente en tu codigo. Lo dificil en este caso es la escala en relacion a la camara web y la medida de la profundida sobre todo debido a que se utiliza una camara estatica. Es por eso que es mejor trabajar con medidas grandes al igual que las distancias.