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.

O que você criará

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?