La realidad virtual llega a la Web

Algunos conceptos básicos para prepararte para un espectro de experiencias envolventes: realidad virtual, realidad aumentada y todo lo que hay en el medio.

Joe Medley
Joe Medley

Las experiencias envolventes llegaron a la Web en Chrome 79. La API de WebXR Device brinda la realidad virtual, mientras que la compatibilidad con la realidad aumentada llega a Chrome 81. Mientras que una actualización de la API de GamePad extiende el uso avanzado de los controles a la VR. Pronto, otros navegadores admitirán estas especificaciones, como Firefox Reality, Oculus Browser, Edge y el navegador Helio de Magic Leap, entre otros.

Este artículo inicia una serie sobre la Web envolvente. En esta entrega, se explica cómo configurar una aplicación básica de WebXR, así como ingresar y salir de una sesión de XR. En artículos posteriores, se abordará el bucle de fotogramas (el caballo de batalla de la experiencia de WebXR), los detalles de la realidad aumentada y la API de Hit Test de WebXR, un medio para detectar superficies en una sesión de RA. A menos que se indique lo contrario, todo lo que explico en este artículo y en los siguientes se aplica por igual a la RA y a la RV.

¿Qué es la Web inmersiva?

Si bien usamos dos términos para describir las experiencias envolventes, la realidad aumentada y la realidad virtual, muchos piensan en ellas como un espectro que va de la realidad completa a la completamente virtual, con grados de inmersión en el medio. La "X" en la XR tiene como objetivo reflejar ese pensamiento, ya que es una especie de variable algebraica que representa cualquier cosa en el espectro de experiencias envolventes.

Gráfico que ilustra el espectro de experiencias visuales, desde la realidad completa hasta la completamente envolvente.
El espectro de experiencias envolventes

Estos son algunos ejemplos de experiencias envolventes:

  • Videojuegos
  • videos panorámicos de 360º
  • Videos tradicionales en 2D (o 3D) presentados en entornos envolventes
  • Compra de casa
  • Ver productos en tu casa antes de comprarlos
  • Arte envolvente
  • Algo genial que nadie haya pensado antes

Conceptos y uso

Te explicaré algunos conceptos básicos del uso de la API de WebXR Device. Si necesitas más detalles que los que te proporcioné, consulta los ejemplos de WebXR del grupo de trabajo de la Web Imersiva o los materiales de referencia en constante expansión de MDN. Si conoces las versiones anteriores de la API de WebXR Device, deberías revisar todo este material. Hubo cambios.

El código de este artículo se basa en la muestra básica del grupo de trabajo de la Web Imersiva (demo, fuente), pero se editó para que sea más claro y sencillo.

Parte de la creación de la especificación de WebXR consistió en definir 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 aplicación debe estar activa y enfocada antes de poder solicitarle al usuario información sensible. Las páginas web o las apps deben entregarse a través de HTTPS. La API en sí está diseñada para proteger la información obtenida de los sensores y las cámaras, que necesita para funcionar.

Cómo solicitar una sesión

Para ingresar a una sesión de XR, se requiere un gesto del usuario. Para obtener eso, usa la detección de atributos para probar XRSystem (a través de 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 adelanto. En la muestra de sesión de RA envolvente, se demuestra esto. Lo explicaré en un artículo posterior.

Una vez que sé que se admiten 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 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. Se mueve de navigator a xr a una instancia de XRSession. En las primeras versiones de la API, una secuencia de comandos debía solicitar un dispositivo antes de solicitar una sesión. Ahora, el dispositivo se adquiere de forma implícita.

Ingresa a una sesión

Después de obtener una sesión, debo iniciarla y acceder a ella. Pero primero, debo configurar algunos aspectos. Una sesión necesita un controlador de eventos onend para que la app o la página web se puedan restablecer cuando el usuario salga.

También necesitaré un elemento <canvas> para dibujar mi escena. Debe ser un WebGLRenderingContext o un 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 en él. Para ello, creo una instancia de XRWebGLLayer. Lo associo con el lienzo llamando a XRSession.updateRenderState().

Una vez que estoy en una sesión, necesito una forma de determinar dónde están los elementos en la realidad virtual. Necesito un espacio de referencia. Un espacio de referencia 'local-floor' es uno en el que el origen se encuentra cerca del usuario, el eje y es 0 en el 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 no puedo hablar 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 comienzo 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 de forma repetida en la pantalla. El contenido se dibuja en bloques discretos llamados marcos. La sucesión de fotogramas crea la ilusión de movimiento. En el caso de las aplicaciones de VR, los fotogramas por segundo pueden variar de 60 a 144. La RA para Android se ejecuta a 30 fotogramas por segundo. Tu código no debe suponer ninguna velocidad de fotogramas en particular.

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

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

En el resto de este artículo, se describe el paso 1 y parte del paso 2, la configuración y la llamada a XRFrameRequestCallback. Los elementos restantes del paso 2 se abordan en la parte II.

XRFrameRequestCallback

Tú defines el XRFrameRequestCallback. Toma dos parámetros: un DOMHighResTimeStamp y una instancia 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, solicitaré el siguiente fotograma de animación. Como se mencionó anteriormente, el usuario-agente determina el tiempo de los fotogramas según el hardware subyacente. Solicitar primero el siguiente fotograma garantiza que el bucle de fotogramas continúe si se produce un error durante la devolución de llamada.

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

En este punto, es hora de dibujar algo para el usuario. Esa es una discusión para la parte II. Antes de ir allí, te mostraré cómo finalizar una sesión.

Finaliza la sesión

Una sesión envolvente puede finalizar por varios motivos, incluido el final de tu código a través de una llamada a XRSession.end(). Otras causas incluyen que los auriculares estén desconectados o que otra aplicación tome el control de ellos. Por este motivo, una aplicación que se comporte bien debe supervisar el evento end. Cuando esto suceda, descarta la sesión y sus 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 que, en Cómo ingresar a una sesión, 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 haya ingresado a una sesión.

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

Conclusión

No te expliqué todo lo que necesitas para escribir una aplicación de XR o RA web. Espero haberte dado suficiente información para que comiences a comprender el código por tu cuenta y para que comiences a experimentar. En el siguiente artículo, explicaré el bucle de fotogramas, que es donde se dibuja el contenido en la pantalla.

Foto de JESHOOTS.COM en Unsplash