Te damos la bienvenida a la Web envolvente

La Web inmersiva se refiere a las experiencias de mundos virtuales alojadas en el navegador. Toda esta experiencia de realidad virtual se muestra en el navegador o en visores compatibles con RV.

Joe Medley
Joe Medley

La Web inmersiva se refiere a las experiencias de mundos virtuales alojadas en el navegador. Esto abarca experiencias completas de realidad virtual (RV) que se muestran en el navegador o en visores compatibles con RV, como Daydream de Google, Oculus Rift, Samsung Gear VR, HTC Vive y visores de realidad mixta de Windows, así como experiencias de realidad aumentada desarrolladas para dispositivos móviles compatibles con RA.

Aunque 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 RV 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 para el hogar
  • Arte
  • Algo genial en lo que nadie haya pensado aún

¿Cómo llego hasta ahí?

La Web envolvente está disponible desde hace casi un año en su 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 incluirla aquí, pero incluye problemas como que la API esté vinculada de forma explícita al 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 un visor, 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 sencillas 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 un mejor camino para el futuro. 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 se usa como una especie de variable algebraica que representa cualquier elemento del espectro de experiencias inmersivas. Está disponible en la prueba de origen mencionada anteriormente, así como a través de un polyfill.

Cuando este artículo se publicó originalmente durante el período de la versión beta de Chrome 67, solo se habilitaron las funciones de RV. La realidad aumentada llegó en Chrome 69. Obtén más información 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 lo suficiente 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 inmersiva. Ampliaré esta última a medida que avance la prueba de origen. No dudes en abrir problemas o enviar solicitudes de extracción.

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

No abordaremos cómo dibujar contenido de RA o 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, recomendamos usar un framework. Las muestras de la Web envolvente usan una creada solo para las demostraciones, llamada Cottontail. Three.js es compatible con WebXR desde mayo. No escuché nada sobre A-Frame.

Cómo iniciar y ejecutar una app

El proceso básico es el siguiente:

  1. Solicita un dispositivo de RE.
  2. Si está disponible, solicita una sesión de RE. Si quieres que el usuario coloque su teléfono en un visor, 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 contenido apropiado 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 RE.

Analicemos esto con un poco más de detalle y veamos algo de código. No podrás ejecutar una app a partir de lo que te mostraré. Pero, de nuevo, esto es solo para dar una idea.

Solicita un dispositivo de RE

Aquí, reconocerás el código de detección de funciones estándar. Podrías 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, y pasar directamente a solicitar una sesión. Una sesión envolvente es aquella que requiere un visor. Una sesión no envolvente simplemente muestra contenido en la pantalla del dispositivo. La primera es en la que piensa la mayoría de las personas cuando se habla de realidad virtual o realidad aumentada. A veces, esta última 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.");
}

Solicita una sesión de XR

Ahora que tenemos nuestro 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 de este paso requiere un poco de explicación. Para desenredarlo, te voy a lanzar un montón de palabras. Si quieres ver el código final, avanza para echar un vistazo rápido y, luego, regresa para obtener la explicación completa. Hay bastantes cosas 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 "fotograma" tiene varios significados en un contexto de WebXR. 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 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 fotograma es el fotograma 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 o RV en el dispositivo. Esto es un poco confuso, ya que se recupera un fotograma de presentación llamando a requestAnimationFrame(). Esto lo hace compatible con window.requestAnimationFrame().

Antes de darte más información para asimilar, te ofreceré 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 uso doble de la palabra fotograma. 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 la pantalla. En general, la posición y la orientación de un objeto en RA/RV se denomina pose. Tanto los visores como los dispositivos de entrada tienen una pose. (Más adelante, hablaré sobre los dispositivos de entrada). Las poses del visor y del dispositivo de entrada se definen como una matriz de 4 por 4 almacenada en un Float32Array en orden de columna principal. Para obtener la pose del usuario, llama a XRFrame.getDevicePose() en el objeto de fotograma de animación actual. Siempre realiza pruebas para ver si obtuviste una pose. Si algo salió mal, no querrás dibujar en la pantalla.

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

Vistas

Después de verificar la pose, es hora de dibujar algo. El objeto en el 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 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 envolventes. 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 junto todo esto, obtengo el siguiente código. Dejé un marcador de posición para los dispositivos de entrada, que abordaré 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);
    }
}

Finaliza la sesión de RE

Una sesión de XR puede finalizar por varios motivos, incluido el cierre por tu propio código a través de una llamada a XRSession.end(). Otras causas incluyen que se desconecten los auriculares o que otra aplicación tome el control de ellos. Por este motivo, una aplicación que se comporte de forma correcta debe supervisar el evento de finalización y, cuando se produzca, descartar los objetos de sesión y renderizador. Una vez que finaliza una sesión de XR, 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 te daré 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 la entrada 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ó 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 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 del puntero es solo una línea tenue entre el control y lo que se está apuntando. Sin embargo, tu app debe dibujarla. Esto significa obtener la pose del dispositivo de entrada y dibujar una línea desde su ubicación hasta un objeto en el espacio de RA o RV. Ese proceso se ve más o menos así:

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 de seguimiento de entrada del Immersive Web Community Group. Al igual que con la renderización de fotogramas, tú decides cómo 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.

Cómo seleccionar elementos en el espacio virtual

Solo señalar cosas en RA o RV es bastante inútil. Para hacer algo útil, los usuarios deben poder seleccionar elementos. La API de WebXR Device proporciona tres eventos para responder a las interacciones de los usuarios: select, selectStart y selectEnd. Tienen una peculiaridad que no esperaba: solo te dicen 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 en cuanto estén disponibles.

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 quieras más contexto.

Para determinar en qué se hizo clic, usa una pose. (¿Te sorprende? Eso pensé). Los detalles de eso son específicos de tu app o de 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: Mirando hacia el futuro

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