Esta é a Web imersiva

A Web imersiva representa experiências de mundo virtual hospedadas no navegador. Toda essa experiência de realidade virtual aparece no navegador ou em headsets compatíveis com RV.

Joe Medley
Joe Medley

A Web imersiva significa experiências de mundo virtual hospedadas por meio do navegador. Isso abrange experiências inteiras de realidade virtual (RV) exibidas no navegador ou em headsets compatíveis com RV, como Daydream, Oculus Rift, Samsung Gear VR, HTC Vive e fones de ouvido de realidade mista do Windows, bem como experiências de realidade aumentada desenvolvidas para dispositivos móveis com RA.

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

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 domésticas
  • Arte
  • Algo legal que ninguém pensou ainda

Como faço para chegar lá?

A Web imersiva já está disponível há quase um ano em formato embrionário. Isso foi feito com a API WebVR 1.1, disponível em um teste de origem desde o Chrome 62. Essa API também é compatível com o Firefox e o 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 mudanças importantes eram necessárias para oferecer suporte aos tipos de aplicativo que os desenvolvedores querem criar. A lista completa de lições aprendidas é muito longa para ser analisada, mas inclui problemas como a API ser explicitamente vinculada à linha de execução principal JavaScript, muitas oportunidades para os desenvolvedores definirem configurações obviamente erradas 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 ficou importante que a API fosse extensível para oferecer suporte a eles no futuro.

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

O que é a API WebXR Device?

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

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

Há mais sobre essa nova API do que eu posso mencionar em um artigo como este. Quero dar a você o suficiente para começar a entender as amostras do WebXR. Confira mais informações na explicação original e no nosso Guia de usuários iniciais da Web Immersive. vou expandir mais tarde à medida que o teste de origem avança. Fique à vontade para abrir problemas ou enviar solicitações de envio.

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

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 imagens. Isso fica nas suas mãos. O desenho é feito usando APIs WebGL. Você pode fazer isso se for realmente ambicioso. No entanto, recomendamos usar uma estrutura. Os exemplos imersivos da Web usam um criado apenas para as demonstrações, denominado Cottontail. O three.js é compatível com o WebXR desde maio. Não ouvi falar sobre o A-Frame.

Como iniciar e executar um app

O processo básico é o seguinte:

  1. Solicite um dispositivo XR.
  2. Se disponível, solicite uma sessão de XR. Se você quiser que o usuário coloque o smartphone em um fone de ouvido, essa é uma sessão imersiva que exige um gesto do usuário para entrar.
  3. Use a sessão para executar um loop de renderização que fornece 60 quadros de imagens por segundo. Desenhe o conteúdo adequado na tela em cada frame.
  4. Execute 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 código. Não será possível executar um app do que vou mostrar agora. Mas, novamente, isso é apenas para dar uma noção da situação.

Solicitar um dispositivo XR

Aqui você reconhecerá o código padrão de detecção de recursos. É possível envolver isso em uma função chamada checkForXR().

Caso não esteja usando uma sessão imersiva, é possível pular a divulgação da funcionalidade, receber um gesto do usuário e ir direto para a solicitação de uma sessão. Uma sessão imersiva 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 se refere à realidade virtual ou aumentada. O último às vezes é chamado 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 XR

Agora que temos o dispositivo e o gesto do usuário, é hora de iniciar uma sessão. Para criar uma sessão, o navegador precisa de uma tela na qual 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 desta etapa precisa de um pouco de desembaralhamento. Para resolver isso, vou jogar um monte de palavras para você. Se quiser dar uma olhada no código final, pule avance para conferir rapidamente e depois volte para a explicação completa. Há muita coisa que você pode não conseguir inferir.

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

  1. Solicite um frame de animação.
  2. Consulte 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. Repetir 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 da Web XR. A primeira é o frame de referência, que define de 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 da 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 para o dispositivo. Isso é um pouco confuso, porque um frame de apresentação é recuperado chamando requestAnimationFrame(). Isso o torna compatível com window.requestAnimationFrame().

Antes de dar mais tempo para você analisar, vou oferecer alguns códigos. O exemplo abaixo mostra como o loop de renderização é iniciado e mantido. Observe o uso duplo do frame de palavras. Além disso, 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 ter acesso à tela. Em geral, a posição e orientação de algo em RA/RV é chamada de pose. Tanto os espectadores quanto os dispositivos de entrada têm uma pose. Falarei sobre os dispositivos de entrada mais adiante. As posições do visualizador e do dispositivo de entrada são definidas como uma matriz 4 por 4 armazenada em um Float32Array na ordem principal da coluna. Você consegue a pose do espectador chamando XRFrame.getDevicePose() no objeto do frame de animação atual. Sempre teste para ver se você recuperou uma posição. 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 postura, é hora de desenhar algo. O objeto para o qual 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ê está em uma sessão não imersiva, a matriz tem uma visualização. Se você estiver em uma sessão imersiva, a matriz tem 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 em 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, obterei o código abaixo. Deixei um espaço reservado 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 XR 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 outro aplicativo assumindo o controle. É por isso que um aplicativo bem comportado precisa monitorar o evento final e, quando ele ocorrer, descartar os objetos de sessão e renderizador. Uma sessão XR, uma vez encerrada, não pode ser retomada.

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 no ciclo de vida do aplicativo, vou dar uma ideia de como interagir com objetos em RA ou RV.

A API WebXR Device adota uma abordagem de "apontar e clicar" para entrada do usuário. Com essa abordagem, cada origem de entrada tem um raio de ponteiro definido para indicar para onde um dispositivo de entrada está apontando e eventos para indicar quando algo foi selecionado. Seu app desenha o raio do ponteiro e mostra para onde ele é apontado. Quando o usuário clica no dispositivo de entrada, os eventos são disparados: select, selectStart e selectEnd, especificamente. O app determina o que foi clicado e responde adequadamente.

O dispositivo de entrada e o raio indicador

Para os usuários, o raio do ponteiro é apenas uma linha fraca entre o controle e o que ele está apontando. No entanto, seu app precisa desenhá-la. Isso significa identificar a posição do dispositivo de entrada e desenhar uma linha da localização 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 raio do ponteiro e o dispositivo cabe a você. Conforme mencionado anteriormente, esse código precisa ser executado como parte do loop de renderização.

Seleção de itens no espaço virtual

Só apontar para coisas em RA/RV é inútil. Para fazer algo útil, os usuários precisam poder selecionar coisas. A API WebXR Device fornece três eventos para responder a interações do usuário: select, selectStart e selectEnd. Eles têm uma peculiaridade que eu não esperava: eles só dizem que um dispositivo de entrada recebeu o clique. Eles não informam em qual item do ambiente foi clicado. Os manipuladores de eventos são adicionados ao objeto XRSession e precisam ser adicionados assim que estiver disponível.

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

Esse código é baseado em um exemplo de seleção de entrada, caso você queira mais contexto.

Para descobrir o que foi clicado, use uma pose. (Você ficou surpreso? não achei que tivesse sido.) Os detalhes são específicos do seu app ou do framework usado e, portanto, estão fora do escopo deste artigo. A abordagem da Cottontail está no exemplo da 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: olhando para o futuro

Como eu disse antes, a realidade aumentada está disponível no Chrome 69 (Canary por volta de junho de 2018). No entanto, recomendo que você tente o que conseguimos até agora. Precisamos de feedback para melhorar. Acompanhe o progresso no ChromeStatus.com para o WebXR Hit Test. Você também pode seguir os fixos do WebXR, que melhoram o rastreamento de poses.