Te damos la bienvenida a la Web envolvente

La Web inmersiva se refiere a experiencias de mundos virtuales alojados a través del navegador. Todas estas experiencias de realidad virtual aparecían en el navegador o en visores compatibles con RV.

Joe Medley
Joe Medley

La Web envolvente implica experiencias de mundos virtuales alojadas a través del navegador. Esto abarca las experiencias de realidad virtual (RV) completas que aparecen en el navegador o en visores compatibles con RV, como Daydream de Google, Oculus Rift, Samsung Gear VR, HTC Vive y Windows Mixed Reality Headsets, así como las experiencias de realidad aumentada desarrolladas para dispositivos móviles compatibles con RA.

Si bien usamos dos términos para describir las experiencias envolventes, se deben considerar como un espectro que va desde la realidad completa hasta un entorno de RA completamente envolvente, con varios niveles de RA en el medio.

Estos son algunos ejemplos de experiencias envolventes:

  • Videos envolventes en 360°
  • Videos tradicionales en 2D (o 3D) presentados en entornos envolventes
  • Visualizaciones de datos
  • Compras en casa
  • Arte
  • Algo genial que nadie haya pensado antes

¿Cómo llego hasta ahí?

La Web inmersiva lleva casi un año disponible en forma embrionaria. Esto se hizo a través de la API de WebVR 1.1, que está disponible en una prueba de origen desde Chrome 62. Esa API también es compatible con Firefox y Edge, así como con un polyfill para Safari.

Pero es hora de seguir adelante.

La prueba de origen finalizó el 24 de julio de 2018, y la especificación se reemplazó por la API de WebXR Device y una nueva prueba de origen.

¿Qué sucedió con WebVR 1.1?

Aprendimos mucho de WebVR 1.1, pero con el tiempo, quedó claro que se necesitaban algunos cambios importantes para admitir los tipos de aplicaciones que los desarrolladores quieren compilar. La lista completa de lecciones aprendidas es demasiado larga para explicarla aquí, pero incluye problemas como que la API esté vinculada de forma explícita al subproceso principal de JavaScript, demasiados oportunidades para que los desarrolladores configuren parámetros de configuración claramente incorrectos, y usos comunes, como que la ventana mágica sea un efecto secundario en lugar de una función intencional. (La ventana mágica es una técnica para ver contenido envolvente sin auriculares, en la que la app renderiza una sola vista según el sensor de orientación del dispositivo).

El nuevo diseño facilita implementaciones más simples y grandes mejoras de rendimiento. Al mismo tiempo, surgieron la RA y otros casos de uso, y se volvió importante que la API fuera extensible para admitirlos en el futuro.

La API de WebXR Device se diseñó y nombró teniendo en cuenta estos casos de uso expandidos, y proporciona una mejor ruta de acceso. Los implementadores de WebVR se comprometieron a migrar a la API de WebXR Device.

¿Qué es la API de WebXR Device?

Al igual que la especificación de WebVR, la API de WebXR Device es un producto de Immersive Web Community Group, que cuenta con colaboradores de Google, Microsoft, Mozilla y otros. La "X en XR" está diseñada como una especie de variable algebraica que representa cualquier cosa en el espectro de experiencias inmersivas. Está disponible en la prueba de origen mencionada anteriormente, así como a través de un polyfill.

Cuando se publicó este artículo originalmente durante el período de la versión beta de Chrome 67, solo se habilitaron las funciones de RV. La realidad aumentada llegó a Chrome 69. Obtén información al respecto en Realidad aumentada para la Web.

Esta nueva API tiene más funciones de las que puedo explicar en un artículo como este. Quiero darte suficiente información para que comiences a comprender las muestras de WebXR. Puedes encontrar más información en la explicación original y en nuestra Guía para usuarios pioneros de la Web Imersiva. Ampliaré la segunda a medida que avance la prueba de origen. No dudes en abrir problemas o enviar solicitudes de extracción.

En este artículo, hablaré sobre cómo iniciar, detener y ejecutar una sesión de XR, además de algunos conceptos básicos sobre el procesamiento de entradas.

No explicaré cómo dibujar contenido de RA/RV en la pantalla. La API de WebXR Device no proporciona funciones de renderización de imágenes. Eso depende de ti. El dibujo se realiza con las APIs de WebGL. Puedes hacerlo si tienes mucha ambición. Sin embargo, te recomendamos que uses un framework. Los ejemplos de la Web inmersiva usan uno que se creó solo para las demostraciones, llamado Cottontail. Three.js admite WebXR desde mayo. No he escuchado nada sobre A-Frame.

Cómo iniciar y ejecutar una app

El proceso básico es el siguiente:

  1. Solicita un dispositivo de RA.
  2. Si está disponible, solicita una sesión de XR. Si quieres que el usuario coloque su teléfono en auriculares, se denomina sesión envolvente y se requiere un gesto del usuario para ingresar.
  3. Usa la sesión para ejecutar un bucle de renderización que proporcione 60 fotogramas de imagen por segundo. Dibuja el contenido adecuado en la pantalla en cada fotograma.
  4. Ejecuta el bucle de renderización hasta que el usuario decida salir.
  5. Finaliza la sesión de XR.

Veamos esto con más detalle y, luego, incluiremos un poco de código. No podrás ejecutar una app con lo que te mostraré a continuación. Pero, de nuevo, esto es solo para darle una idea.

Solicita un dispositivo XR

Aquí, reconocerás el código de detección de características estándar. Puedes unir esto en una función llamada checkForXR().

Si no usas una sesión envolvente, puedes omitir la publicidad de la funcionalidad y obtener un gesto del usuario para ir directamente a solicitar una sesión. Una sesión envolvente es aquella que requiere auriculares. Una sesión no inmersiva solo muestra contenido en la pantalla del dispositivo. La primera es lo que la mayoría de las personas piensan cuando se hace referencia a la realidad virtual o a la realidad aumentada. Esta última se denomina a veces "ventana mágica".

if (navigator.xr) {
    navigator.xr.requestDevice()
    .then(xrDevice => {
    // Advertise the AR/VR functionality to get a user gesture.
    })
    .catch(err => {
    if (err.name === 'NotFoundError') {
        // No XRDevices available.
        console.error('No XR devices available:', err);
    } else {
        // An error occurred while requesting an XRDevice.
        console.error('Requesting XR device failed:', err);
    }
    })
} else{
    console.log("This browser does not support the WebXR API.");
}

Cómo solicitar una sesión de XR

Ahora que tenemos nuestro dispositivo y nuestro gesto del usuario, es hora de obtener una sesión. Para crear una sesión, el navegador necesita un lienzo en el que dibujar.

xrPresentationContext = htmlCanvasElement.getContext('xrpresent');
let sessionOptions = {
    // The immersive option is optional for non-immersive sessions; the value
    //   defaults to false.
    immersive: false,
    outputContext: xrPresentationContext
}
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
    // Use a WebGL context as a base layer.
    xrSession.baseLayer = new XRWebGLLayer(session, gl);
    // Start the render loop
})

Ejecuta el bucle de renderización

El código de este paso requiere un poco de desenredo. Para desenredarlo, te voy a decir muchas palabras. Si quieres echar un vistazo al código final, ve al final para verlo rápidamente y, luego, vuelve para obtener la explicación completa. Es posible que no puedas inferir mucho.

El proceso básico de un bucle de renderización es el siguiente:

  1. Solicita un fotograma de animación.
  2. Consulta la posición del dispositivo.
  3. Dibuja contenido en la posición del dispositivo según su posición.
  4. Realiza el trabajo necesario para los dispositivos de entrada.
  5. Repite 60 veces por segundo hasta que el usuario decida salir.

Cómo solicitar un marco de presentación

La palabra "marco" tiene varios significados en un contexto de XR web. El primero es el marco de referencia, que define desde dónde se calcula el origen del sistema de coordenadas y qué sucede con ese origen cuando el dispositivo se mueve. (¿La vista se mantiene igual cuando el usuario se mueve o cambia como lo haría en la vida real?)

El segundo tipo es el marco de presentación, representado por un objeto XRFrame. Este objeto contiene la información necesaria para renderizar en el dispositivo un solo fotograma de una escena de RA/RV. Esto es un poco confuso porque se recupera un marco de presentación llamando a requestAnimationFrame(). Esto lo hace compatible con window.requestAnimationFrame().

Antes de darte más información, te brindaré un poco de código. En el siguiente ejemplo, se muestra cómo se inicia y se mantiene el bucle de renderización. Observa el doble uso del marco de palabras. Observa la llamada recursiva a requestAnimationFrame(). Se llamará a esta función 60 veces por segundo.

xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
    xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
    // The time argument is for future use and not implemented at this time.
    // Process the frame.
    xrFrame.session.requestAnimationFrame(onFrame);
    }
});

Poses

Antes de dibujar algo en la pantalla, debes saber hacia dónde apunta el dispositivo de visualización y debes tener acceso a la pantalla. En general, la posición y orientación de un objeto en la RA/VR se denomina pose. Tanto los visores como los dispositivos de entrada tienen una pose. (más adelante hablaremos sobre los dispositivos de entrada). Las posiciones del visor y del dispositivo de entrada se definen como una matriz de 4 por 4 almacenada en un Float32Array en orden mayor de columna. Para obtener la pose del usuario, llama a XRFrame.getDevicePose() en el objeto de fotograma de animación actual. Prueba siempre si tu postura es favorable. Si algo sale mal, no quieres dibujar en la pantalla.

let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
    // Draw something to the screen.
}

Vistas

Después de comprobar la pose, es hora de dibujar algo. El objeto que dibujas se llama vista (XRView). Aquí es donde el tipo de sesión se vuelve importante. Las vistas se recuperan del objeto XRFrame como un array. Si estás en una sesión no inmersiva, el array tiene una vista. Si estás en una sesión immersiva, el array tiene dos, uno para cada ojo.

for (let view of xrFrame.views) {
    // Draw something to the screen.
}

Esta es una diferencia importante entre WebXR y otros sistemas envolventes. Si bien puede parecer inútil iterar a través de una vista, hacerlo te permite tener una sola ruta de renderización para una variedad de dispositivos.

Todo el bucle de renderización

Si junto todo esto, obtengo el siguiente código. Dejé un marcador de posición para los dispositivos de entrada, que analizaremos más adelante.

xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
    xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
    // The time argument is for future use and not implemented at this time.
    let pose = xrFrame.getDevicePose(xrFrameOfRef);
    if (pose) {
        for (let view of xrFrame.views) {
        // Draw something to the screen.
        }
    }
    // Input device code will go here.
    frame.session.requestAnimationFrame(onFrame);
    }
}

Finaliza la sesión de XR

Una sesión de XR puede finalizar por varios motivos, como finalizar con tu propio código a través de una llamada a XRSession.end(). Otras causas incluyen que los auriculares se desconecten o que otra aplicación los controle. Esta es la razón por la que una aplicación que tiene un buen comportamiento debe supervisar el evento final y, cuando ocurre, descartar la sesión y los objetos del renderizador. No se puede reanudar una sesión de XR una vez finalizada.

xrDevice.requestSession(sessionOptions)
.then(xrSession => {
    // Create a WebGL layer and initialize the render loop.
    xrSession.addEventListener('end', onSessionEnd);
});

// Restore the page to normal after immersive access has been released.
function onSessionEnd() {
    xrSession = null;

    // Ending the session stops executing callbacks passed to the XRSession's
    // requestAnimationFrame(). To continue rendering, use the window's
    // requestAnimationFrame() function.
    window.requestAnimationFrame(onDrawFrame);
}

¿Cómo funciona la interacción?

Al igual que con el ciclo de vida de la aplicación, solo te daré una idea de cómo interactuar con objetos en RA o RV.

La API de WebXR Device adopta un enfoque “señalar y hacer clic” en la entrada del usuario. Con este enfoque, cada fuente de entrada tiene un rayo del puntero definido para indicar hacia dónde apunta un dispositivo de entrada y eventos para indicar cuándo se seleccionó algo. Tu app dibuja el rayo del puntero y muestra hacia dónde apunta. Cuando el usuario hace clic en el dispositivo de entrada, se activan los eventos select, selectStart y selectEnd, específicamente. Tu app determina en qué se hizo clic y responde de manera adecuada.

El dispositivo de entrada y el rayo del puntero

Para los usuarios, el rayo del puntero es solo una débil línea entre el control y lo que sea que apunte. Sin embargo, tu app debe dibujarlo. Eso significa obtener la posición del dispositivo de entrada y dibujar una línea desde su ubicación hasta un objeto en el espacio de RA/VR. Ese proceso se ve de la siguiente manera:

let inputSources = xrSession.getInputSources();
for (let xrInputSource of inputSources) {
    let inputPose = frame.getInputPose(inputSource, xrFrameOfRef);
    if (!inputPose) {
    continue;
    }
    if (inputPose.gripMatrix) {
    // Render a virtual version of the input device
    //   at the correct position and orientation.
    }
    if (inputPose.pointerMatrix) {
    // Draw a ray from the gripMatrix to the pointerMatrix.
    }
}

Esta es una versión simplificada de la muestra del seguimiento de entradas del grupo de Immersive Web Community Group. Al igual que con la renderización de tramas, depende de ti dibujar el rayo del puntero y el dispositivo. Como se mencionó antes, este código se debe ejecutar como parte del bucle de renderización.

Selecciona elementos en el espacio virtual

Hacer referencia a elementos en RA y RV es bastante inútil. Para hacer algo útil, los usuarios deben poder seleccionar cosas. La API de WebXR Device proporciona tres eventos para responder a las interacciones del usuario: select, selectStart y selectEnd. Tienen un inconveniente que no esperaba: solo te indican que se hizo clic en un dispositivo de entrada. No te indican en qué elemento del entorno se hizo clic. Los controladores de eventos se agregan al objeto XRSession y deben agregarse apenas esté disponible.

xrDevice.requestSession(sessionOptions)
.then(xrSession => {
    // Create a WebGL layer and initialize the render loop.
    xrSession.addEventListener('selectstart', onSelectStart);
    xrSession.addEventListener('selectend', onSelectEnd);
    xrSession.addEventListener('select', onSelect);
});

Este código se basa en un ejemplo de selección de entrada, en caso de que necesites más contexto.

Para averiguar en qué se hizo clic, usa una pose. (¿Te sorprende? No lo creo.) Los detalles son específicos de tu app o cualquier framework que uses y, por lo tanto, exceden el alcance de este artículo. El enfoque de Cottontail se encuentra en el ejemplo de selección de entrada.

function onSelect(ev) {
    let inputPose = ev.frame.getInputPose(ev.inputSource, xrFrameOfRef);
    if (!inputPose) {
    return;
    }
    if (inputPose.pointerMatrix) {
    // Figure out what was clicked and respond.
    }
}

Conclusión: De cara al futuro

Como dije antes, se espera que la realidad aumentada esté disponible en Chrome 69 (Canary en algún momento de junio de 2018). Sin embargo, te recomiendo que pruebes lo que tenemos hasta ahora. Necesitamos comentarios para mejorarlo. Para seguir su progreso, consulta WebXR Hit Test en ChromeStatus.com. También puedes seguir las anclas de WebXR, que mejorarán el seguimiento de poses.