Esta é a Web imersiva

A Web imersiva significa experiências do mundo virtual hospedadas no navegador. Todas essas experiências de realidade virtual apareceram no navegador ou em headsets com RV.

Joe Medley
Joe Medley

A Web imersiva significa experiências do mundo virtual hospedadas no navegador. Isso inclui experiências de realidade virtual (RV) completas exibidas no navegador ou em headsets com RV, como Daydream do Google, Oculus Rift, Samsung Gear VR, HTC Vive e Windows Mixed Reality Headsets, além de experiências de realidade aumentada desenvolvidas para dispositivos móveis com RA.

Embora usemos dois termos para descrever experiências imersivas, elas precisam ser pensadas como um espectro da realidade completa para um ambiente de RV completamente imersivo, com vários níveis de RA intermediários.

Confira alguns exemplos de experiências imersivas:

  • Vídeos imersivos em 360°
  • Vídeos tradicionais em 2D (ou 3D) apresentados em ambientes imersivos
  • Visualizações de dados
  • Compras em casa
  • Arte
  • Algo legal que ninguém pensou ainda

Como faço para chegar lá?

A Web imersiva está disponível há quase um ano em formato embrionário. Isso foi feito usando a API WebVR 1.1, que está disponível em um teste de origem desde o Chrome 62. Essa API também tem suporte do Firefox e do Edge, além de um polyfill para o Safari.

Mas é hora de seguir em frente.

O teste de origem terminou em 24 de julho de 2018, e a especificação foi substituída pela API WebXR Device e por um novo teste de origem.

O que aconteceu com a WebVR 1.1?

Aprendemos muito com a WebVR 1.1, mas, com o tempo, ficou claro que algumas grandes mudanças eram necessárias para oferecer suporte aos tipos de aplicativos que os desenvolvedores querem criar. A lista completa de lições aprendidas é muito longa para ser abordada aqui, mas inclui problemas como a API estar explicitamente vinculada à linha de execução principal do JavaScript, muitas oportunidades para os desenvolvedores configurarem configurações obviamente incorretas e usos comuns, como a janela mágica sendo um efeito colateral em vez de um recurso intencional. A janela mágica é uma técnica para visualizar conteúdo imersivo sem um fone de ouvido, em que o app renderiza uma única visualização com base no sensor de orientação do dispositivo.

O novo design facilita implementações mais simples e grandes melhorias de desempenho. Ao mesmo tempo, a RA e outros casos de uso estavam surgindo, e tornou-se importante que a API fosse extensível para oferecer suporte a eles no futuro.

A API WebXR Device foi projetada e nomeada com esses casos de uso expandidos em mente e oferece um caminho melhor. Os implementadores da WebVR se comprometeram a migrar para a API WebXR Device.

O que é a API WebXR Device?

Assim como a especificação do WebVR, a API WebXR Device é um produto do Immersive Web Community Group, que tem colaboradores do Google, Microsoft, Mozilla e outros. O "X" em XR é uma espécie de variável algébrica que representa qualquer coisa no espectro de experiências imersivas. Ele está disponível no teste de origem mencionado anteriormente e em um polyfill.

Quando este artigo foi publicado pela primeira vez durante o período Beta do Chrome 67, apenas os recursos de RV estavam ativados. A realidade aumentada chegou no Chrome 69. Leia mais sobre isso em Realidade aumentada para a Web.

Há mais informações sobre essa nova API do que eu posso acessar em um artigo como este. Quero dar a você o suficiente para começar a entender as amostras do WebXR. Confira mais informações no texto explicativo original e no Guia para os primeiros usuários da Web imersiva. Vou expandir o teste de origem à medida que ele avança. Fique à vontade para informar problemas ou enviar solicitações de envio.

Neste artigo, vou discutir como iniciar, interromper e executar uma sessão de XR, além de alguns conceitos básicos sobre o processamento de entrada.

Não vou abordar como desenhar conteúdo de RA/RV na tela. A API WebXR Device não oferece recursos de renderização de imagem. Isso fica nas suas mãos. O desenho é feito usando APIs WebGL. Você pode fazer isso se for realmente ambicioso. No entanto, recomendamos o uso de uma estrutura. Os exemplos da Web imersiva usam um criado apenas para as demonstrações chamado Cottontail. O Three.js oferece suporte ao WebXR desde maio. Não ouvi nada sobre o A-Frame.

Como iniciar e executar um app

O processo básico é o seguinte:

  1. Solicite um dispositivo de RA.
  2. Se estiver disponível, solicite uma sessão de RA. Se você quiser que o usuário coloque o smartphone em um fone de ouvido, isso é chamado de sessão imersiva e exige um gesto do usuário para entrar.
  3. Use a sessão para executar um loop de renderização que fornece 60 frames de imagem por segundo. Desenhe o conteúdo apropriado na tela em cada frame.
  4. Executar o loop de renderização até que o usuário decida sair.
  5. Encerre a sessão de XR.

Vamos analisar isso com mais detalhes e incluir algum código. Não será possível executar um app com o que vou mostrar. Mas, novamente, isso é apenas para dar uma ideia.

Solicitar um dispositivo XR

Aqui, você vai reconhecer o código padrão de detecção de recursos. Você pode agrupar isso em uma função chamada algo como checkForXR().

Se você não estiver usando uma sessão imersiva, poderá pular a publicidade da funcionalidade e receber um gesto do usuário e ir direto para a solicitação de uma sessão. Uma sessão imersiva é aquela que exige um fone de ouvido. Uma sessão não imersiva simplesmente mostra o conteúdo na tela do dispositivo. O primeiro é o que a maioria das pessoas pensa quando você se refere à realidade virtual ou aumentada. Essa é chamada de "janela 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 uma sessão de RA

Agora que temos o dispositivo e o gesto do usuário, é hora de conseguir uma sessão. Para criar uma sessão, o navegador precisa de uma tela para desenhar.

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

Executar o loop de renderização

O código dessa etapa requer um pouco de desorganização. Para desvendar, vou jogar um monte de palavras para você. Se você quiser dar uma olhada no código final, pule para a frente para dar uma olhada rápida e volte para a explicação completa. Há muitas coisas que você não consegue inferir.

O processo básico para um loop de renderização é este:

  1. Solicitar um frame de animação.
  2. Consultar a posição do dispositivo.
  3. Desenhe o conteúdo na posição do dispositivo com base na posição dele.
  4. Faça o trabalho necessário para os dispositivos de entrada.
  5. Repita 60 vezes por segundo até que o usuário decida sair.

Solicitar um frame de apresentação

A palavra "frame" tem vários significados em um contexto de XR da Web. O primeiro é o sistema de referência, que define onde a origem do sistema de coordenadas é calculada e o que acontece com essa origem quando o dispositivo se move. A visualização permanece a mesma quando o usuário se move ou muda como na vida real?

O segundo tipo de frame é o frame de apresentação, representado por um objeto XRFrame. Esse objeto contém as informações necessárias para renderizar um único frame de uma cena de RA/RV no dispositivo. Isso é um pouco confuso porque um frame de apresentação é recuperado chamando requestAnimationFrame(). Isso a torna compatível com window.requestAnimationFrame().

Antes de continuar, vou mostrar um pouco de código. O exemplo abaixo mostra como o loop de renderização é iniciado e mantido. Observe o uso duplo do frame de palavras. Observe a chamada recursiva para requestAnimationFrame(). Essa função será chamada 60 vezes 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 desenhar algo na tela, você precisa saber para onde o dispositivo de exibição está apontando e acessar a tela. Em geral, a posição e a orientação de uma coisa em RA/RV é chamada de pose. Os espectadores e os dispositivos de entrada têm uma pose. Vamos abordar os dispositivos de entrada mais tarde. As poses do dispositivo de entrada e do espectador são definidas como uma matriz 4 x 4 armazenada em um Float32Array na ordem principal da coluna. Para conferir a pose do espectador, chame XRFrame.getDevicePose() no objeto de frame de animação atual. Sempre teste para ver se você recebeu uma pose de volta. Se algo deu errado, você não quer desenhar na tela.

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

Visualizações

Depois de verificar a pose, é hora de desenhar algo. O objeto em que você desenha é chamado de visualização (XRView). É aqui que o tipo de sessão se torna importante. As visualizações são recuperadas do objeto XRFrame como uma matriz. Se você estiver em uma sessão não imersiva, a matriz terá uma visualização. Se você estiver em uma sessão imersiva, a matriz terá dois, um para cada olho.

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

Essa é uma diferença importante entre o WebXR e outros sistemas imersivos. Embora possa parecer inútil iterar por uma visualização, isso permite que você tenha um único caminho de renderização para vários dispositivos.

Todo o loop de renderização

Se eu juntar tudo isso, vou ter o código abaixo. Deixei um marcador de posição para os dispositivos de entrada, que vou abordar em uma seção 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);
    }
}

Encerrar a sessão de XR

Uma sessão de RA pode ser encerrada por vários motivos, incluindo o encerramento pelo seu próprio código por meio de uma chamada para XRSession.end(). Outras causas incluem a desconexão do fone de ouvido ou o controle de outro aplicativo. É por isso que um aplicativo bem comportado precisa monitorar o evento de término e, quando ele ocorre, descartar a sessão e os objetos do renderizador. Uma sessão de RV não pode ser retomada após o término.

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

Como funciona a interação?

Assim como com a duração do aplicativo, vou mostrar como interagir com objetos em RA ou RV.

A API WebXR Device adota uma abordagem de "apontar e clicar" para a entrada do usuário. Com essa abordagem, todas as fontes de entrada têm um raio de ponteiro definido para indicar para onde um dispositivo de entrada está apontando e os eventos para indicar quando algo foi selecionado. O app desenha o raio do ponteiro e mostra para onde ele está apontando. Quando o usuário clicar no dispositivo de entrada, os eventos serão acionados: select, selectStart e selectEnd, especificamente. O app determina o que foi clicado e responde corretamente.

O dispositivo de entrada e o raio do ponteiro

Para os usuários, o raio do ponteiro é apenas uma linha fraca entre o controle e o que quer que eles estejam apontando. Mas o app precisa renderizá-lo. Isso significa receber a pose do dispositivo de entrada e desenhar uma linha do local dele até um objeto no espaço de RA/RV. Esse processo é mais ou menos assim:

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 é uma versão simplificada do exemplo de rastreamento de entrada do grupo da comunidade da Web imersiva. Assim como na renderização de frames, o desenho do feixe do ponteiro e do dispositivo fica a seu critério. Como mencionado anteriormente, esse código precisa ser executado como parte do loop de renderização.

Seleção de itens no espaço virtual

Simplesmente apontar para os elementos em RA/RV é bastante inútil. Para fazer qualquer coisa útil, os usuários precisam poder selecionar coisas. A API WebXR Device oferece três eventos para responder às interações do usuário: select, selectStart e selectEnd. Eles têm uma peculiaridade que eu não esperava: eles só informam que um dispositivo de entrada foi clicado. Eles não informam qual item no ambiente foi clicado. Os manipuladores de eventos são adicionados ao objeto XRSession e precisam ser adicionados assim que estiverem disponíveis.

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 é baseado em um exemplo de seleção de entrada para que você tenha mais contexto.

Para descobrir o que foi clicado, você usa uma pose. (Você está surpreso? Não acho que sim.) Os detalhes são específicos para seu app ou qualquer framework que você use e, portanto, estão fora do escopo deste artigo. A abordagem da Cottontail está no exemplo de seleção 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.
    }
}

Conclusão: o que o futuro trará

Como disse antes, a realidade aumentada está prevista para o Chrome 69 (Canary, em algum momento de junho de 2018). No entanto, recomendo que você teste o que temos até agora. Precisamos de feedback para melhorar esse recurso. Acompanhe o progresso dele no ChromeStatus.com para o teste de hit do WebXR. Você também pode seguir as âncoras do WebXR, o que vai melhorar o rastreamento de pose.