Realidad aumentada: Quizás ya la conozcas

Si ya usaste la API de WebXR Device, ya habrás terminado.

Joe Medley
Joe Medley

La API de WebXR Device se envió el otoño pasado en Chrome 79. Como se indicó en ese momento, la implementación de la API por parte de Chrome está en desarrollo. Chrome se complace en anunciar que parte del trabajo está terminado. En Chrome 81, llegaron dos funciones nuevas:

En este artículo, se aborda la realidad aumentada. Si ya usaste la API de WebXR Device, te alegrará saber que hay muy poco que aprender. Acceder a una sesión de WebXR es prácticamente lo mismo. Ejecutar un bucle de fotogramas es en gran medida lo mismo. Las diferencias se encuentran en las configuraciones que permiten que el contenido se muestre de forma adecuada para la realidad aumentada. Si no estás familiarizado con los conceptos básicos de WebXR, deberías leer mis publicaciones anteriores sobre la API de WebXR Device o, al menos, estar familiarizado con los temas que se tratan allí. Debes saber cómo solicitar y, luego, ingresar una sesión y saber cómo ejecutar un bucle de fotogramas.

Para obtener más información sobre las pruebas de posicionamiento, consulta el artículo complementario Posicionamiento de objetos virtuales en vistas del mundo real. El código de este artículo se basa en la muestra de la sesión de RA de Immersive (demo fuente) de las muestras de la API de WebXR Device del Immersive Web Working Group.

Antes de sumergirte en el código, debes usar la muestra de sesión de RA envolvente al menos una vez. Necesitarás un teléfono Android moderno con Chrome 81 o una versión posterior.

¿Para qué es útil?

La realidad aumentada será un complemento valioso para muchas páginas web nuevas o existentes, ya que les permitirá implementar casos de uso de RA sin salir del navegador. Por ejemplo, puede ayudar a las personas a aprender en sitios educativos y permitir que los compradores potenciales visualicen los objetos de sus casas mientras compran.

Considera el segundo caso de uso. Imagina que simulas colocar la representación de tamaño real de un objeto virtual en una escena real. Una vez colocada, la imagen permanece en la superficie seleccionada, aparece del tamaño que tendría si el elemento real estuviera en esa superficie y permite que el usuario se acerque a ella o se acerque a ella. De esta manera, los usuarios comprenden mejor el objeto que con una imagen bidimensional.

Me estoy adelantando un poco. Para hacer lo que describí, necesitas funciones de RA y algunos medios para detectar superficies. En este artículo, se abarca el primero. En el artículo adjunto sobre la API de WebXR Hit Test (que se vincula más arriba), se aborda este último.

Cómo solicitar una sesión

El proceso de solicitar una sesión es muy parecido a lo que viste antes. Primero, llama a xr.isSessionSupported() para averiguar si el tipo de sesión que deseas está disponible en el dispositivo actual. En lugar de solicitar 'immersive-vr' como antes, solicita 'immersive-ar'.

if (navigator.xr) {
  const supported = await navigator.xr.isSessionSupported('immersive-ar');
  if (supported) {
    xrButton.addEventListener('click', onButtonClicked);
    xrButton.textContent = 'Enter AR';
    xrButton.enabled = supported; // supported is Boolean
  }
}

Como antes, esto habilita el botón “Enter AR”. Cuando el usuario haga clic en él, llama a xr.requestSession() y pasa 'immersive-ar'.

let xrSession = null;
function onButtonClicked() {
  if (!xrSession) {
    navigator.xr.requestSession('immersive-ar')
    .then((session) => {
      xrSession = session;
      xrSession.isImmersive = true;
      xrButton.textContent = 'Exit AR';
      onSessionStarted(xrSession);
    });
  } else {
    xrSession.end();
  }
}

Propiedad de conveniencia

Probablemente hayas notado que destaqué dos líneas en la última muestra de código. Parece que el objeto XRSession tiene una propiedad llamada isImmersive. Esta es una propiedad útil que creé y no forma parte de la especificación. La usaré más adelante para tomar decisiones sobre qué mostrarle al usuario. ¿Por qué esta propiedad no es parte de la API? Debido a que es posible que tu app necesite realizar un seguimiento de esta propiedad de otra manera, los autores de especificaciones decidieron mantener limpia la API.

Cómo ingresar a una sesión

Recuerda cómo era onSessionStarted() en mi artículo anterior:

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(session, gl)
  });

  xrSession.requestReferenceSpace('local-floor')
  .then((refSpace) => {
    xrRefSpace = refSpace;
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

Debo agregar algunos aspectos para renderizar la realidad aumentada. Desactivar el fondo. Primero, voy a determinar si necesito el fondo. Este es el primer lugar en el que usaré mi propiedad de conveniencia.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  if (session.isImmersive) {
    removeBackground();
  }

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(session, gl)
  });

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });

}

Espacios de referencia

Mis artículos anteriores hojearon espacios de referencia. En la muestra que describo, se usan dos, así que es hora de corregir esa omisión.

Un espacio de referencia describe la relación entre el mundo virtual y el entorno físico del usuario. Para ello, sigue estos pasos:

  • Especificar el origen del sistema de coordenadas que se usa para expresar posiciones en el mundo virtual
  • Especificar si se espera que el usuario se mueva dentro de ese sistema de coordenadas
  • Si ese sistema de coordenadas tiene límites preestablecidos (En los ejemplos que se muestran aquí, no se usan sistemas de coordenadas con límites preestablecidos).

Para todos los espacios de referencia, la coordenada X se expresa hacia la izquierda y la derecha, la Y se expresa hacia arriba y hacia abajo, y Z se expresa hacia delante y hacia atrás. Los valores positivos son derecha, arriba y atrás, respectivamente.

Las coordenadas que muestra XRFrame.getViewerPose() dependen del tipo de espacio de referencia solicitado. Más información sobre eso cuando lleguemos al bucle de marcos. Ahora mismo, tenemos que seleccionar un tipo de referencia que sea apropiado para la realidad aumentada. De nuevo, se usa mi propiedad de conveniencia.

let refSpaceType
function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  if (session.isImmersive) {
    removeBackground();
  }

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(session, gl)
  });

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

Si visitaste la Muestra de sesión de RA envolvente, notarás que, inicialmente, la escena es estática y no es de realidad aumentada. Puedes arrastrar y deslizar el dedo para moverte por la escena. Si haces clic en "COMENZAR RA", el fondo desaparece y puedes mover el dispositivo para moverte por la escena. Los modos usan diferentes tipos de espacios de referencia. El texto destacado anterior muestra cómo se selecciona esta opción. Usa los siguientes tipos de referencia:

local: El origen se encuentra en la posición del usuario en el momento de la creación de la sesión. Esto significa que la experiencia no necesariamente tiene un precio mínimo bien definido, y la posición exacta del origen puede variar según la plataforma. Aunque no hay límites preestablecidos en el espacio, es de esperar que el contenido se pueda ver sin otro movimiento que no sea la rotación. Como puedes ver en nuestro propio ejemplo de RA, es posible que se pueda realizar cierto movimiento dentro del espacio.

viewer: Se usa con mayor frecuencia para el contenido que se presenta intercalado en la página; este espacio sigue al dispositivo de visualización. Cuando se pasa a getViewerPose, no proporciona seguimiento y, por lo tanto, siempre informa una postura en el origen, a menos que la aplicación la modifique con XRReferenceSpace.getOffsetReferenceSpace(). En el ejemplo, se usa esto para habilitar el desplazamiento lateral táctil de la cámara.

Cómo ejecutar un bucle de fotogramas

Conceptualmente, nada cambia con respecto a lo que hice en la sesión de RV descrita en mis artículos anteriores. Pasa el tipo de espacio de referencia a XRFrame.getViewerPose(). El objeto XRViewerPose que se muestre corresponderá al tipo de espacio de referencia actual. Si usas viewer como opción predeterminada, una página puede mostrar vistas previas de contenido antes de que se solicite el consentimiento del usuario para RA o RV. Esto ilustra un punto importante: el contenido intercalado usa el mismo bucle de fotogramas que el contenido envolvente, lo que reduce la cantidad de código que debe mantenerse.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(refSpaceType);
  if (xrViewerPose) {
    // Render based on the pose.
  }
}

Conclusión

En esta serie de artículos, solo se abarcan los aspectos básicos para implementar contenido envolvente en la Web. Las muestras de la API de WebXR Device de Immersive Web Working Group presentan muchas más capacidades y casos de uso. También acabamos de publicar un artículo sobre pruebas de posicionamiento, en el que se explica una API para detectar superficies y colocar elementos virtuales en una vista de cámara real. Échales un vistazo y mira el blog web.dev para ver más artículos durante el próximo año.

Foto de David Grandmougin en Unsplash