Imagens aumentadas no ARCore

Imagens aumentadas do ARCore

Sobre este codelab

subjectÚltimo jun. 25, 2021 atualizado
account_circleEscrito por Kai Zhou

1. Visão geral

O ARCore é uma plataforma para criação de apps de realidade aumentada no Android. Com as imagens aumentadas, você pode criar apps de RA capazes de reconhecer imagens pré-registradas em 2D no mundo real e fixar o conteúdo virtual nelas.

Este codelab orientará você durante a modificação de um app ARCore de exemplo para incorporar imagens aumentadas em movimento ou fixas.

Neste codelab, você criará um app de amostra existente do ARCore. Ao final do codelab, seu app poderá:

  • detectar um destino de imagem e anexar um labirinto virtual a ele;
  • rastrear o alvo em movimento, desde que ele esteja na visualização da câmera.

6bc6605df89de525.gif

Esta é a primeira vez que você faz um app com o ARCore?

Você pretende escrever um exemplo de código neste codelab ou quer apenas ler estas páginas?

O que você aprenderá

  • Como usar imagens aumentadas no ARCore com o Java
  • Como avaliar a capacidade de uma imagem ser reconhecida pelo ARCore
  • Como anexar um conteúdo virtual a uma imagem e rastrear o movimento dela

Pré-requisitos

Você precisará de hardware e software específicos para concluir este codelab.

Requisitos de hardware

Requisitos de software

  • ARCore APK 1.9.0 ou versão mais recente. Em geral, esse APK é instalado automaticamente no dispositivo pela Play Store
  • Uma máquina de desenvolvimento com o Android Studio (v3.1 ou mais recente)
  • Você precisará de acesso à Internet para fazer o download das bibliotecas durante o desenvolvimento

Agora que está tudo pronto, vamos começar.

2. Configurar o ambiente para desenvolvedores

Fazer download do SDK

Primeiro, faremos o download do SDK do ARCore Android mais recente no GitHub. Descompacte-o no local de sua preferência. Neste codelab, a versão mais antiga do SDK é 1.18.1. A pasta será indicada como arcore-android-sdk-x.xx.x, o valor exato será a versão do SDK que você usará.

Inicie o Android Studio e clique em Open an existing Android Studio project.

5fbf2b21609187cc.png

Navegue até esta pasta descompactada:

arcore-android-sdk-x.xx.x/samples/augmented_image_java

Clique em Open.

Aguarde até o Android Studio terminar de sincronizar o projeto. Se o Android Studio não tiver os componentes necessários, poderá apresentar erro com a mensagem Install missing platform and sync project. Siga as instruções para corrigir o problema.

Execute o aplicativo de amostra

Agora que você tem um projeto de app com o ARCore, vamos testá-lo.

Conecte seu dispositivo ARCore à máquina de desenvolvimento e use o menu Run > Run ‘app' para executar a versão de depuração no dispositivo. Na caixa de diálogo que exige a escolha de um dispositivo para execução, selecione o dispositivo conectado e clique em OK.

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

Este projeto de exemplo usa a targetSdkVersion 28. Se você tiver um erro de compilação como Failed to find Build Tools revision 28.0.3, siga as instruções descritas no Android Studio para fazer o download e instalar a versão necessária das ferramentas de compilação do Android.

Se tudo der certo, o app de amostra será aberto no dispositivo e pedirá permissão para que a imagem aumentada tire fotos e grave vídeos. Toque em ALLOW para conceder permissão.

Testar com uma imagem de amostra

Agora que você configurou o ambiente de desenvolvimento, teste o app com uma imagem.

No Android Studio, na janela Project, navegue até app > assets e clique duas vezes no arquivo default.jpg para abri-lo.

9b333680e7b9f247.jpeg

Aponte a câmera do dispositivo para a imagem da Terra na tela e siga as instruções para encaixar a imagem que você está lendo na mira.

Uma moldura de imagem será sobreposta na imagem, desta forma:

999e05ed35964f6e.png

Em seguida, faremos pequenas melhorias no app de exemplo.

3. Exibir um modelo de labirinto na imagem 2D

Você pode começar a brincar com as imagens aumentadas exibindo um modelo 3D sobre elas.

Fazer o download de um modelo 3D

Neste codelab, usaremos Circle Maze - Green, da Evol, que tem a licença CC-BY 3.0. Armazenei uma cópia desse modelo 3D no repositório GitHub deste codelab, que você pode encontrar aqui.

Siga estas etapas para fazer o download do modelo e colocá-lo no Android Studio.

  1. Acesse o repositório do GitHub deste codelab, diretório de terceiros.
  2. Clique em GreenMaze_obj.zip e, em seguida, no botão Download.

Será feito o download de um arquivo chamado GreenMaze_obj.zip.

  1. No Android Studio, crie o diretório green-maze em app > assets > models
  2. Descompacte GreenMaze_obj.zip e copie o conteúdo neste local: arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/assets/models/green-maze
  3. No Android Studio, navegue até app > assets > templates > green-lack.

Deve haver dois arquivos nesta pasta: GreenMaze.obj e GreenMaze.mtl.

a1f33a2d2d407e03.png

Renderizar o modelo de labirinto

Siga estas etapas para exibir o modelo 3D GreenMaze.obj sobre a imagem 2D existente.

Em AugmentedImageRenderer.java, adicione uma variável de membro chamada mazeRenderer para renderizar o modelo de labirinto. Como o labirinto precisa ser anexado à imagem, faz sentido colocar a mazeRenderer dentro da classe AugmentedImageRenderer.

AugmentedImageRenderer.java

  // Add a member variable to hold the maze model.
 
private final ObjectRenderer mazeRenderer = new ObjectRenderer();

Na função createOnGlThread(), carregue o GreenMaze.obj. Para simplificar, use a mesma textura da moldura.

AugmentedImageRenderer.java

  // Replace the definition of the createOnGlThread() function with the
 
// following code, which loads GreenMaze.obj.
 
public void createOnGlThread(Context context) throws IOException {

    mazeRenderer
.createOnGlThread(
        context
, "models/green-maze/GreenMaze.obj", "models/frame_base.png");
    mazeRenderer
.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);

 
}

Substitua a definição da função draw() pela seguinte. Isso ajusta o labirinto para que ele tenha o tamanho da imagem detectada e a renderiza na tela.

AugmentedImageRenderer.java

  // Adjust size of detected image and render it on-screen
 
public void draw(
     
float[] viewMatrix,
     
float[] projectionMatrix,
     
AugmentedImage augmentedImage,
     
Anchor centerAnchor,
     
float[] colorCorrectionRgba) {
   
float[] tintColor =
        convertHexToColor
(TINT_COLORS_HEX[augmentedImage.getIndex() % TINT_COLORS_HEX.length]);

   
final float mazeEdgeSize = 492.65f; // Magic number of maze size
   
final float maxImageEdgeSize = Math.max(augmentedImage.getExtentX(), augmentedImage.getExtentZ()); // Get largest detected image edge size

   
Pose anchorPose = centerAnchor.getPose();

   
float mazeScaleFactor = maxImageEdgeSize / mazeEdgeSize; // scale to set Maze to image size
   
float[] modelMatrix = new float[16];

   
// OpenGL Matrix operation is in the order: Scale, rotation and Translation
   
// So the manual adjustment is after scale
   
// The 251.3f and 129.0f is magic number from the maze obj file
   
// You mustWe need to do this adjustment because the maze obj file
   
// is not centered around origin. Normally when you
   
// work with your own model, you don't have this problem.
   
Pose mazeModelLocalOffset = Pose.makeTranslation(
                               
-251.3f * mazeScaleFactor,
                               
0.0f,
                               
129.0f * mazeScaleFactor);
    anchorPose
.compose(mazeModelLocalOffset).toMatrix(modelMatrix, 0);
    mazeRenderer
.updateModelMatrix(modelMatrix, mazeScaleFactor, mazeScaleFactor/10.0f, mazeScaleFactor); // This line relies on a change in ObjectRenderer.updateModelMatrix later in this codelab.
    mazeRenderer
.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
 
}

Agora, o labirinto será exibido sobre a imagem default.jpg da Terra.

Observação: como você não tem controle total sobre o modelo de amostra em 3D, o código acima usa alguns números "mágicos". A dimensão do modelo de labirinto é 492,65 x 120 x 492,65, com o centro em (251,3, 60, -129,0). O intervalo de valores de coordenadas X, Y e Z dos vértices é [5,02, 497,67], [0, 120] e [-375,17, 117,25], respectivamente. Assim, a escala do modelo de labirinto precisa ser image_size / 492.65. O mazeModelLocalOffset foi introduzido porque o modelo 3D do labirinto não está centralizado na origem (0, 0, 0).

A parede do labirinto ainda é muito alta para caber sobre a imagem. Crie uma função auxiliar updateModelMatrix() que possa escalonar X, Y, Z de forma desigual para dimensionar a altura do Labirinto em 0,1. Você precisa manter o updateModelMatrix(float[] modelMatrix, float scaleFactor) atual e adicionar a sobrecarga de função updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) como uma nova função.

common/rendering/ObjectRenderer.java

// Scale X, Y, Z coordinates unevenly
public void updateModelMatrix(float[] modelMatrix, float scaleFactorX, float scaleFactorY, float scaleFactorZ) {
   
float[] scaleMatrix = new float[16];
   
Matrix.setIdentityM(scaleMatrix, 0);
    scaleMatrix
[0] = scaleFactorX;
    scaleMatrix
[5] = scaleFactorY;
    scaleMatrix
[10] = scaleFactorZ;
   
Matrix.multiplyMM(this.modelMatrix, 0, modelMatrix, 0, scaleMatrix, 0);
}

Execute o código. Agora o labirinto deve se encaixar perfeitamente acima da imagem.

772cbe2a8baef3ba.png

4. Adicionar Andy ao labirinto

Agora que você tem um labirinto, adicione um personagem para se mover dentro dele. Use o arquivo andy.obj incluído no SDK do ARCore Android. Mantenha a textura da moldura da imagem nele, porque ela é diferente daquela do labirinto verde renderizada sobre a imagem.

Em AugmentedImageRenderer.java, adicione um ObjectRenderer particular para renderizar o Andy.

AugmentedImageRenderer.java

// Render for Andy
 
private final ObjectRenderer andyRenderer = new ObjectRenderer();

Em seguida, inicialize andyRenderer no final de createOnGlThread().

AugmentedImageRenderer.java

public void createOnGlThread(Context context) throws IOException {

   
// Initialize andyRenderer
    andyRenderer
.createOnGlThread(
        context
, "models/andy.obj", "models/andy.png");
    andyRenderer
.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
 
}

Por fim, renderize Andy em cima do labirinto no final da função draw().

AugmentedImageRenderer.java

public void draw(
     
float[] viewMatrix,
     
float[] projectionMatrix,
     
AugmentedImage augmentedImage,
     
Anchor centerAnchor,
     
float[] colorCorrectionRgba) {

   
// Render Andy, standing on top of the maze
   
Pose andyModelLocalOffset = Pose.makeTranslation(
       
0.0f,
       
0.1f,
       
0.0f);
    anchorPose
.compose(andyModelLocalOffset).toMatrix(modelMatrix, 0);
    andyRenderer
.updateModelMatrix(modelMatrix, 0.05f); // 0.05f is a Magic number to scale
    andyRenderer
.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);

 
}

Execute o código. Você verá o Andy em cima do labirinto.

cb1e74569d7ace69.png

Determinar a qualidade da imagem de destino

O ARCore depende dos recursos visuais para reconhecer imagens. Devido a diferenças de qualidade, nem todas as imagens podem ser facilmente reconhecidas.

arcoreimg é uma ferramenta de linha de comando que permite determinar o quanto uma imagem será reconhecida no ARCore. A saída é um número de 0 a 100, sendo 100 o mais fácil de reconhecer.

Veja um exemplo:

arcore-android-sdk-x.xx.x/tools/arcoreimg/macos$
$
./arcoreimg  eval-img --input_image_path=/Users/username/maze.jpg
100

maze.jpg tem um valor de 100, por isso é facilmente reconhecido pelo ARCore.

5. Opcional: fazer o Andy andar no labirinto

Por fim, você pode adicionar códigos para fazer com que o Andy se mova no labirinto. Por exemplo, use o mecanismo de física de código aberto, jBullet, para processar a simulação física. Não há problemas se você pular essa parte.

Faça o download de PhysicsController.java e adicione-o ao seu projeto no diretório

arcore-android-sdk-x.xx.x/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/

No Android Studio, Adicione GreenMaze.obj ao diretório recursos do projeto para que possa ser carregado no ambiente de execução. Copie GreenMaze.obj de app > assets > models > green-maze para app > assets.

Adicione as seguintes dependências ao arquivo build.gradle do app.

app/build.gradle

    // jbullet library
    implementation
'cz.advel.jbullet:jbullet:20101010-1'

   
// Obj - a simple Wavefront OBJ file loader
   
// https://github.com/javagl/Obj
    implementation
'de.javagl:obj:0.2.1'

Defina uma variável andyPose para armazenar a posição atual do Andy.

AugmentedImageRenderer.java

  // Create a new pose for the Andy
 
private Pose andyPose = Pose.IDENTITY;

Modifique AugmentedImageRenderer.java para renderizar o Andy usando a nova variável andyPose.

AugmentedImageRenderer.java

public void draw(
     
float[] viewMatrix,
     
float[] projectionMatrix,
     
AugmentedImage augmentedImage,
     
Anchor centerAnchor,
     
float[] colorCorrectionRgba) {

   
// Use these code to replace previous code for rendering the Andy object
   
//
   
// Adjust the Andy's rendering position
   
// The Andy's pose is at the maze's vertex's coordinate
   
Pose andyPoseInImageSpace = Pose.makeTranslation(
        andyPose
.tx() * mazeScaleFactor,
        andyPose
.ty() * mazeScaleFactor,
        andyPose
.tz() * mazeScaleFactor);

    anchorPose
.compose(andyPoseInImageSpace).toMatrix(modelMatrix, 0);
    andyRenderer
.updateModelMatrix(modelMatrix, 0.05f);
    andyRenderer
.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
 
}

Adicione uma nova função utilitária, updateAndyPose(), para receber atualizações de posições do Andy.

AugmentedImageRenderer.java

  // Receive Andy pose updates
 
public void updateAndyPose(Pose pose) {
    andyPose
= pose;
 
}

Em AugmentedImageActivity.java, crie um objeto PhysicsController que usa o mecanismo de física JBullet para gerenciar todas as funções relacionadas à física.

AugmentedImageActivity.java

import com.google.ar.core.Pose;

 
// Declare the PhysicsController object
 
private PhysicsController physicsController;

No mecanismo de física, usamos uma bola rígida para representar Andy e atualizar a posição de Andy usando a posição da bola. Chame PhysicsController para atualizar a física sempre que o app reconhecer uma imagem. Para mover a bola como se ela estivesse no mundo real, aplique a gravidade do mundo real para mover a bola no labirinto.

AugmentedImageActivity.java

// Update the case clause for TRACKING to call PhysicsController
// whenever the app recognizes an image
 
private void drawAugmentedImages(

   
...

       
case TRACKING:
         
// Switch to UI Thread to update View
         
this.runOnUiThread(
             
new Runnable() {
               
@Override
               
public void run() {
                  fitToScanView
.setVisibility(View.GONE);
               
}
             
});

         
// Create a new anchor for newly found images
         
if (!augmentedImageMap.containsKey(augmentedImage.getIndex())) {
           
Anchor centerPoseAnchor = augmentedImage.createAnchor(augmentedImage.getCenterPose());
            augmentedImageMap
.put(
                augmentedImage
.getIndex(), Pair.create(augmentedImage, centerPoseAnchor));

            physicsController
= new PhysicsController(this);
         
} else {
           
Pose ballPose = physicsController.getBallPose();
            augmentedImageRenderer
.updateAndyPose(ballPose);

           
// Use real world gravity, (0, -10, 0), as gravity
           
// Convert to Physics world coordinate(maze mesh has to be static)
           
// Use the converted coordinate as a force to move the ball
           
Pose worldGravityPose = Pose.makeTranslation(0, -10f, 0);
           
Pose mazeGravityPose = augmentedImage.getCenterPose().inverse().compose(worldGravityPose);
           
float mazeGravity[] = mazeGravityPose.getTranslation();
            physicsController
.applyGravityToBall(mazeGravity);

            physicsController
.updatePhysics();
         
}
         
break;

Execute o app. O Andy agora se move de maneira realista ao inclinar a imagem.

O exemplo abaixo usa outro smartphone para exibir a imagem. Você pode usar qualquer coisa que seja conveniente para você, como um tablet ou a capa de um livro impresso ou apenas um papel impresso preso a um objeto plano.

2f0df284705d3704.gif

Pronto! Divirta-se andando com o Andy pelo labirinto. Dica: é mais fácil encontrar a saída quando você segura a imagem de destino de cabeça para baixo.

6. Parabéns

Parabéns, você chegou ao fim deste codelab e, assim, realizou as seguintes tarefas:

  • Criação e execução de um exemplo de AugmentedImage Java do ARCore.
  • Atualização do exemplo para exibir um modelo de labirinto na imagem, na escala correta.
  • Uso da posição da imagem para fazer algo divertido.

Para consultar o código completo, faça o download dele aqui.

Você se divertiu neste codelab?

Você aprendeu algo útil neste codelab?

Você concluiu a criação do app neste codelab?

Você pretende criar um app com o ARCore nos próximos seis meses?