Imagens aumentadas no ARCore

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 e ancorar 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á com base em um app de exemplo ARCore existente. Ao final do codelab, seu app será:

  • capaz de detectar uma imagem de destino e anexar um labirinto virtual a ela. Veja um exemplo de visualização abaixo;
  • capaz de rastrear a imagem em movimento, desde que ela esteja na visualização.

6bc6605df89de525.gif

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

Sim Não

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

Escrever um exemplo de código 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

Confira se você tem tudo o que precisa antes de iniciar este codelab:

  • Um dispositivo compatível com o ARCore, conectado por um cabo USB à sua máquina de desenvolvimento
  • ARCore 1.9 ou mais recente. Em geral, esse APK é instalado automaticamente no dispositivo pela Play Store. Se o dispositivo não tiver a versão necessária do ARCore, será possível instalá-la na Play Store
  • Uma máquina de desenvolvimento com o Android Studio (v3.1 ou mais recente)
  • Acesso à Internet para fazer o download de bibliotecas durante o desenvolvimento

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

Primeiro, faremos o download do SDK ARCore Java no GitHub arcore-android-sdk-1.18.1.zip. Descompacte-o no local de sua preferência. A pasta extraída será chamada de arcore-android-sdk-1.18.1.

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

5fbf2b21609187cc.png

Navegue até esta pasta descompactada:

arcore-android-sdk-1.18.1/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á ser exibida a mensagem de falha "Install missing platform and sync project". Siga as instruções para corrigir o problema.

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 pede para você escolher em qual dispositivo executar,

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 exemplo será iniciado no dispositivo e solicitará sua permissão para as imagens aumentadas tirarem fotos e gravarem vídeos. Toque em ALLOW para conceder permissão.

Vamos dar ao nosso app de exemplo uma imagem para ser analisada.

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.

Como mencionamos no início deste codelab, faremos um jogo incrível de labirinto na imagem. Primeiro, vamos encontrar um modelo de labirinto em poly.google.com, que contém muitos modelos 3D com a licença CC-BY para uso gratuito.

Neste codelab, usaremos "Circle Maze - Green", da Evol, que tem a licença CC-BY 3.0.

832fc0f1b09fea1e.png

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

  1. Navegue até a página do modelo no Poly.
  2. Clique em Fazer o download e selecione Arquivo OBJ.

Será feito o download de um arquivo chamado green-maze.zip.

  1. Descompacte green-maze.zip e copie o conteúdo neste local: arcore-android-sdk-1.18.1/samples/augmented_image_java/app/src/main/assets/models/green-maze
  2. No Android Studio, navegue até app > assets > templates > green-lack.

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

a1f33a2d2d407e03.png

Em seguida, carregaremos esse arquivo OBJ e o exibiremos acima da imagem detectada.

Agora que temos o modelo 3D, GreenMaze.obj, vamos exibi-lo sobre nossa imagem.

  1. No 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.
  2. Na função createOnGlThread, carregue o GreenMaze.obj. Para simplificar, usaremos a mesma textura de moldura nele.
  3. Na função draw, ajuste o labirinto para que tenha o tamanho da imagem detectada e desenhe-o.

No AugmentedImageRenderer.java, faça essas mudanças.

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

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

  }

  // Replace the definition of the draw function with the
  // following code
  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 maze_edge_size = 492.65f; // Magic number of maze size
    final float max_image_edge = Math.max(augmentedImage.getExtentX(), augmentedImage.getExtentZ()); // Get largest detected image edge size

    Pose anchorPose = centerAnchor.getPose();

    float mazsScaleFactor = max_image_edge / maze_edge_size; // 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
    // We 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 mozeModelLocalOffset = Pose.makeTranslation(
                                -251.3f * mazsScaleFactor,
                                0.0f,
                                129.0f * mazsScaleFactor);
    anchorPose.compose(mozeModelLocalOffset).toMatrix(modelMatrix, 0);
    mazeRenderer.updateModelMatrix(modelMatrix, mazsScaleFactor, mazsScaleFactor/10.0f, mazsScaleFactor);
    mazeRenderer.draw(viewMatrix, projectionMatrix, colorCorrectionRgba, tintColor);
  }

Parece que fizemos mudanças de código suficientes para exibir o labirinto sobre a nossa foto default.jpg da Terra.

Usei alguns números mágicos no código acima. Não se assuste, eles estão ali apenas porque não temos controle total sobre esse modelo 3D. Então, analisei manualmente o arquivo obj para entender a posição central (x, y, z) do modelo e o tamanho dele. Não vamos fazer isso neste codelab, então vou simplesmente passar o valor aqui. 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, Z dos vértices é [5,02, 497,67], [0, 120], [-375,17, 117,25], respectivamente. Então, precisamos definir a escala do modo labirinto por image_size / 492.65. Como você já deve ter notado, o modelo 3D do labirinto não está centralizado na origem (0, 0, 0). É por isso que precisamos introduzir um deslocamento mozeModelLocalOffset manualmente.

Além disso, como a parede do labirinto ainda é muito alta para nosso codelab, vamos escaloná-la em 0,1 vez mais. Isso diminui a parede para que os vãos fiquem mais visíveis. Para isso, precisamos introduzir uma função auxiliar que nos permita dimensionar as coordenadas X, Y e Z de maneira desigual.

No augmentedimage/rendering/ObjectRenderer.java, faça estas mudanças.

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

Vamos tentar executar no seu dispositivo compatível com o ARCore. O tamanho do labirinto agora deve ser o mesmo da imagem.

772cbe2a8baef3ba.png

Agora, vamos adicionar um objeto que se move dentro do labirinto. Neste codelab, vamos simplificar e usar o arquivo Android de miniatura andy.obj incluído no SDK do Android ARCore. E use a textura da moldura da imagem nele, porque ela é diferente daquela do labirinto verde que renderizamos sobre a imagem.

Adicione este código no AugmentedImageNode.java.

// Add a private member to render andy
  private final ObjectRenderer andyRenderer = new ObjectRenderer();

  public void createOnGlThread(Context context) throws IOException {

    // Add initialization for andyRenderer at the end of the createOnGlThread function.
    andyRenderer.createOnGlThread(
        context, "models/andy.obj", "models/andy.png");
    andyRenderer.setMaterialProperties(0.0f, 3.5f, 1.0f, 6.0f);
  }

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

    // In draw() function, at the end add code to display the 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);

  }

Agora, vamos tentar executá-lo no dispositivo. Veremos algo assim.

cb1e74569d7ace69.png

Determinar a qualidade da imagem de destino

Para reconhecer uma imagem, o ARCore depende dos recursos visuais dela. Nem todas as imagens têm a mesma qualidade e podem ser reconhecidas facilmente.

A ferramenta arcoreimg do SDK do Android ARCore permite verificar a qualidade de uma imagem de destino. Podemos executar essa ferramenta de linha de comando para determinar o quanto uma imagem será reconhecida no ARCore. A ferramenta gera um número entre 0 e 100, sendo 100 o mais fácil de reconhecer. Veja um exemplo:

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

A última seção não é muito relevante para o ARCore, mas é uma parte extra que torna este app de exemplo divertido. Não há problemas se você pular essa parte.

Usaremos um mecanismo de física de código aberto, jBullet, para lidar com a simulação de física.

Veja o que vamos fazer:

  1. Adicionar o GreenMaze.obj ao diretório de recursos do projeto para que possamos carregá-lo no momento da execução.
  2. Criar a classe PhysicsController para gerenciar todas as funções de física. Internamente, ela usa o mecanismo de física JBullet.
  3. Chamar PhysicsController quando uma imagem for reconhecida e updatePhysics.
  4. Usar a gravidade do mundo real para mover a bola no labirinto. Precisamos dimensionar um pouco o tamanho da bola para que ela possa passar pelos vãos do labirinto.

Faça o download do código PhysicsController.java e adicione-o ao seu projeto no diretório arcore-android-sdk-1.18.1/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/

Em seguida, faça essas mudanças no código Java existente. Como você verá abaixo,

no Android Studio, copie GreenMaze.obj de

app > assets > models > green-maze

para

app > assets

No app/build.gradle, adicione este código.

    // Add these dependencies.
    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'

No AugmentedImageRenderer.java, adicione este código.

// Add this line at the top with the rest of the imports.
  private Pose andyPose = Pose.IDENTITY;

  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 andy's rendering position
    // Andy's pose is at Maze's vertex's coordinate
    Pose andyPoseInImageSpace = Pose.makeTranslation(
        andyPose.tx() * mazsScaleFactor,
        andyPose.ty() * mazsScaleFactor,
        andyPose.tz() * mazsScaleFactor);

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

  // Add a new utility function to receive Andy pose updates
  public void updateAndyPose(Pose pose) {
    andyPose = pose;
  }

No AugmentedImageActivity.java, adicione este código.

import com.google.ar.core.Pose;

  // Declare the PhysicsController class.
  private PhysicsController physicsController;

  // Update the case clause for TRACKING as below
  private void drawAugmentedImages(

    ...

        case TRACKING:
          // Have to 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 (because Maze mesh has to be static)
            // Use it 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;

Em seguida, podemos fazer a bola se mover da seguinte forma.

2f0df284705d3704.gif

Divirta-se.

Parabéns, você chegou ao fim deste codelab. Vamos rever o que conseguimos fazer nele.

  • Criação e execução de um exemplo de AugmentedImage Java do ARCore.
  • Atualização do exemplo para que foque automaticamente nas imagens próximas e alinhamento da moldura da imagem com o tamanho dela.
  • Atualização do exemplo para usar uma imagem especificada pelo usuário como destino.
  • 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?

Sim Não

Você aprendeu algo útil neste codelab?

Sim Não

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

Sim Não

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

Sim Talvez Não