La API Hit Test le posibilita colocar ítems virtuales en una vista del mundo real.
Actualizado
La API del dispositivo WebXR se envió el otoño pasado en Chrome 79. Como se dijo entonces, la implementación de la API en Chrome es un trabajo en progreso. Chrome se complace en anunciar que parte del trabajo está acabado. En Chrome 81, han llegado dos nuevas funciones:
Este post cubre el API de prueba de visita de WebXR, un medio para colocar objetos virtuales en una vista de cámara del mundo real.
En este post, supongo que ya sabe cómo crear una sesión de realidad aumentada y que sabe cómo ejecutar un frame loop. Si no está familiarizado con estos conceptos, debería leer los posts anteriores de esta serie.
La muestra de sesión de realidad aumentada inmersiva
El código de este post se basa, pero no es equivalente, al que se encuentra en la muestra de prueba de impacto del Immersive Web Working Group (manifestación,
fuente). Este ejemplo le posibilita colocar girasoles virtuales en superficies del mundo real.
Cuando abra la aplicación por primera vez, verá un círculo azul con un punto en el medio. El punto es la intersección entre una línea imaginaria desde su dispositivo hasta el punto en el entorno. Se mueve a medida que mueve el dispositivo. A medida que encuentra puntos de intersección, parece que se ajusta a superficies como suelos, mesas y paredes. Lo hace debido a que la prueba de impacto proporciona la posición y orientación del punto de intersección, pero nada sobre las superficies en sí.
Un recordatorio para aquellos de ustedes que son nuevos: en la API del dispositivo WebXR, «posición y orientación» están cubiertos por el término pose. Usaré ese término de ahora en más.
Este círculo se llama retículo, que es una imagen temporal que ayuda a colocar un objeto en realidad aumentada. Si toca la pantalla, se coloca un girasol en la superficie en la ubicación de la retícula y la orientación del punto de la retícula, sin tener en cuenta dónde haya tocado la pantalla. La retícula continúa moviéndose con su dispositivo.

La retícula es una imagen temporal que ayuda a colocar un objeto en realidad aumentada.
Crea la retícula
Debe crear la imagen de la retícula usted mismo, dado que no la proporciona el navegador ni la API. El método de carga y dibujo es específico del marco. Si no lo está dibujando de forma directa utilizando WebGL o WebGL2, consulte la documentación de su marco. Por esta razón, no entraré en detalles acerca de cómo se dibuja la retícula en la muestra. A continuación, muestro una línea por una sola razón: para que en ejemplos de código posteriores, sepa a qué me refiero cuando uso el reticle
variable.
let reticle = new Gltf2Node({dirección url: 'media/gltf/reticle/reticle.gltf'});
Solicita una sesión
Al solicitar una sesión, debe solicitar 'hit-test'
en el
requiredFeatures
matriz como se muestra a continuación.
navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['local', 'hit-test']
})
.then((session) => {
});
Ingresar en una sesión
En posts anteriores, presenté el código para entrar a una sesión XR. He mostrado una versión de esto a continuación con algunas adiciones. Primero agregué el select
oyente de eventos. Cuando el usuario toca la pantalla, se colocará una flor en la vista de la cámara según la pose del retículo. Describiré ese oyente de eventos más tarde.
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
xrSession.addEventListener('select', onSelect);
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
xrSession.requestReferenceSpace('viewer').then((refSpace) => {
xrViewerSpace = refSpace;
xrSession.requestHitTestSource({ space: xrViewerSpace })
.then((hitTestSource) => {
xrHitTestSource = hitTestSource;
});
});
xrSession.requestReferenceSpace('local').then((refSpace) => {
xrRefSpace = refSpace;
xrSession.requestAnimationFrame(onXRFrame);
});
}
Diversos espacios de referencia
Observe que el código resaltado llama XRSession.requestReferenceSpace()
dos veces. Inicialmente encontré esto confuso. Pregunté por qué el código de prueba de impacto no solicita un cuadro de animación (iniciando el ciclo de cuadro) y por qué el ciclo de cuadro no parece involucrar pruebas de impacto. El origen de la confusión fue un malentendido de los espacios de referencia. Los espacios de referencia expresan relaciones entre un origen y el mundo.
Para saber lo que hace este código, imagina que estás viendo esta muestra con un equipo independiente y tienes tanto un auricular como un controlador. Para medir distancias desde el controlador, usaría un marco de referencia centrado en el controlador. Pero para dibujar algo en la pantalla, usaría coordenadas centradas en el usuario.
En esta muestra, el visor y el controlador son el mismo dispositivo. Pero tengo un obstáculo. Lo que dibujo debe ser estable con respecto al entorno, pero el ‘controlador’ con el que estoy dibujando se está moviendo.
Para dibujar imágenes, utilizo el local
espacio de referencia, lo que me da estabilidad en cuanto al entorno. Posteriormente de conseguir esto, comienzo el ciclo del marco llamando requestAnimationFrame()
.
Para las pruebas de posicionamiento, utilizo el viewer
espacio de referencia, que se basa en la pose del dispositivo en el momento de la prueba de impacto. La etiqueta «visor» es algo confusa en este contexto debido a que estoy hablando de un controlador. Tiene sentido si piensa en el controlador como un visor electrónico. Posteriormente de recibir esto, llamo xrSession.requestHitTestSource()
, que crea la fuente de datos de prueba de impacto que usaré al dibujar.
Ejecutando un bucle de cuadro
los requestAnimationFrame()
callback además obtiene un nuevo código para manejar las pruebas de visitas.
A medida que mueve su dispositivo, la retícula debe moverse con él mientras intenta hallar superficies. Para crear la ilusión de movimiento, vuelva a dibujar la retícula en cada fotograma. Pero no muestre la retícula si falla la prueba. Entonces, para la retícula que creé previamente, configuré su visible
propiedad a false
.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
reticle.visible = false;
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.visible = true;
reticle.matrix = pose.transform.matrix;
}
}
}
Para dibujar cualquier cosa en AR, necesito saber dónde está el espectador y hacia dónde está mirando. Por lo tanto pruebo eso hitTestSource
y el xrViewerPose
siguen siendo válidos.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
reticle.visible = false;
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.visible = true;
reticle.matrix = pose.transform.matrix;
}
}
}
Ahora llamo getHitTestResults()
. Se requiere el hitTestSource
como argumento y devuelve una matriz de HitTestResult
instancias. La prueba de impacto puede hallar diversos superficies. El primero de la matriz es el más cercano a la cámara. La mayoría de las veces lo usará, pero se devuelve una matriz para casos de uso avanzados. A modo de ejemplo, imagina que tu cámara apunta a una caja en una mesa en el piso. Es viable que la prueba de impacto devuelva las tres superficies de la matriz. En la mayoría de los casos, será la caja que me importa. Si la longitud de la matriz devuelta es 0, en otras palabras, si no se devuelve ninguna prueba, continúe hacia adelante. Vuelve a intentarlo en el siguiente cuadro.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
reticle.visible = false;
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.visible = true;
reticle.matrix = pose.transform.matrix;
}
}
}
Para terminar, necesito procesar los resultados de la prueba de posicionamiento. El procedimiento básico es este. Obtenga una pose del resultado de la prueba de impacto, transforme (mueva) la imagen del retículo a la posición de la prueba de impacto, posteriormente configure su visible
propiedad a verdadero. La pose representa la pose de un punto en una superficie.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);reticle.visible = false;
if (xrHitTestSource && xrViewerPose) {
let hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
let pose = hitTestResults[0].getPose(xrRefSpace);
reticle.matrix = pose.transform.matrix;
reticle.visible = true;
}
}
}
Colocando un objeto
Un objeto se coloca en AR cuando el usuario toca la pantalla. Ya agregué un
select
controlador de eventos a la sesión. (Véase más arriba.)
Lo importante en este paso es saber dónde colocarlo. Puesto que la retícula en movimiento le brinda una fuente constante de pruebas de impacto, la forma más sencilla de colocar un objeto es dibujarlo en la ubicación de la retícula en la última prueba de impacto.
function onSelect(event) {
if (reticle.visible) {
addARObjectAt(reticle.matrix);
}
}
Conclusión
La mejor manera de manejar esto es pasar por el Código de muestra o prueba el
codelab. Espero haberte dado suficientes antecedentes para que ambos tengan sentido.
No hemos acabado de crear API web inmersivas, ni mucho menos. Publicaremos nuevos posts aquí a medida que avancemos.
Foto por Daniel Frank en Unsplash