Android용 Google Cardboard 빠른 시작

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

이 가이드에서는 Android용 Cardboard SDK를 사용하여 자체 가상 현실 (VR) 환경을 만드는 방법을 보여줍니다.

Cardboard SDK를 사용하여 스마트폰을 VR 플랫폼으로 변환할 수 있습니다. 스마트폰은 입체 렌더링으로 3D 장면을 표시하고, 머리 움직임을 추적 및 반응하며, 사용자가 뷰어 버튼을 누르는 시점을 감지하여 앱과 상호작용할 수 있습니다.

시작하려면 Cardboard SDK의 핵심 기능을 보여주는 데모 게임인 HelloCardboard를 사용합니다. 게임에서 사용자는 가상 세계를 둘러보며 객체를 찾고 수집합니다. 다음 방법을 안내합니다.

  • 개발 환경 설정
  • 데모 앱 다운로드 및 빌드
  • Cardboard 뷰어의 QR 코드를 스캔하여 매개변수를 저장하세요.
  • 사용자의 머리 움직임 추적
  • 각 눈에 올바른 뷰 투영 매트릭스를 설정하여 입체 이미지를 렌더링합니다.

HelloCardboard는 Android NDK를 사용합니다. 모든 네이티브 메서드는 다음과 같습니다.

  • HelloCardboardApp 클래스 메서드에 고유하게 결합됨
  • 해당 클래스의 인스턴스를 만들거나 삭제합니다.

개발 환경 설정

하드웨어 요구사항:

  • Android 7.0 이상(Nougat, API 수준 24) 이상을 실행하는 Android 기기
  • Cardboard 뷰어

소프트웨어 요구사항:

  • Android 스튜디오 버전 4.2.2 이상
  • Android SDK 11.0 이상(API 수준 30) 이상
  • 최신 버전의 Android NDK 프레임워크

    설치된 SDK를 검토하거나 업데이트하려면 환경설정 > 디자인 및 동작으로 이동하세요.

    Android 스튜디오의 시스템 설정 > Android SDK

데모 앱 다운로드 및 빌드

Cardboard SDK는 각 셰이더에 사전 컴파일된 Vulkan 헤더 파일을 사용하여 빌드됩니다. 헤더 파일을 처음부터 빌드하는 단계는 여기에서 확인할 수 있습니다.

  1. 다음 명령어를 실행하여 GitHub에서 Cardboard SDK 및 HelloCardboard 데모 앱을 클론합니다.

    git clone https://github.com/googlevr/cardboard.git
  2. Android 스튜디오에서 Open an existing Android Studio Project를 선택한 다음 Cardboard SDK 및 HelloCardboard 데모 앱을 클론한 디렉터리를 선택합니다.

    코드가 Android 스튜디오의 Project 창에 표시됩니다.

  3. Cardboard SDK를 조합하려면 Gradle 탭의 cardboard/:sdk/Tasks/build 폴더 내에서 compose 옵션을 더블클릭합니다. (View > Tool Windows > Gradle)

  4. Run > Run...을 선택하고 휴대전화에서 hellocardboard-android 대상을 선택하여 HelloCardboard 데모 앱을 실행합니다.

QR 코드 스캔

기기 매개변수를 저장하려면 Cardboard 뷰어에서 QR 코드를 스캔합니다.

사용자가 'SKIP'를 누르고 이전에 저장된 매개변수가 없으면 Cardboard는 Google Cardboard v1 (Google I/O 2014에서 출시됨) 매개변수를 저장합니다.

데모 사용해 보기

HelloCardboard에서 3D 공간에서 측지 구를 찾고 수집합니다.

구를 찾고 수집하려면 다음 단계를 따르세요.

  1. 머리가 원하는 방향으로 움직여 플로팅 모양이 표시될 때까지 이동합니다.

  2. 구체를 직접 보세요. 이로 인해 색상이 변경됩니다.

  3. Cardboard 뷰어 버튼을 눌러 구를 수집하세요.

기기 구성

사용자가 톱니바퀴 아이콘을 탭하여 Cardboard 뷰어를 전환하면 nativeSwitchViewer 메서드가 호출됩니다. nativeSwitchViewerCardboardQrCode_scanQrCodeAndSaveDeviceParams를 호출하여 시청자의 QR 코드를 스캔하는 창을 엽니다. QR 코드를 스캔하면 뷰어의 렌즈 왜곡 및 기타 매개변수가 업데이트됩니다.

// Called by JNI method
void HelloCardboardApp::SwitchViewer() {
  CardboardQrCode_scanQrCodeAndSaveDeviceParams();
}

머리 추적

헤드 추적기 만들기

헤드 추적기는 HelloCardboardApp의 생성자에서 한 번 생성됩니다.

HelloCardboardApp::HelloCardboardApp(JavaVM* vm, jobject obj, jobject asset_mgr_obj) {
  Cardboard_initializeAndroid(vm, obj); // Must be called in constructor
  head_tracker_ = CardboardHeadTracker_create();
}

VrActivity가 생성되면 nativeOnCreate 메서드를 호출하여 HelloCardboardApp 클래스의 인스턴스가 생성됩니다.

public void onCreate(Bundle savedInstance) {
  super.onCreate(savedInstance);
  nativeApp = nativeOnCreate(getAssets());
  //...
}

헤드 추적기 일시중지 및 재개

헤드 추적기를 일시중지, 재개, 폐기하려면 CardboardHeadTracker_pause(head_tracker_), CardboardHeadTracker_resume(head_tracker_), CardboardHeadTracker_destroy(head_tracker_)를 각각 호출해야 합니다. 'HelloCardboard' 앱에서는 nativeOnPause, nativeOnResume, nativeOnDestroy로 호출합니다.

// Code to pause head tracker in hello_cardboard_app.cc

void HelloCardboardApp::OnPause() { CardboardHeadTracker_pause(head_tracker_); }

// Call nativeOnPause in VrActivity
@Override
protected void onPause() {
  super.onPause();
  nativeOnPause(nativeApp);
  //...
}

// Code to resume head tracker in hello_cardboard_app.cc
void HelloCardboardApp::onResume() {
  CardboardHeadTracker_resume(head_tracker_);
  //...
}

// Call nativeOnResume in VrActivity
@Override
protected void onResume() {
  super.onResume();
  //...
  nativeOnResume(nativeApp);
}

// Code to destroy head tracker in hello_cardboard_app.cc
HelloCardboardApp::~HelloCardboardApp() {
  CardboardHeadTracker_destroy(head_tracker_);
  //...
}

// Call nativeOnDestroy in VrActivity
@Override
protected void onDestroy() {
  super.onDestroy();
  nativeOnDestroy(nativeApp);
  nativeApp = 0;
}

렌즈 왜곡

Cardboard가 새 QR 코드를 스캔할 때마다 다음 코드가 저장된 매개변수를 읽고 이를 사용하여 렌즈 왜곡 객체를 만듭니다. 이 객체는 렌더링된 콘텐츠에 적절한 렌즈 왜곡을 적용합니다.

CardboardQrCode_getSavedDeviceParams(&buffer, &size);

CardboardLensDistortion_destroy(lens_distortion_);
lens_distortion_ = CardboardLensDistortion_create(
    buffer, size, screen_width_, screen_height_);

CardboardQrCode_destroy(buffer);

렌더링

Cardboard에서 콘텐츠를 렌더링하려면 다음이 필요합니다.

  • 텍스처 만들기
  • 왼쪽 및 오른쪽 눈의 뷰 및 투영 매트릭스 가져오기
  • 렌더기 만들기 및 왜곡 메시 설정
  • 각 프레임 렌더링

텍스처 만들기

모든 콘텐츠는 텍스처에 그려지고 왼쪽과 오른쪽 눈의 섹션으로 분할됩니다. 이러한 섹션은 각각 _leftEyeTexture_rightEyeTexture에서 초기화됩니다.

void HelloCardboardApp::GlSetup() {
  LOGD("GL SETUP");

  if (framebuffer_ != 0) {
    GlTeardown();
  }

  // Create render texture.
  glGenTextures(1, &texture_);
  glBindTexture(GL_TEXTURE_2D, texture_);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screen_width_, screen_height_, 0,
               GL_RGB, GL_UNSIGNED_BYTE, 0);

  left_eye_texture_description_.texture = texture_;
  left_eye_texture_description_.left_u = 0;
  left_eye_texture_description_.right_u = 0.5;
  left_eye_texture_description_.top_v = 1;
  left_eye_texture_description_.bottom_v = 0;

  right_eye_texture_description_.texture = texture_;
  right_eye_texture_description_.left_u = 0.5;
  right_eye_texture_description_.right_u = 1;
  right_eye_texture_description_.top_v = 1;
  right_eye_texture_description_.bottom_v = 0;

  //...
  CHECKGLERROR("GlSetup");
}

이러한 텍스처는 CardboardDistortionRenderer_renderEyeToDisplay에 매개변수로 전달됩니다.

왼쪽 및 오른쪽 눈의 뷰 및 투영 매트릭스 가져오기

먼저 왼쪽 및 오른쪽 눈의 눈 행렬을 검색합니다.

CardboardLensDistortion_getEyeFromHeadMatrix(
    lens_distortion_, kLeft, eye_matrices_[0]);
CardboardLensDistortion_getEyeFromHeadMatrix(
    lens_distortion_, kRight, eye_matrices_[1]);
CardboardLensDistortion_getProjectionMatrix(
    lens_distortion_, kLeft, kZNear, kZFar, projection_matrices_[0]);
CardboardLensDistortion_getProjectionMatrix(
    lens_distortion_, kRight, kZNear, kZFar, projection_matrices_[1]);

그런 다음 각 눈의 왜곡 메시를 가져와 왜곡 렌더기에 전달합니다.

CardboardLensDistortion_getDistortionMesh(lens_distortion_, kLeft, &left_mesh);
CardboardLensDistortion_getDistortionMesh(lens_distortion_, kRight, &right_mesh);

렌더기 만들기 및 올바른 왜곡 메시 설정

렌더러는 한 번만 초기화하면 됩니다. 렌더기가 생성되면 CardboardLensDistortion_getDistortionMesh 함수에서 반환된 메시 값에 따라 왼쪽 및 오른쪽 눈에 대한 새 왜곡 메시를 설정합니다.

distortion_renderer_ = CardboardOpenGlEs2DistortionRenderer_create();
CardboardDistortionRenderer_setMesh(distortion_renderer_, &left_mesh, kLeft);
CardboardDistortionRenderer_setMesh(distortion_renderer_, &right_mesh, kRight);

콘텐츠 렌더링

각 프레임에 대해 CardboardHeadTracker_getPose에서 현재 헤드 방향을 검색합니다.

CardboardHeadTracker_getPose(head_tracker_, monotonic_time_nano, &out_position[0], &out_orientation[0]);

뷰 및 투영 매트릭스와 함께 현재 헤드 방향을 사용하여 각 눈의 뷰 투영 매트릭스를 구성하고 화면에 콘텐츠를 렌더링합니다.

// Draw eyes views
for (int eye = 0; eye < 2; ++eye) {
  glViewport(eye == kLeft ? 0 : screen_width_ / 2, 0, screen_width_ / 2,
             screen_height_);

  Matrix4x4 eye_matrix = GetMatrixFromGlArray(eye_matrices_[eye]);
  Matrix4x4 eye_view = eye_matrix * head_view_;

  Matrix4x4 projection_matrix =
      GetMatrixFromGlArray(projection_matrices_[eye]);
  Matrix4x4 modelview_target = eye_view * model_target_;
  modelview_projection_target_ = projection_matrix * modelview_target;
  modelview_projection_room_ = projection_matrix * eye_view;

  // Draw room and target. Replace this to render your own content.
  DrawWorld();
}

CardboardDistortionRenderer_renderEyeToDisplay을 사용하여 왜곡 수정을 콘텐츠에 적용하고 화면에 콘텐츠를 렌더링합니다.

// Render
CardboardDistortionRenderer_renderEyeToDisplay(
    distortion_renderer_, /* target_display = */ 0, /* x = */ 0, /* y = */ 0,
    screen_width_, screen_height_, &left_eye_texture_description_,
    &right_eye_texture_description_);