
¿Alguna vez has comprado unos aretes por internet y al llegar no eran del tamaño que esperabas? La Realidad Aumentada (WebAR) está solucionando esto con los «Virtual Try-On» (Probadores Virtuales). Hoy, vamos a construir uno desde cero que funcione directamente en el navegador, sin instalar aplicaciones.
¿Qué estamos construyendo?
Vamos a crear una aplicación web que activa la cámara frontal (modo selfie), detecta tus orejas y coloca modelos 3D de aretes sobre ellas. Pero no nos quedaremos ahí: resolveremos el problema técnico número uno de estos filtros: la oclusión. Haremos que el arete «desaparezca» lógicamente cuando gires la cabeza, para que no se vea flotando a través de tu cráneo.
Para este proyecto utilizaremos tecnologías gratuitas y de código abierto:
- HTML5: La estructura base.
- A-Frame (v1.4.2 o 1.5.0): Nuestro motor de gráficos 3D. Nos permite manejar modelos, luces y cámaras con etiquetas simples.
- MindAR Face Tracking: Una librería increíble que detecta 468 puntos en el rostro humano directamente en el navegador.
¿Cómo funciona?
Encontrando las Orejas: MindAR nos devuelve una «malla» facial. Aunque no tiene un punto exacto para el «lóbulo», usamos dos anclajes (anchors) que están muy cerca:
- Punto 127: Lado izquierdo de la cara.
- Punto 356: Lado derecho de la cara.
Te preguntaras de donde salen estos números y estas referencias: tfjs-models/face-landmarks-detection/mesh_map.jpg at master · tensorflow/tfjs-models · GitHub
Ajustamos manualmente la posición (position=»-0.01 -0.35 -0.1″) para mover el modelo desde el borde de la mandíbula hasta el lóbulo de la oreja.
El Problema del «Arete Fantasma»
Si solo colocas los modelos 3D, verás un error visual molesto: al girar la cabeza a la izquierda, tu oreja derecha queda oculta por tu propia cabeza, pero el arete virtual sigue flotando visible.
La Solución: Interruptor por Profundidad (Z-Depth)
Para arreglarlo, implementamos un script inteligente (directional-switch). En lugar de intentar tapar el arete con objetos invisibles, usamos matemáticas:
- Calculamos la posición en el espacio (Eje Z – Profundidad) de ambas orejas en tiempo real.
- Si la oreja derecha está mucho más «al fondo» (lejos de la cámara) que la izquierda, significa que has girado la cara.
- El código automáticamente escala el arete lejano a 0, haciéndolo desaparecer.
- Cuando vuelves a mirar de frente (la diferencia de profundidad es mínima), ambos aretes reaparecen.
Aquí te dejo el JavaScript que es el cerebro de la aplicacion.
<script>
document.addEventListener("DOMContentLoaded", function() {
const list = [".modelo-oro", ".modelo-plata", ".modelo-diamante", ".modelo-glb"];
let visibleIndex = 0;
const setStyle = (idx) => {
document.querySelectorAll(".arete").forEach(el => el.setAttribute("visible", false));
document.querySelectorAll(list[idx]).forEach(el => el.setAttribute("visible", true));
};
document.querySelector("#boton-cambiar").addEventListener("click", () => {
visibleIndex = (visibleIndex + 1) % list.length;
setStyle(visibleIndex);
});
const scene = document.querySelector('a-scene');
scene.addEventListener("arReady", (event) => {
document.querySelector("#loading").style.display = "none";
console.log("¡Sistema Facial Listo!");
setTimeout(() => { window.dispatchEvent(new Event('resize')); }, 500);
});
});
AFRAME.registerComponent('directional-switch', {
init: function() {
this.leftAnchor = document.querySelector('#anchor-left');
this.rightAnchor = document.querySelector('#anchor-right');
this.leftGroup = document.querySelector('#grupo-izquierdo');
this.rightGroup = document.querySelector('#grupo-derecho');
this.debugText = document.querySelector('#debug-console');
this.vecL = new THREE.Vector3();
this.vecR = new THREE.Vector3();
},
tick: function() {
if (!this.leftAnchor || !this.rightAnchor) return;
this.leftAnchor.object3D.getWorldPosition(this.vecL);
this.rightAnchor.object3D.getWorldPosition(this.vecR);
const diffZ = this.vecL.z - this.vecR.z;
if (this.debugText) {
this.debugText.setAttribute('value', `Diff Z: ${diffZ.toFixed(3)}`);
}
const umbral = 2;
if (diffZ > umbral) {
this.leftGroup.object3D.scale.set(1, 1, 1);
this.rightGroup.object3D.scale.set(0, 0, 0);
} else if (diffZ < -umbral) {
this.leftGroup.object3D.scale.set(0, 0, 0);
this.rightGroup.object3D.scale.set(1, 1, 1);
} else {
this.leftGroup.object3D.scale.set(1, 1, 1);
this.rightGroup.object3D.scale.set(1, 1, 1);
}
}
});
</script>Puedes agregar cualquier tipo de elemento que quieras para este proyecto, desde crear y agregar los aretes 3D por código o puedes utilizar modelos 3D en formato GLB o tal vez quieras usar archivos SPLAT super realistas y para este tipo de aplicaciones es perfecto.
Te dejo aquí una entrada sobre los archivos SPLAT que te puede interesar: WebAR Hiperrealista: Tutorial de Gaussian Splatting
Para continuar con el tutorial es importante que agregue el HTML y el CSS para que todo funcione perfecto y se vea bien.
Esta es la scene de realidad aumentada.
<a-scene mindar-face color-space="sRGB" renderer="colorManagement: true, physicallyCorrectLights" vr-mode-ui="enabled: false" device-orientation-permission-ui="enabled: false"> <a-assets> <a-asset-item id="arete-3d" src="./Arete.glb"></a-asset-item> </a-assets> <a-camera active="false" position="0 0 0"></a-camera> <a-entity directional-switch> <a-text id="debug-console" value="Calculando Z..." position="0 0.8 -1" align="center" scale="0.5 0.5 0.5" color="red"></a-text> </a-entity> <a-entity id="anchor-left" mindar-face-target="anchorIndex: 127"> <a-entity id="grupo-izquierdo" position="-0.01 -0.35 -0.1" rotation="0 -90 0"> <a-torus class="arete modelo-oro" radius="0.04" radius-tubular="0.005" color="#FFD700" visible="true" material="metalness: 1; roughness: 0.2"></a-torus> <a-torus class="arete modelo-plata" radius="0.03" radius-tubular="0.008" color="#C0C0C0" visible="false" material="metalness: 1; roughness: 0.1"></a-torus> <a-sphere class="arete modelo-diamante" radius="0.02" color="#E0FFFF" visible="false" material="metalness: 0.9; roughness: 0; opacity: 0.8"></a-sphere> <a-gltf-model src="#arete-3d" class="arete modelo-glb" visible="false" scale="0.035 0.035 0.035" position="0 -1.1 0" rotation="0 -90 0"></a-gltf-model> </a-entity> </a-entity> <a-entity id="anchor-right" mindar-face-target="anchorIndex: 356"> <a-entity id="grupo-derecho" position="0.01 -0.35 -0.1" rotation="0 90 0"> <a-torus class="arete modelo-oro" radius="0.04" radius-tubular="0.005" color="#FFD700" visible="true" material="metalness: 1; roughness: 0.2"></a-torus> <a-torus class="arete modelo-plata" radius="0.03" radius-tubular="0.008" color="#C0C0C0" visible="false" material="metalness: 1; roughness: 0.1"></a-torus> <a-sphere class="arete modelo-diamante" radius="0.02" color="#E0FFFF" visible="false" material="metalness: 0.9; roughness: 0; opacity: 0.8"></a-sphere> <a-gltf-model src="#arete-3d" class="arete modelo-glb" visible="false" scale="0.035 0.035 0.035" position="0 -1.1 0" rotation="0 -90 0"></a-gltf-model> </a-entity> </a-entity> <a-light type="directional" position="1 4 2" intensity="1.2"></a-light> <a-light type="ambient" intensity="0.6"></a-light> </a-scene>
y listo! ya deberías tener tu aplicación de realidad aumentada funcionando 100%.
Descarga los archivos fuentes de este tutorial en mi Patreon: PROBADOR VIRTUAL DE ARETES CON REALIDAD AUMENTADA
¿Que contiene este artículo en Patreon?
- Código HTML.
- Código CSS.
- Código JavaScript.
- Todo el proyecto armado en un solo archivo. Lo subes a tu servidor y ya tienes tu probador virtual.
- Cada línea de código esta comentada para que aprendas que hace y para qué sirve cada línea y así puedas crear y agregar mas contenido a tu WebAR.
DA CLIC EN LA IMAGEN PARA SER ENVIADO A MI PATREON. – Gracias por tu apoyo.
