Realidade aumentada: você talvez já saiba

Se você já tiver usado a API WebXR Device, poderá concluir quase todas as etapas.

Joe Medley
Joe Medley

A API WebXR Device lançada no último trimestre no Chrome 79. Como dissemos, a implementação da API pelo Chrome ainda está em andamento. O Chrome tem o prazer de anunciar que parte do trabalho foi concluída. No Chrome 81, dois novos recursos chegaram:

Este artigo aborda realidade aumentada. Se você já usou a API WebXR Device, vai gostar de saber que há poucas coisas novas para aprender. O processo de entrar em uma sessão do WebXR é basicamente o mesmo. A execução de um loop de frame é basicamente a mesma. As diferenças estão em configurações que permitem que o conteúdo seja mostrado corretamente para realidade aumentada. Se você não conhece os conceitos básicos do WebXR, leia minhas postagens anteriores sobre a API WebXR Device ou, pelo menos, conheça os tópicos abordados nele. Você precisa saber como solicitar e entrar uma sessão, além de executar um loop de frame.

Para informações sobre testes de hit, consulte o artigo complementar Como posicionar objetos virtuais em visualizações do mundo real. O código neste artigo é baseado no exemplo da sessão de RA imersiva (demonstração fonte) dos exemplos da API WebXR Device do Immersive Web Working Group.

Antes de mergulhar no código, use o exemplo da sessão de RA imersiva pelo menos uma vez. Você vai precisar de um smartphone Android moderno com o Chrome 81 ou versão mais recente.

Para que ela é útil?

A realidade aumentada vai ser uma adição valiosa a muitas páginas da Web novas ou atuais, permitindo que as pessoas implementem casos de uso de RA sem sair do navegador. Por exemplo, ele pode ajudar as pessoas a aprender em sites educacionais e permitir que compradores em potencial visualizem objetos em casa enquanto fazem compras.

Considere o segundo caso de uso. Imagine simular a colocação de uma representação em tamanho real de um objeto virtual em uma cena real. Depois de colocada, a imagem permanece na superfície selecionada, parece ter o tamanho que seria se o item real estivesse nessa superfície e permite que o usuário se mova o mais perto ou mais longe dela. Isso dá aos espectadores uma compreensão mais profunda do objeto do que possível com uma imagem bidimensional.

Estou me adiantando um pouco. Para fazer o que descrevi, você precisa da funcionalidade de RA e alguns meios para detectar superfícies. Este artigo aborda o primeiro. O artigo complementar sobre a API WebXR Hit Test (link acima) abrange esse último.

Como solicitar uma sessão

Solicitar uma sessão é muito parecido com o que você já aprendeu. Primeiro, descubra se o tipo de sessão que você quer está disponível no dispositivo atual chamando xr.isSessionSupported(). Em vez de solicitar 'immersive-vr' como antes, solicite 'immersive-ar'.

if (navigator.xr) {
  const supported = await navigator.xr.isSessionSupported('immersive-ar');
  if (supported) {
    xrButton.addEventListener('click', onButtonClicked);
    xrButton.textContent = 'Enter AR';
    xrButton.enabled = supported; // supported is Boolean
  }
}

Como antes, isso ativa o botão "Enter AR". Quando o usuário clicar nele, chame xr.requestSession(), transmitindo também 'immersive-ar'.

let xrSession = null;
function onButtonClicked() {
  if (!xrSession) {
    navigator.xr.requestSession('immersive-ar')
    .then((session) => {
      xrSession = session;
      xrSession.isImmersive = true;
      xrButton.textContent = 'Exit AR';
      onSessionStarted(xrSession);
    });
  } else {
    xrSession.end();
  }
}

Uma propriedade de conveniência

Você deve ter notado que destaquei duas linhas no último exemplo de código. O objeto XRSession parece ter uma propriedade com o nome isImmersive. Esta é uma propriedade de conveniência que eu criei e não faz parte da especificação. Vou usá-la mais tarde para tomar decisões sobre o que mostrar ao espectador. Por que essa propriedade não faz parte da API? Como seu app pode precisar rastrear essa propriedade de forma diferente, os autores das especificações decidiram manter a API limpa.

Entrar em uma sessão

Lembre-se de como onSessionStarted() era no meu artigo anterior:

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(session, gl)
  });

  xrSession.requestReferenceSpace('local-floor')
  .then((refSpace) => {
    xrRefSpace = refSpace;
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

Preciso adicionar alguns elementos para considerar a renderização da realidade aumentada. Desative o segundo plano. Primeiro, vou determinar se preciso dele. Este é o primeiro lugar em que vou usar minha propriedade de conveniência.

function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  if (session.isImmersive) {
    removeBackground();
  }

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(session, gl)
  });

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });

}

Espaços de referência

Meus artigos anteriores examinaram os espaços de referência. O exemplo que estou descrevendo usa dois deles, por isso é hora de corrigir essa omissão.

Um espaço de referência descreve a relação entre o mundo virtual e o ambiente físico do usuário. Ele faz isso:

  • Especificar a origem do sistema de coordenadas usado para expressar posições no mundo virtual.
  • Especificar se o usuário deve se mover dentro desse sistema de coordenadas.
  • Se esse sistema de coordenadas tem limites pré-estabelecidos. Os exemplos mostrados aqui não usam sistemas de coordenadas com limites preestabelecidos.

Para todos os espaços de referência, a coordenada X expressa a esquerda e a direita, Y expressa para cima e para baixo e Z expressa para frente e para trás. Os valores positivos são para a direita, para cima e para trás, respectivamente.

As coordenadas retornadas por XRFrame.getViewerPose() dependem do tipo de espaço de referência solicitado. Vamos falar mais sobre isso quando chegarmos ao loop do frame. Agora, precisamos selecionar um tipo de referência apropriado para realidade aumentada. Novamente, isso usa minha propriedade de conveniência.

let refSpaceType
function onSessionStarted(xrSession) {
  xrSession.addEventListener('end', onSessionEnded);

  if (session.isImmersive) {
    removeBackground();
  }

  let canvas = document.createElement('canvas');
  gl = canvas.getContext('webgl', { xrCompatible: true });

  xrSession.updateRenderState({
    baseLayer: new XRWebGLLayer(session, gl)
  });

  refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
  xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
    xrSession.requestAnimationFrame(onXRFrame);
  });
}

Se você tiver acessado o Exemplo de sessão de RA imersiva, vai perceber que, inicialmente, a cena é estática e não está em realidade aumentada. É possível arrastar e deslizar com o dedo para se mover pela cena. Se você clicar em "START RA", o plano de fundo será descartado e você poderá se mover pela cena movendo o dispositivo. Os modos usam diferentes tipos de espaço de referência. O texto destacado acima mostra como isso é selecionado. Ele usa os seguintes tipos de referência:

local: a origem está na posição do espectador no momento da criação da sessão. Isso significa que a experiência não tem necessariamente um valor mínimo bem definido, e a posição exata da origem pode variar de acordo com a plataforma. Embora não haja limites pré-estabelecidos para o espaço, é esperado que o conteúdo possa ser visualizado sem nenhum movimento além da rotação. Como mostrado no nosso exemplo de RA, pode-se fazer algum movimento no espaço.

viewer: usado com mais frequência para conteúdo apresentado inline na página, esse espaço segue o dispositivo de visualização. Quando transmitido para getViewerPose, ele não fornece rastreamento e, portanto, sempre informa uma pose na origem, a menos que o aplicativo o modifique com XRReferenceSpace.getOffsetReferenceSpace(). O exemplo usa isso para ativar a movimentação por toque da câmera.

Como executar um loop de frame

Conceitualmente, nada muda em relação ao que fiz na sessão de RV descrita nos artigos anteriores. Transmita o tipo de espaço de referência para XRFrame.getViewerPose(). O XRViewerPose retornado será para o tipo de espaço de referência atual. Usar viewer como padrão permite que uma página mostre visualizações de conteúdo antes que o consentimento do usuário seja solicitado para RA ou RV. Isso ilustra um ponto importante: o conteúdo inline usa o mesmo loop de frames que o conteúdo imersivo, reduzindo a quantidade de código que precisa ser mantida.

function onXRFrame(hrTime, xrFrame) {
  let xrSession = xrFrame.session;
  xrSession.requestAnimationFrame(onXRFrame);
  let xrViewerPose = xrFrame.getViewerPose(refSpaceType);
  if (xrViewerPose) {
    // Render based on the pose.
  }
}

Conclusão

Esta série de artigos aborda apenas os conceitos básicos da implementação de conteúdo imersivo na Web. Muitos outros recursos e casos de uso são apresentados pelos exemplos da API WebXR Device do Immersive Web Working Group. Além disso, acabamos de publicar um artigo de teste de hit que explica uma API para detectar superfícies e colocar itens virtuais em uma visualização de câmera real. Confira e assista o blog The web.dev para ver mais artigos no próximo ano.

Foto de David Grandmougin no Unsplash