Android NDK 用 Google Cardboard のクイックスタート

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

このガイドでは、Android 用の Cardboard SDK を使用して、独自のバーチャル リアリティ(VR)体験を創出する方法について説明します。

Cardboard SDK を使用して、スマートフォンを VR プラットフォームにすることができます。スマートフォンは、立体画像レンダリングで 3D シーンを表示し、頭の動きを追跡して反応し、ユーザーがビューアボタンを押したことを検知してアプリを操作できます。

まず、HelloCardboard を使用します。これは、Cardboard SDK の主な機能のデモを実施するデモゲームです。このゲームでは、ユーザーは仮想世界を見回り、オブジェクトを見つけて収集します。次の方法について説明します。

  • 開発環境をセットアップする
  • デモアプリのダウンロードとビルド
  • Cardboard ビューアの QR コードをスキャンして、パラメータを保存します
  • ユーザーの頭の動きをトラッキングする
  • 左右の目の視界に適切な投影射影行列を設定して、立体画像をレンダリングする

HelloCardboard は Android NDK を使用しています。すべてのネイティブ メソッドは次のとおりです。

  • HelloCardboardApp クラスメソッドに一意にバインドされている。
  • そのクラスのインスタンスを作成または削除

開発環境をセットアップする

ハードウェア要件:

  • Android 7.0(Nougat)以降を搭載した Android デバイス(API レベル 24)以降
  • Cardboard ビューア

ソフトウェア要件:

  • Android Studio バージョン 4.2.2 以降
  • Android SDK 11.0 “R”(API レベル 30)以降
  • 最新バージョンの Android NDK フレームワーク

    インストール済みの SDK を確認または更新するには、[設定] > [デザインと動作] に移動します。

    Android Studio の [System Settings] > [Android SDK]。

デモアプリのダウンロードとビルド

Cardboard SDK は、各シェーダーのコンパイル済み Vulkan ヘッダー ファイルを使用してビルドされます。ヘッダー ファイルをゼロから作成する手順については、こちらをご覧ください。

  1. 次のコマンドを実行して、GitHub から Cardboard SDK と HelloCardboard デモアプリのクローンを作成します。

    git clone https://github.com/googlevr/cardboard.git
  2. Android Studio で [Open an existing Android Studio Project] を選択し、Cardboard SDK と HelloCardboard デモアプリのクローンを作成したディレクトリを選択します。

    コードが Android Studio の [Project] ウィンドウに表示されます。

  3. Cardboard SDK をアセンブルするには、[Gradle] タブの [cardboard/:sdk/Tasks/build] フォルダにある [analyze] オプションをダブルクリックします([View] > [Tool Windows] > Gradle)。

  4. [Run] > [Run...] を選択して、hellocardboard-android ターゲットを選択して、スマートフォンで HelloCardboard デモアプリを実行します。

QR コードをスキャンする

デバイスのパラメータを保存するには、Cardboard ビューアで QR コードをスキャンします。

ユーザーが [スキップ] を押し、以前に保存したパラメータがない場合、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 のコンストラクタで 1 回作成されます。

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」を nativeOnPausenativeOnResumenativeOnDestroy でそれぞれ呼び出します。

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

レンダラを作成し、正しい歪みメッシュを設定する

レンダラの初期化が必要なのは 1 回のみです。レンダラを作成したら、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_);