ARCore 증강 이미지

ARCore는 Android의 증강 현실 앱을 빌드할 수 있는 플랫폼입니다. 증강 이미지를 사용하면 사전 등록된 이미지를 인식하고 이미지에 가상 콘텐츠를 고정하는 기능이 있는 AR 앱을 만들 수 있습니다.

이 Codelab에서는 기존 ARCore 샘플 앱을 수정하여 움직이거나 고정된 증강 이미지를 통합하는 과정을 안내합니다.

빌드할 기능

이 Codelab에서는 기존 ARCore 샘플 앱을 기반으로 빌드하겠습니다. Codelab을 마치면 앱에 다음 기능을 구현할 수 있습니다.

  • 대상 이미지를 감지하고 대상에 가상 미로 연결 (아래의 예시 이미지 참고)
  • 움직이는 대상이 뷰 안에 있을 때 대상 추적

6bc6605df89de525.gif

ARCore 앱을 처음 만드나요?

아니요

이 Codelab에서 샘플 코드를 작성할 계획인가요 아니면 이 페이지를 읽기만 할 건가요?

샘플 코드 작성페이지 읽기

학습할 내용

  • 자바로 ARCore에서 증강 이미지를 사용하는 방법
  • ARCore의 이미지 인식 능력을 측정하는 방법
  • 이미지에 가상 콘텐츠를 연결하고 이미지의 움직임을 추적하는 방법

필요한 항목

이 Codelab을 시작하기 전에 필요한 항목을 모두 갖추었는지 확인하세요.

  • 지원되는 ARCore 기기: USB 케이블을 통해 개발 머신에 연결해야 합니다.
  • ARCore 1.9 이상: 이 APK는 일반적으로 Play 스토어를 통해 기기에 자동으로 설치됩니다. 필요한 ARCore 버전이 기기에 없는 경우 언제든지 Play 스토어에서 설치할 수 있습니다.
  • Android 스튜디오(v3.1 이상)가 설치된 개발 머신
  • 인터넷 액세스: 개발 중에 라이브러리를 다운로드할 때 필요합니다.

이제 모두 준비했으니 시작하겠습니다.

먼저 GitHub에서 ARCore Java SDK arcore-android-sdk-1.18.1.zip을 다운로드합니다. 원하는 위치에서 압축 해제합니다. 압축 해제 폴더 이름은 arcore-android-sdk-1.18.1입니다.

Android 스튜디오를 시작하고 Open an existing Android Studio project를 클릭합니다.

5fbf2b21609187cc.png

다음의 압축 해제된 폴더로 이동합니다.

arcore-android-sdk-1.18.1/samples/augmented_image_java

Open을 클릭합니다.

Android 스튜디오가 프로젝트 동기화를 마칠 때까지 기다립니다. 필요한 구성요소가 없는 경우 'Install missing platform and sync project'라는 메시지가 표시되며 Android 스튜디오가 작동하지 않을 수도 있습니다. 안내에 따라 문제를 해결합니다.

이제 작동하는 ARCore 앱 프로젝트가 있으므로 테스트를 실행해 봅니다.

ARCore 기기를 개발 머신에 연결하고 Run > Run ‘app' 메뉴를 사용하여 기기에서 디버그 버전을 실행합니다. 실행할 기기를 선택하라는 대화상자에서 다음을 선택합니다.

연결된 기기를 선택하고 OK를 클릭합니다.

1aa2c6faa7ecdbd0.png

92e4c144a632b4ca.png

이 샘플 프로젝트는 targetSdkVersion 28을 사용합니다. Failed to find Build Tools revision 28.0.3과 같은 빌드 오류가 발생하는 경우 Android 스튜디오에서 설명하는 안내에 따라 필요한 Android 빌드 도구 버전을 다운로드하여 설치합니다.

여기까지 모두 성공하면 기기에서 샘플 앱이 실행되며 증강 이미지의 사진 및 동영상 촬영을 허용할 것을 요청하는 메시지가 표시됩니다. ALLOW를 탭하여 권한을 부여합니다.

샘플 앱이 이미지를 인식하는지 확인해 보겠습니다.

Android 스튜디오에서 Project 창의 app > assets로 이동하고 default.jpg 파일을 더블클릭하여 엽니다.

9b333680e7b9f247.jpeg

화면에 있는 지구 이미지에 기기 카메라를 대고 안내에 따라 스캔할 이미지를 십자선에 맞춥니다.

이미지 프레임이 다음과 같이 이미지 위에 오버레이됩니다.

999e05ed35964f6e.png

다음으로 샘플 앱을 약간 개선합니다.

이 Codelab의 시작 부분에서 언급했듯이, 이미지에서 간단한 미로 게임을 해 보겠습니다. 먼저 CC-BY 라이선스에 따라 무료로 사용할 수 있으며 많은 3D 모델이 포함된 미로 모델을 poly.google.com에서 찾겠습니다.

이 Codelab에서는 게시자가 Evol이고 CC-BY 3.0에 따라 라이선스가 부여된 'Circle Maze - Green'을 사용합니다.

832fc0f1b09fea1e.png

다음 단계에 따라 모델을 다운로드하여 Android 스튜디오로 가져오세요.

  1. Poly 모델 페이지로 이동합니다.
  2. Download(다운로드)를 클릭하고 OBJ File(OBJ 파일)을 선택합니다.

그러면 green-maze.zip이라는 파일이 다운로드됩니다.

  1. green-maze.zip을 압축 해제하고 콘텐츠를 arcore-android-sdk-1.18.1/samples/augmented_image_java/app/src/main/assets/models/green-maze 위치에 복사합니다.
  2. Android 스튜디오에서 app > assets > models > green-maze로 이동합니다.

이 폴더에는 GreenMaze.objGreenMaze.mtl의 두 파일이 있습니다.

a1f33a2d2d407e03.png

다음으로, 이 OBJ 파일을 로드하고 감지된 이미지 위에 표시합니다.

이제 3D 모델 GreenMaze.obj를 이미지 위에 표시해 보겠습니다.

  1. 미로 모델을 렌더링하도록 AugmentedImageRenderer.java에서 mazeRenderer라는 멤버 변수를 추가합니다. 미로를 이미지에 연결해야 하므로 mazeRendererAugmentedImageRenderer 클래스 안에 배치하는 것이 좋습니다.
  2. createOnGlThread 함수에 GreenMaze.obj를 로드합니다. 여기서는 편의를 위해 텍스처와 동일한 프레임 텍스처를 사용합니다.
  3. draw 함수에서 감지된 이미지의 크기에 맞춰 미로 크기를 조정하고 미로를 그립니다.

AugmentedImageRenderer.java에서 다음과 같이 변경합니다.

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

좋습니다. 지구의 default.jpg 그림 위에 미로를 표시할 수 있도록 코드를 변경했습니다.

위의 코드에 매직 숫자가 몇 개 사용되었습니다. 걱정하지 마세요. 이 3D 모델을 완전히 제어할 수 없기 때문에 포함한 숫자입니다. 모델의 중심(x, y, z) 위치와 크기를 파악하기 위해 obj 파일을 직접 파싱했습니다. 이 Codelab에서는 이 작업을 하지 않습니다. 다만 여기에 값을 제공해 두겠습니다. 미로 모델의 크기는 492.65 x 120 x 492.65이고 중심 위치는 (251.3, 60, -129.0)입니다. X, Y, Z 좌표의 꼭짓점 값 범위는 각각 [5.02, 497.67], [0, 120], [-375.17, 117.25]입니다. 따라서 미로 모드의 크기를 image_size / 492.65로 설정해야 합니다. 이미 확인한 바와 같이 미로 3D 모델의 중심은 원점 (0, 0, 0)에 맞춰지지 않으므로 오프셋 mozeModelLocalOffset을 직접 넣어야 합니다.

또한 미로 벽이 이 Codelab에서 취급하기에는 너무 높으므로 0.1배로 추가 확장하겠습니다. 그러면 벽이 낮아져 차이가 더 잘 보입니다. 이렇게 하려면 X, Y, Z 좌표를 불균등하게 조정할 수 있는 도우미 함수를 도입해야 합니다.

augmentedimage/rendering/ObjectRenderer.java에서 다음과 같이 변경합니다.

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

ARCore 지원 기기에서 실행해 보겠습니다. 이제 미로 크기가 이미지 크기와 같습니다.

772cbe2a8baef3ba.png

이제 미로 내부를 이동하는 객체를 추가하겠습니다. 이 Codelab에서는 단순하게 ARCore Android SDK에 포함된 Android 피겨 andy.obj 파일을 사용합니다. 또한 이미지 위에 렌더링되는 녹색 미로와 다르게 보이기 때문에 이미지 프레임 텍스처를 텍스처로 사용합니다.

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

  }

그런 다음 기기에서 실행하겠습니다. 다음과 같이 표시됩니다.

cb1e74569d7ace69.png

대상 이미지 품질 확인

이미지를 인식하기 위해 ARCore는 이미지의 시각적 특징을 사용합니다. 이미지마다 품질이 다르며, 모든 이미지를 쉽게 인식할 수는 없습니다.

ARCore Android SDK의 arcoreimg 도구를 사용하면 대상 이미지의 품질을 확인할 수 있습니다. 명령줄 도구를 실행하여 ARCore에서 이미지가 얼마나 잘 인식될지 판단할 수 있습니다. 이 도구는 0에서 100 사이의 숫자를 출력합니다(100은 가장 쉽게 인식됨을 나타냄). 예를 들면 다음과 같습니다.

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

마지막 섹션은 ARCore와 실제로 관련이 없고 이 예제 앱을 더 재밌게 만드는 추가 내용입니다. 이 부분을 건너뛰어도 괜찮습니다.

오픈소스 물리 엔진인 jBullet을 사용하여 물리 시뮬레이션을 처리합니다.

다음 단계를 따릅니다.

  1. 런타임에 로드할 수 있도록 GreenMaze.obj를 프로젝트 애셋 디렉터리에 추가합니다.
  2. 모든 물리 관련 함수를 관리하는 PhysicsController 클래스를 만듭니다. 이 클래스는 내부적으로 JBullet 물리 엔진을 사용합니다.
  3. 이미지가 인식되면 PhysicsController를 호출하고 updatePhysics를 호출합니다.
  4. 실제 중력을 사용하여 미로에서 공을 움직입니다. 미로의 간격을 통과할 수 있도록 공의 크기를 약간 조정해야 합니다.

PhysicsController.java 코드를 다운로드하여 arcore-android-sdk-1.18.1/samples/augmented_image_java/app/src/main/java/com/google/ar/core/examples/java/augmentedimage/ 디렉터리의 프로젝트에 추가합니다.

그런 다음 기존 자바 코드에서 변경합니다. 다음과 같습니다.

Android 스튜디오에서 다음 위치에 있는 GreenMaze.obj

app > assets > models > green-maze

다음 위치로 복사합니다.

app > assets

app/build.gradle에 다음 코드를 추가합니다.

    // 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'

AugmentedImageRenderer.java에 다음 코드를 추가합니다.

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

AugmentedImageActivity.java에 다음 코드를 추가합니다.

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;

그러면 공이 다음과 같이 움직입니다.

2f0df284705d3704.gif

즐겨 보시기 바랍니다.

축하합니다. 이 Codelab의 내용을 완료했습니다. 이 Codelab에서 실습한 내용을 다시 살펴보겠습니다.

  • ARCore AugmentedImage 자바 샘플을 빌드하고 실행했습니다.
  • 근처 이미지에 자동으로 초점을 맞추도록 샘플을 업데이트하고 이미지 프레임을 이미지 크기에 맞춥니다.
  • 사용자가 지정한 이미지를 대상 이미지로 사용하도록 샘플을 업데이트했습니다.
  • 이미지에 미로 모델을 적절한 크기로 표시하도록 샘플을 업데이트했습니다.
  • 이미지의 위치를 활용하여 재미있는 효과를 만들었습니다.

전체 코드를 참고하려면 여기에서 다운로드하세요.

Codelab이 재미있었나요?

아니요

Codelab에서 유용한 정보를 얻었나요?

아니요

Codelab에서 앱 만들기를 완료했나요?

아니요

향후 6개월 안에 ARCore 앱을 만들 계획인가요?

가능성 있음아니요