The API Hit Test le posibilita colocar ítems virtuales en una vista del mundo real.
Updated
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:
East post cubre el WebXR Visit Test API, a means of placing virtual objects in a camera view of the real world.
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.
The immersive augmented reality session sample
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 (manifestation,
source). 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.
This circle is called reticle, 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.
Create the grid
Debe crear la imagen de la retícula usted mismo, dado que no la proporciona el browser 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'});
Request a session
When requesting a session, you must request 'hit-test'
at
requiredFeatures
matrix as shown below.
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 Username 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
Notice that the highlighted code calls XRSession.requestReferenceSpace ()
twice. I initially found this confusing. I asked why the crash test code doesn't ask for an animation frame (starting the frame loop) and why the frame loop doesn't seem to involve crash tests. The source of the confusion was a misunderstanding of the reference spaces. Reference spaces express relationships between an origin and the world.
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.
To draw pictures, I use the 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 positioning, 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 ()
, which creates the bump test data source that I will use when drawing.
Running a frame loop
the 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
property to 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
and the xrViewerPose
are still valid.
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;
}
}
}
Now i call getHitTestResults ()
. Se requiere el hitTestSource
as an argument and returns an array of 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
property to true. The pose represents the pose of a point on a surface.
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;
}
}
}
Placing an object
An object is placed in AR when the user touches the screen. I already added a
select
event handler to the session. (See above.)
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);
}
}
conclusion
The best way to handle this is to go through the Sample code or try the
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.
Photo by Daniel frank in Unsplash