Te damos la bienvenida a la Web envolvente

La Web inmersiva implica experiencias de mundo virtual alojadas a través del navegador. Todas estas experiencias de realidad virtual aparecen en el navegador o en visores compatibles con RV.

Joe Medley
Joe Medley

La Web envolvente significa experiencias de mundo virtual alojadas a través del navegador. Esto abarca experiencias de realidad virtual (RV) completas que se muestran en el navegador o en visores compatibles con RV, como Daydream, Oculus Rift de Google, Samsung Gear VR, HTC Vive y los auriculares de realidad mixta de Windows, así como experiencias de realidad aumentada desarrolladas para dispositivos móviles con RA.

Si bien usamos dos términos para describir experiencias inmersivas, deben considerarse como un espectro que va de la realidad completa a un entorno de RV completamente envolvente, con varios niveles de RA en medio.

Estos son algunos ejemplos de experiencias inmersivas:

  • Videos envolventes en 360°
  • Videos tradicionales en 2D (o 3D) presentados en un entorno inmersivo
  • Visualizaciones de datos
  • Compra desde casa
  • Arte
  • Algo interesante en lo que todavía nadie había pensado

¿Cómo llego hasta ahí?

La Web inmersiva está disponible desde hace casi un año en forma embrionaria. Esto se realizó 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, y con polyfills 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 prueba de origen nueva.

¿Qué pasó 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 detallarla, pero incluye problemas como la vinculación explícita de la API con el subproceso principal de JavaScript, demasiadas oportunidades para que los desarrolladores establezcan configuraciones obviamente incorrectas 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 y mejoras importantes de rendimiento. Al mismo tiempo, estaba surgiendo la RA y otros casos de uso, y era importante que la API fuera extensible a admitir esos casos 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 a seguir. 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 anterior, la API de WebXR Device es un producto del 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 significa cualquier cosa en el espectro de experiencias inmersivas. Está disponible en la prueba de origen mencionada anteriormente y mediante un polyfill.

Cuando se publicó originalmente este artículo 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 más información en Realidad aumentada para la Web.

Esta API nueva ofrece mucho más de lo que puedo encontrar en un artículo como este. Quiero brindarte lo suficiente para comenzar 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 envolvente. Ampliaré la última a medida que avance la prueba de origen. Puedes abrir los problemas o enviar solicitudes de extracción.

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

Lo que no trataremos es cómo dibujar contenido de RA y RV en la pantalla. La API de WebXR Device no proporciona funciones de renderización de imágenes. Eso depende de usted. El dibujo se realiza con las APIs de WebGL. Puedes hacerlo si eres realmente ambicioso. Sin embargo, recomendamos usar un framework. Las muestras web envolventes usan una creada solo para las demostraciones llamada Cottontail. Three.js es compatible con WebXR desde mayo. No he oído nada sobre A-Frame.

Cómo iniciar y ejecutar una app

El proceso básico es el siguiente:

  1. Solicitar un dispositivo XR
  2. Si está disponible, solicita una sesión de XR. Si quieres que el usuario se ponga su teléfono en auriculares, se denomina sesión envolvente y 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 apropiado en la pantalla en cada marco.
  4. Ejecuta el bucle de renderización hasta que el usuario decida salir.
  5. Finalizar la sesión XR.

Veamos esto con más detalle e incluyamos un poco de código. No podrás ejecutar una app de lo que te voy a mostrar. Pero, de nuevo, esto es solo para dar una idea.

Solicitar un dispositivo XR

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

Si no usas una sesión envolvente, puedes omitir la publicidad de la funcionalidad, obtener un gesto del usuario y solicitar una sesión. Una sesión inmersiva requiere auriculares. Una sesión no envolvente simplemente muestra contenido en la pantalla del dispositivo. El primero es lo que la mayoría de la gente piensa cuando te refieres a la realidad virtual o la realidad aumentada. Esta última a veces se denomina “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.");
}

Solicitar una sesión de XR

Ahora que tenemos el dispositivo y el 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 para este paso requiere un poco de desenredo. Para desenredarlo, voy a aprovecharte un montón de palabras. Si quieres ver el código final, avanza para verlo rápido y, luego, regresa para obtener una explicación completa. Hay bastantes cantidades que tal vez no puedas inferir.

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

  1. Solicita un marco 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 el proceso 60 veces por segundo hasta que el usuario decida salir.

Solicita un marco de presentación

La palabra “marco” tiene varios significados en un contexto de Web XR. La primera 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 se mueve el dispositivo. (¿La vista permanece igual cuando el usuario se mueve o cambia como lo haría en la vida real?)

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

Antes de darte más información para procesar, te ofreceré algún código. En el siguiente ejemplo, se muestra cómo se inicia y mantiene el bucle de renderización. Observa el uso doble de la palabra marco. Y 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 necesitas acceso a ella. En general, la posición y la orientación de un objeto en RA/RV se llaman pose. Tanto los visores como los dispositivos de entrada tienen una postura. (hablaremos sobre los dispositivos de entrada más adelante). Tanto las posturas del visualizador como las del dispositivo de entrada se definen como una matriz de 4 por 4 almacenada en un Float32Array en el orden principal de la columna. Para obtener la pose del usuario, llama a XRFrame.getDevicePose() en el objeto de marco de animación actual. Siempre prueba para ver si recuperaste la pose. Si algo salió mal, no debes dibujar en la pantalla.

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

Vistas

Después de revisar la pose, es hora de dibujar algo. El objeto al que dibujas se denomina 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 envolvente, el array tiene una vista. Si estás en una sesión envolvente, 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 inmersivos. Aunque 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 comparto todo esto, obtendré el código que aparece a continuación. Dejé un marcador de posición para los dispositivos de entrada, que trataré en una sección posterior.

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);
    }
}

Finalizar la sesión XR

Una sesión XR 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 de finalización y, cuando ocurre, descartar la sesión y los objetos del procesador. Una sesión XR una vez finalizada no se puede reanudar.

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 voy a darte una idea de cómo interactuar con objetos en RA o RV.

La API de WebXR Device adopta un enfoque de "apuntar y hacer clic" para las entradas del usuario. Con este enfoque, cada fuente de entrada tiene un rayo de puntero definido para indicar hacia dónde apunta un dispositivo de entrada y eventos para indicar cuándo se seleccionó un elemento. 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 eventos, específicamente select, selectStart y selectEnd. Tu app determina en qué se hizo clic y responde de forma adecuada.

El dispositivo de entrada y el rayo del puntero

Para los usuarios, el rayo puntero es solo una línea tenue entre el control y lo que sea hacia lo que apunta. Pero tu aplicación tiene que dibujarlo. Eso significa adoptar la pose del dispositivo de entrada y dibujar una línea desde su ubicación hasta un objeto en el espacio de RA/RV. El proceso es similar al siguiente:

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 reducida de la muestra de seguimiento de entradas del grupo comunitario de Immersive Web. Al igual que con la renderización de fotogramas, dibujar el rayo de puntero y el dispositivo depende de ti. Como se mencionó anteriormente, este código debe ejecutarse como parte del bucle de renderización.

Selección de elementos en el espacio virtual

El simple hecho de señalar elementos en RA y RV es bastante inútil. Para hacer algo útil, los usuarios necesitan poder seleccionar elementos. La API de WebXR Device proporciona tres eventos para responder a las interacciones del usuario: select, selectStart y selectEnd. Tiene una peculiaridad que no esperaba: solo te dice que se hizo clic en un dispositivo de entrada. No indican en qué elemento del entorno se hizo clic. Los controladores de eventos se agregan al objeto XRSession y deben agregarse en cuanto 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 a qué se hizo clic, utiliza una pose. (¿Te sorprenden? no lo pensé). Los detalles son específicos de tu app o cualquier framework que uses y, por lo tanto, están fuera del alcance de este artículo. El enfoque de Cola de algodón 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, la realidad aumentada está disponible en Chrome 69 (Canary en junio de 2018). Sin embargo, te animo a que pruebes lo que tenemos hasta ahora. Necesitamos comentarios para mejorarlo. Mira ChromeStatus.com para WebXR Hit Test y sigue su progreso. También puedes seguir las WebXR Anchors, que mejorarán el seguimiento de la postura.