La realidad virtual llega a la Web

Te presentamos algunos conceptos básicos que te prepararán para una variedad de experiencias envolventes: realidad virtual, realidad aumentada y mucho más.

Joe Medley
Joe Medley

Las experiencias inmersivas llegaron a la Web en Chrome 79. La API de WebXR Device lleva la realidad virtual a la realidad virtual, mientras que la compatibilidad con la realidad aumentada llega en Chrome 81. Aunque una actualización de la API de GamePad extiende el uso avanzado de controles a la RV. Otros navegadores serán compatibles con estas especificaciones muy pronto, como Firefox Reality, Oculus Browser, Edge y Helio de Magic Leap, entre otros.

En este artículo, se inicia una serie sobre la Web interactiva. Esta cuota abarca la configuración de una aplicación WebXR básica, así como el ingreso a una sesión de XR y la salida de ella. Los artículos posteriores cubrirán el bucle de fotogramas (el caballo de trabajo de la experiencia de WebXR), los detalles de la realidad aumentada y la API de WebXR Hit Test, un medio para detectar superficies en una sesión de RA. A menos que se indique lo contrario, todo lo que abordamos en este artículo y en los siguientes artículos se aplica de la misma manera tanto a la RA como a la RV.

¿Qué es la Web envolvente?

Si bien usamos dos términos para describir las experiencias inmersivas (realidad aumentada y realidad virtual), muchas las consideran un espectro que va desde la realidad completa a la completamente virtual, con grados de inmersión intermedios. La "X" en XR pretende reflejar ese pensamiento por medio de una especie de variable algebraica que significa cualquier cosa en el espectro de experiencias inmersivas.

Gráfico que ilustra el espectro de las experiencias visuales desde la realidad completa hasta la inmersión completa.
El espectro de las experiencias envolventes

Estos son algunos ejemplos de experiencias inmersivas:

  • Juegos
  • Videos panorámicos de 360º
  • Videos tradicionales en 2D (o 3D) presentados en un entorno inmersivo
  • Compra de vivienda
  • Ver productos en tu casa antes de comprarlos
  • Arte envolvente
  • Algo interesante en lo que todavía nadie había pensado

Conceptos y uso

Explicaré algunos conceptos básicos del uso de la API de WebXR Device. Si necesitas más información de la que indiqué, consulta las muestras de WebXR del Immersive Web Working Group o los materiales de referencia de aumento de MDN. Si conoces las primeras versiones de la API de WebXR Device, deberías consultar todo este material. Hubo cambios.

El código de este artículo se basa en el ejemplo básico del Immersive Web Working Group (demostración, fuente), pero se edita para mayor claridad y simplicidad.

Parte de la creación de la especificación WebXR fue ampliar las medidas de seguridad y privacidad para proteger a los usuarios. Por lo tanto, las implementaciones deben cumplir con ciertos requisitos. Una página web o app debe estar activa y enfocada antes de que pueda solicitar información sensible al visualizador. Las páginas o apps web deben entregarse a través de HTTPS. La API está diseñada para proteger la información que se obtiene de sensores y cámaras, que necesita para funcionar.

Solicita una sesión

El ingreso a una sesión XR requiere un gesto del usuario. Para ello, usa la detección de funciones y prueba XRSystem (mediante navigator.xr) y haz una llamada a XRSystem.isSessionSupported(). Ten en cuenta que en las versiones 79 y 80 de Chrome, el objeto XRSystem se llamaba XR.

En el siguiente ejemplo, indiqué que quiero una sesión de realidad virtual con el tipo de sesión 'immersive-vr'. Los otros tipos de sesión son 'immersive-ar' y 'inline'. Una sesión intercalada se usa para presentar contenido dentro de HTML y se usa principalmente para contenido de avance. En el ejemplo de Immersive AR Session, se muestra esto. Explicaré eso en un artículo posterior.

Una vez que sé que se admiten las sesiones de realidad virtual, habilito un botón que me permite adquirir un gesto del usuario.

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

Después de habilitar el botón, espero que aparezca un evento de clic y, luego, solicito una sesión.

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

Observa la jerarquía de objetos en este código. Pasa de navigator a xr y, luego, a una instancia XRSession. En las primeras versiones de la API, una secuencia de comandos tenía que solicitar un dispositivo antes de solicitar una sesión. Ahora, el dispositivo se adquiere de forma implícita.

Ingresar a una sesión

Después de obtener una sesión, debo iniciarla e ingresarla. Pero primero, necesito configurar algunas cosas. Una sesión necesita un controlador de eventos onend para que la app o la página web se puedan restablecer cuando se cierra el usuario.

También necesitaré un elemento <canvas> para dibujar mi escena. Debe ser un elemento WebGLRenderingContext o WebGL2RenderingContext compatible con XR. Todo el dibujo se realiza con ellos o con un framework basado en WebGL, como Three.js.

Ahora que tengo un lugar para dibujar, necesito una fuente de contenido para dibujar. Para eso, creo una instancia de XRWebGLLayer. Lo asocio con el lienzo llamando a XRSession.updateRenderState().

Una vez que estoy en una sesión, necesito una forma de determinar dónde están las cosas en realidad virtual. Necesitaré un espacio de referencia. Un espacio de referencia 'local-floor' es aquel en el que el origen se encuentra cerca del usuario, y el eje Y es 0 a nivel del piso y no se espera que se mueva. Existen otros tipos de espacios de referencia, pero ese es un tema más complicado del que puedo analizar aquí. Guardo el espacio de referencia en una variable porque lo necesitaré cuando dibuje en la pantalla.

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

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

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

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

Después de obtener un espacio de referencia, llamo a XRSession.requestAnimationFrame(). Este es el inicio de la presentación de contenido virtual, que se realiza en el bucle de fotogramas.

Ejecuta un bucle de fotogramas

El bucle de fotogramas es un bucle infinito controlado por el usuario-agente en el que el contenido se dibuja en la pantalla de forma reiterada. El contenido se dibuja en bloques discretos llamados marcos. La sucesión de marcos crea la ilusión del movimiento. Para las aplicaciones de RV, los fotogramas por segundo pueden ser de entre 60 y 144. La RA para Android se ejecuta a 30 fotogramas por segundo. Tu código no debe asumir una velocidad de fotogramas en particular.

El proceso básico para el bucle de fotogramas es el siguiente:

  1. Llamar a XRSession.requestAnimationFrame() En respuesta, el usuario-agente invoca el XRFrameRequestCallback, que definiste tú.
  2. Dentro de la función de devolución de llamada:
    1. Vuelve a llamar a XRSession.requestAnimationFrame().
    2. Toma la pose del espectador.
    3. Pasa (vincula) el WebGLFramebuffer de XRWebGLLayer a WebGLRenderingContext.
    4. Itera en cada objeto XRView, recupera su XRViewport de XRWebGLLayer y pásalo a WebGLRenderingContext.
    5. Dibuja algo en el búfer de fotogramas.

En el resto de este artículo, se describe el paso 1 y la parte del paso 2, cómo configurar y llamar al XRFrameRequestCallback. Los elementos restantes del paso 2 se abordan en la Parte II.

La XRFrameRequestCallback

Tú defines el XRFrameRequestCallback. Requiere dos parámetros: una instancia de DOMHighResTimeStamp y una de XRFrame. El objeto XRFrame proporciona la información necesaria para renderizar un solo fotograma en la pantalla. El argumento DOMHighResTimeStamp es para uso futuro.

Antes de hacer cualquier otra cosa, voy a solicitar el siguiente fotograma de animación. Como se indicó anteriormente, el usuario-agente determina la latencia de los fotogramas en función del hardware subyacente. Solicitar el siguiente fotograma primero garantiza que el bucle de fotogramas continúe si algo durante la devolución de llamada genera un error.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  // Render a frame.
}

A esta altura, es hora de dibujar algo para el usuario. Ese es un debate para la parte II. Antes de pasar ahí, déjame mostrarte cómo finalizar una sesión.

Cómo finalizar la sesión

Una sesión inmersiva puede finalizar por varios motivos, como finalizar con tu propio código a través de una llamada a XRSession.end(). Otras causas son que los auriculares se desconecten o que otra aplicación los controle. Es por eso que una aplicación con buen comportamiento debe supervisar el evento end. Cuando ocurra, descarta la sesión y los objetos de renderización relacionados. No se puede reanudar una sesión envolvente finalizada. Para volver a ingresar a la experiencia envolvente, mi app debe iniciar una sesión nueva.

Recuerda de Cómo ingresar a una sesión que, durante la configuración, agregué un controlador de eventos onend.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);
  // More setup…
}

Dentro del controlador de eventos, restablece el estado de la app antes de que el usuario ingresara a una sesión.

function onSessionEnded(event) {
  xrSession = null;
  xrButton.textContent = 'Enter VR';
}

Conclusión

No expliqué todo lo que necesitas para escribir una aplicación Web XR o AR. Espero que te dimos lo suficiente para que empieces a comprender el código por ti mismo y a experimentar. En el siguiente artículo, explicaré el bucle de fotogramas, que es donde el contenido se dibuja en la pantalla.

Foto de JESHOOTS.COM en Unsplash