1. Antes de começar
Este codelab aborda um exemplo de como criar um app da Web de RA. Ele usa JavaScript para renderizar modelos 3D como no mundo real.
Use a API WebXR Device, que combina funcionalidades de RA e realidade virtual (RV). Você pode utilizar as extensões de RA na API WebXR Device para criar um app de RA simples que será executado na Web interativa.
O que é RA?
RA é um termo geralmente usado para descrever a combinação do mundo real com gráficos criados por computador. No caso da RA baseada em smartphone, isso significa posicionar elementos gráficos convincentes em um feed de câmera ao vivo. Para que esse efeito permaneça realista conforme o smartphone se move, o dispositivo com RA precisa entender a movimentação do dispositivo e determinar a posição dele (posição e orientação) no espaço 3D. Isso pode incluir a detecção de superfícies e estimativas da iluminação do ambiente.
A RA passou a ser muito usada em apps após o lançamento do ARCore e do ARKit da Apple, seja para filtros de selfie ou jogos baseados em RA.
O que você vai criar
Neste codelab, você criará um app da Web que projeta um modelo no mundo real usando a realidade aumentada. Esse app vai:
- usar os sensores do dispositivo de destino para determinar e rastrear a posição e a orientação dele no mundo real;
- renderizar um modelo 3D baseado em uma visualização da câmera em tempo real;
- fazer testes de hit para colocar objetos em superfícies descobertas no mundo real.
O que você vai aprender
- Como usar a API WebXR Device
- Como configurar uma cena de RA básica
- Como encontrar uma superfície usando testes de hit de RA
- Como carregar e renderizar um modelo 3D sincronizado com o feed da câmera do mundo real
- Como renderizar sombras com base no modelo 3D
Este codelab é focado em APIs de RA. Resumiremos conceitos e blocos de código sem relevância no código do repositório correspondente.
Pré-requisitos
- Uma estação de trabalho para programar e hospedar conteúdo da Web estático
- Dispositivo Android compatível com ARCore com o Android 8.0 Oreo
- Google Chrome
- Google Play Services para RA instalado (o Chrome solicita que você o instale automaticamente em dispositivos compatíveis)
- Um servidor da Web de sua escolha
- Cabo USB para conectar o dispositivo de RA à estação de trabalho
- Código de amostra
- Um editor de texto
- Conhecimento básico de HTML, CSS, JavaScript e Ferramentas para desenvolvedores do Chrome
Clique em Testar no seu dispositivo de RA para usar a primeira etapa deste codelab. Se você receber uma página com a mensagem "Seu navegador não tem recursos de RA", verifique se o Google Play Services para RA está instalado no dispositivo Android.
2. Configurar seu ambiente de desenvolvimento
Fazer o download do código
- Clique no link abaixo para fazer o download de todo o código deste codelab na sua estação de trabalho:
- Descompacte o arquivo ZIP transferido por download. Isso descompacta uma pasta raiz (
ar-with-webxr-master
), que contém diretórios de várias etapas deste codelab com todos os recursos necessários.
As pastas step-03
e step-04
contêm o estado final desejado da terceira e quarta etapas deste codelab, bem como o resultado final
. Elas servem como referência.
Todo o trabalho de codificação vai ser feito no diretório work
.
Instalar o servidor da Web
- É possível usar seu próprio servidor da Web. Se você ainda não tiver um configurado, veja nesta seção como configurar o servidor da Web para Chrome.
Se você ainda não tiver o app instalado na sua estação de trabalho, poderá instalá-lo pela Chrome Web Store.
- Depois de instalar o app do servidor da Web para Chrome, acesse
chrome://apps
e clique no ícone do servidor da Web:
Você verá esta caixa de diálogo, que permite configurar seu servidor da Web local:
- Clique em Escolher pasta e selecione a pasta
ar-with-webxr-master
. Isso permite que seu trabalho seja detalhado pelo URL destacado na caixa de diálogo do servidor da Web (na seção URLs do servidor da Web). - Em Opções (precisa ser reiniciado), marque a caixa de seleção Mostrar automaticamente index.html.
- Alterne o Servidor da Web para Parado e depois para Iniciado.
- Verifique se pelo menos um URL do servidor da Web é exibido: http://127.0.0.1:8887, que é o URL do host local padrão.
Configurar o encaminhamento de portas
Configure seu dispositivo de RA para que ele acesse a mesma porta na estação de trabalho quando você abrir o localhost:8887.
- Na estação de trabalho de desenvolvimento, acesse chrome://inspect e clique em Encaminhamento de portas...:
- Use a caixa de diálogo Configurações de encaminhamento de portas para encaminhar a porta 8887 para localhost:8887.
- Marque a caixa de seleção Ativar encaminhamento de portas:
Verificar sua configuração
Testar sua conexão:
- Conecte seu dispositivo de RA à estação de trabalho usando um cabo USB.
- No dispositivo de RA no Chrome, digite http://localhost:8887 na barra de endereço. Seu dispositivo de RA deve encaminhar essa solicitação ao seu servidor da Web da estação de trabalho de desenvolvimento. Você verá um diretório de arquivos.
- No dispositivo de RA, clique em
step-03
para carregar o arquivostep-03/index.html
no navegador.
Você verá uma página que contém um botão Iniciar realidade aumentada. | Caso apareça a página de erro Navegador incompatível, é provável que o dispositivo não seja aceito. |
A conexão com o servidor da Web funcionará com seu dispositivo de RA.
- Clique em Iniciar realidade aumentada. Talvez seja necessário instalar o ARCore.
Você verá uma solicitação de permissões da câmera na primeira vez que executar um app de RA.
→
Quando estiver tudo certo, você verá uma cena de cubos sobreposta no topo do feed da câmera. A compreensão de cena melhora conforme mais paisagens são analisadas pela câmera. Dessa forma, movimentar-se pode ajudar a estabilizar o quadro.
3. Configurar o WebXR
Nesta etapa, você aprenderá a configurar uma sessão do WebXR e uma cena de RA básica. A página HTML tem o estilo CSS e o JavaScript para ativar a funcionalidade básica de RA. Isso acelera o processo de configuração, permitindo que o codelab se concentre nos recursos de RA.
Página HTML
Você cria uma experiência de RA em uma página da Web tradicional usando as tecnologias atuais da Web. Nesta experiência, use uma tela de renderização em tela cheia. Portanto, o arquivo HTML não precisa ter muita complexidade.
Os recursos de RA exigem um gesto do usuário para iniciar. Portanto, há alguns componentes do Material Design para exibir o botão Iniciar RA e a mensagem do navegador incompatível.
O arquivo index.html
que já está no diretório work
precisa ser semelhante ao arquivo abaixo. Ele é um subconjunto do conteúdo real, então não copie esse código no seu arquivo.
<!-- Don't copy this code into your file! -->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Building an augmented reality application with the WebXR Device API</title>
<link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
<script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
<!-- three.js -->
<script src="https://unpkg.com/three@0.123.0/build/three.js"></script>
<script src="https://unpkg.com/three@0.123.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="../shared/utils.js"></script>
<script src="app.js"></script>
</head>
<body>
<!-- Information about AR removed for brevity. -->
<!-- Starting an immersive WebXR session requires user interaction. Start the WebXR experience with a simple button. -->
<a onclick="activateXR()" class="mdc-button mdc-button--raised mdc-button--accent">
Start augmented reality
</a>
</body>
</html>
Abrir o código JavaScript principal
O ponto de partida do seu app é o app.js
. Esse arquivo contém um código clichê para configurar uma experiência de RA.
Seu diretório de trabalho também inclui o código do app (app.js
).
Verificar a compatibilidade com WebXR e RA
Antes que um usuário possa usar a RA, verifique a existência do navigator.xr
e os recursos XR necessários. O objeto navigator.xr
é o ponto de entrada da API WebXR Device, então ele precisa existir se o dispositivo for compatível. Além disso, verifique se o modo de sessão "immersive-ar"
é aceito.
Se tudo estiver certo, clicar no botão Abrir realidade aumentada tentará gerar uma sessão de XR. Caso contrário, o método onNoXRDevice()
será chamado (em shared/utils.js
), que exibirá uma mensagem indicando que não há compatibilidade com RA.
Esse código já está presente no app.js
. Por isso, não é necessário fazer nenhuma alteração.
(async function() {
if (navigator.xr && await navigator.xr.isSessionSupported("immersive-ar")) {
document.getElementById("enter-ar").addEventListener("click", activateXR)
} else {
onNoXRDevice();
}
})();
Solicitar uma XRSession
Quando você clica em Abrir realidade aumentada, o código chama activateXR()
. Isso inicia a experiência de RA.
- Localize a função
activateXR()
noapp.js
. Parte do código foi omitida:
activateXR = async () => {
// Initialize a WebXR session using "immersive-ar".
this.xrSession = /* TODO */;
// Omitted for brevity
}
O ponto de entrada para o WebXR é pelo XRSystem.requestSession()
. Use o modo immersive-ar
para permitir que o conteúdo renderizado seja visualizado em um ambiente real.
- Inicialize
this.xrSession
usando o modo"immersive-ar"
:
activateXR = async () => {
// Initialize a WebXR session using "immersive-ar".
this.xrSession = await navigator.xr.requestSession("immersive-ar");
// ...
}
Inicializar um XRReferenceSpace
Um XRReferenceSpace
descreve o sistema de coordenadas usado para objetos no mundo virtual. O modo 'local'
é mais adequado para uma experiência de RA com um espaço de referência que tem uma origem próxima ao espectador e um acompanhamento estável.
Inicialize o this.localReferenceSpace
no onSessionStarted()
com o seguinte código:
this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");
Definir um loop de animação
- Use o
requestAnimationFrame
deXRSession
para iniciar um loop de renderização, semelhante awindow.requestAnimationFrame
.
Em cada frame, o onXRFrame
é chamado com um carimbo de data/hora e um XRFrame.
- Conclua a implementação do
onXRFrame
. Quando um frame for criado, enfileire a próxima solicitação adicionando:
// Queue up the next draw request.
this.xrSession.requestAnimationFrame(this.onXRFrame);
- Adicione um código para configurar o ambiente gráfico. Inclua no fim de
onXRFrame
:
// Bind the graphics framebuffer to the baseLayer's framebuffer.
const framebuffer = this.xrSession.renderState.baseLayer.framebuffer;
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
this.renderer.setFramebuffer(framebuffer);
- Para determinar a posição do espectador, use
XRFrame.getViewerPose()
. EsteXRViewerPose
descreve a posição e a orientação do dispositivo no espaço. Ele também contém uma matriz deXRView
s, que mostra todos os pontos de vista onde a cena precisa ser renderizada para ser exibida corretamente no dispositivo atual. Embora a RV estereoscópica tenha duas visualizações, uma para cada olho, os dispositivos de RA só têm uma.
: as informações nopose.views
são mais usadas para configurar a matriz de visualização e a de projeção da câmera virtual. Isso afeta o layout da cena em 3D. Quando a câmera é configurada, a cena pode ser renderizada. - Inclua no fim de
onXRFrame
:
// Retrieve the pose of the device.
// XRFrame.getViewerPose can return null while the session attempts to establish tracking.
const pose = frame.getViewerPose(this.localReferenceSpace);
if (pose) {
// In mobile AR, we only have one view.
const view = pose.views[0];
const viewport = this.xrSession.renderState.baseLayer.getViewport(view);
this.renderer.setSize(viewport.width, viewport.height);
// Use the view's transform matrix and projection matrix to configure the THREE.camera.
this.camera.matrix.fromArray(view.transform.matrix);
this.camera.projectionMatrix.fromArray(view.projectionMatrix);
this.camera.updateMatrixWorld(true);
// Render the scene with THREE.WebGLRenderer.
this.renderer.render(this.scene, this.camera);
}
Realizar o teste
Execute o app. No dispositivo de desenvolvimento, acesse work/index.html
. Você verá o feed da câmera com cubos flutuando no espaço em que a perspectiva muda à medida que o dispositivo se move. O rastreamento melhora o movimento. Por isso, explore o que funciona para você e seu aparelho.
Se você tiver problemas para executar o app, consulte as seções Introdução e Configurar seu ambiente de desenvolvimento.
4. Adicionar um retículo de foco
Com um cenário básico de RA configurado, é hora de começar a interagir com o mundo real usando um teste de hit. Nesta seção, você vai programar um teste de hit e usá-lo para encontrar uma superfície no mundo real.
Entender o teste de hit
Um teste de hit geralmente é uma maneira de converter uma linha reta de um ponto no espaço em alguma direção e determinar se ela cruza com objetos de interesse. Neste exemplo, o dispositivo é apontado para um local no mundo real. Imagine um raio indo da câmera do seu dispositivo direto para o mundo físico na frente dele.
Com a API WebXR Device, você pode saber se esse raio cruzou qualquer objeto no mundo real, determinado pelos recursos de RA subjacentes e pela compreensão do mundo.
Solicitar uma XRSession
com recursos extras
Para realizar testes de hit, são necessários recursos adicionais ao solicitar a XRSession
.
- No
app.js
, localizenavigator.xr.requestSession
. - Adicione os recursos
"hit-test"
e"dom-overlay"
comorequiredFeature
da seguinte maneira:
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
requiredFeatures: ["hit-test", "dom-overlay"]
});
- Configure a sobreposição DOM. Sobreponha o elemento
document.body
na visualização da câmera de RA da seguinte maneira:
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
requiredFeatures: ["hit-test", "dom-overlay"],
domOverlay: { root: document.body }
});
Adicionar uma solicitação de movimento
O ARCore funciona melhor quando há o entendimento adequado do ambiente. Isso é viabilizado por um processo chamado localização e mapeamento simultâneos (SLAM, na sigla em inglês) em que pontos de recursos visualmente distintos são usados para calcular uma mudança nas características de localização e do ambiente.
Use o "dom-overlay"
da etapa anterior para exibir uma solicitação de movimento na parte superior do stream da câmera.
Adicione um <div>
ao index.html
com stabilization
de ID. Esse <div>
exibe uma animação para os usuários que representa o status de estabilização e solicita que eles se movam com o dispositivo para melhorar o processo de SLAM. A animação é exibida quando o usuário está na RA e ocultada quando o retículo encontra uma superfície, controlado pelas classes <body>
.
<div id="stabilization"></div>
</body>
</html>
Adicionar um retículo
Use um retículo para indicar o local para que a visualização do dispositivo está apontada.
- No
app.js
, substitua a chamadaDemoUtils.createCubeScene()
nosetupThreeJs()
por umThree.Scene()
vazio.
setupThreeJs() {
// ...
// this.scene = DemoUtils.createCubeScene();
this.scene = DemoUtils.createLitScene();
}
- Preencha a nova cena com um objeto que representa o ponto de colisão. A classe
Reticle
indicada gerencia o carregamento do modelo de retículo noshared/utils.js
. - Adicione o
Reticle
à cena nosetupThreeJs()
:
setupThreeJs() {
// ...
// this.scene = DemoUtils.createCubeScene();
this.scene = DemoUtils.createLitScene();
this.reticle = new Reticle();
this.scene.add(this.reticle);
}
Para realizar um teste de hit, use um novo XRReferenceSpace
. Esse espaço de referência indica um novo sistema de coordenadas com base na perspectiva do visualizador para criar um raio alinhado à direção de visualização. Esse sistema de coordenadas é usado na XRSession.requestHitTestSource()
, que pode calcular testes de hit.
- Adicione o seguinte a
onSessionStarted()
noapp.js
:
async onSessionStarted() {
// ...
// Setup an XRReferenceSpace using the "local" coordinate system.
this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");
// Add these lines:
// Create another XRReferenceSpace that has the viewer as the origin.
this.viewerSpace = await this.xrSession.requestReferenceSpace("viewer");
// Perform hit testing using the viewer as origin.
this.hitTestSource = await this.xrSession.requestHitTestSource({ space: this.viewerSpace });
// ...
}
- Usando esse
hitTestSource
, execute um teste de hit em cada quadro:- Se não houver resultados para o teste de hit, isso significa que o ARCore não teve tempo suficiente para entender o ambiente. Nesse caso, solicite que o usuário mova o dispositivo usando o
<div>
de estabilização. - Se houver resultados, movimente o retículo para esse local.
- Se não houver resultados para o teste de hit, isso significa que o ARCore não teve tempo suficiente para entender o ambiente. Nesse caso, solicite que o usuário mova o dispositivo usando o
- Modifique
onXRFrame
para deslocar o retículo:
onXRFrame = (time, frame) => {
// ... some code omitted ...
this.camera.updateMatrixWorld(true);
// Add the following:
const hitTestResults = frame.getHitTestResults(this.hitTestSource);
if (!this.stabilized && hitTestResults.length > 0) {
this.stabilized = true;
document.body.classList.add("stabilized");
}
if (hitTestResults.length > 0) {
const hitPose = hitTestResults[0].getPose(this.localReferenceSpace);
// update the reticle position
this.reticle.visible = true;
this.reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z)
this.reticle.updateMatrixWorld(true);
}
// More code omitted.
}
Adicionar comportamento de toque na tela
Uma XRSession
pode emitir eventos com base na interação do usuário com base no evento select
, que representa a ação principal. No WebXR em dispositivos móveis, a ação principal é um toque na tela.
- Adicione um listener de eventos
select
na parte inferior deonSessionStarted
:
this.xrSession.addEventListener("select", this.onSelect);
Neste exemplo, um toque de tela faz com que um girassol seja colocado no retículo.
- Crie uma implementação para
onSelect
na classeApp
.
onSelect = () => {
if (window.sunflower) {
const clone = window.sunflower.clone();
clone.position.copy(this.reticle.position);
this.scene.add(clone);
}
}
Testar o app
Você criou um retículo que pode ser usado com testes de hit. Ao tocar na tela, um girassol será posicionado no local designado pelo retículo.
- Quando você executar o app, verá um retículo que acompanha a superfície do chão. Se não, tente girar o smartphone lentamente.
- Quando vir o retículo, toque nele. Um girassol deve aparecer sobre ele. Talvez seja necessário se mover um pouco para que a plataforma de RA subjacente consiga detectar melhor as superfícies do mundo real. Pouca iluminação e superfícies sem recursos diminuem a qualidade do entendimento da cena e aumentam a chance de nenhum hit ser encontrado. Se você encontrar algum problema, confira o código
step-04/app.js
para ver um exemplo prático desta etapa.
5. Adicionar sombras
Criar uma cena realista envolve elementos como iluminação e sombras adequadas em objetos digitais que adicionam realismo e imersão.
A iluminação e as sombras são processadas pelo three.js
. Você pode especificar quais luzes devem projetar sombras, quais materiais devem receber e renderizar essas sombras e quais malhas podem criá-las. A cena desse app contém uma luz que gera uma sombra e uma superfície plana para renderizar apenas sombras.
- Ative as sombras no
WebGLRenderer
dothree.js
. Depois de criar o renderizador, defina os seguintes valores noshadowMap
:
setupThreeJs() {
...
this.renderer = new THREE.WebGLRenderer(...);
...
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
...
}
O exemplo de cena criado no DemoUtils.createLitScene()
contém um objeto chamado shadowMesh
, que é uma superfície plana horizontal que renderiza somente sombras. Inicialmente, essa superfície tem uma posição Y de 10.000 unidades. Depois que um girassol é colocado, mova o shadowMesh
para a mesma altura da superfície real de modo que a sombra da flor seja renderizada sobre o solo do mundo real.
- Em
onSelect
, depois de adicionarclone
à cena, inclua um código para reposicionar o plano de sombra:
onSelect = () => {
if (window.sunflower) {
const clone = window.sunflower.clone();
clone.position.copy(this.reticle.position);
this.scene.add(clone);
const shadowMesh = this.scene.children.find(c => c.name === "shadowMesh");
shadowMesh.position.y = clone.position.y;
}
}
Realizar o teste
Ao colocar um girassol, você deve vê-lo lançando uma sombra. Em caso de problemas, confira o código final/app.js
para ver um exemplo prático desta etapa.
6. Outros recursos
Parabéns! Você chegou ao fim deste codelab sobre RA usando o WebXR.