このガイドでは、Android 用 Cardboard SDK を使用して独自のバーチャル リアリティ(VR)コンテンツを作成する方法について説明します。
Cardboard SDK を使用すると、スマートフォンを VR プラットフォームに変えることができます。スマートフォンは、立体視レンダリングで 3D シーンを表示し、頭の動きを追跡して反応し、ユーザーがビューア ボタンを押したことを検出してアプリを操作できます。
まず、Cardboard SDK のコア機能をデモするゲーム HelloCardboard を使用します。ゲームでは、ユーザーは仮想世界を見回してオブジェクトを探し、収集します。このガイドでは、次の方法について説明します。
- 開発環境を設定する
- デモアプリをダウンロードしてビルドする
- Cardboard ビューアの QR コードをスキャンしてパラメータを保存する
- ユーザーの頭の動きをトラッキングする
- 各眼の正しいビュー投影行列を設定して立体視画像をレンダリングする
HelloCardboard は Android NDK を使用します。すべてのネイティブ メソッドは次のとおりです。
HelloCardboardApp
クラスメソッドに一意にバインドされているか、- そのクラスのインスタンスを作成または削除する
開発環境を設定する
ハードウェア要件:
- Android 8.0「Oreo」(API レベル 26)以降を搭載した Android デバイス
- Cardboard ビューア
ソフトウェア要件:
- Android Studio バージョン 2024.1.2「Koala Feature Drop」以降
- Android SDK 15.0「バニラ アイスクリーム」(API レベル 35)以降
Android NDK フレームワークの最新バージョン
インストールされている SDK を確認または更新するには、[Preferences] > [Appearance and Behavior] に移動します。
Android Studio の [System Settings] > [Android SDK]。
デモアプリをダウンロードしてビルドする
Cardboard SDK は、各シェーダーの事前コンパイル済み Vulkan ヘッダー ファイルを使用してビルドされます。ヘッダー ファイルをゼロからビルドする手順については、こちらをご覧ください。
次のコマンドを実行して、GitHub から Cardboard SDK と HelloCardboard デモアプリのクローンを作成します。
git clone https://github.com/googlevr/cardboard.git
Android Studio で、[Open an existing Android Studio Project] を選択し、Cardboard SDK と HelloCardboard デモアプリがクローンされたディレクトリを選択します。
コードは Android Studio の [Project] ウィンドウに表示されます。
Cardboard SDK をアセンブルするには、Gradle タブ([View] > [Tool Windows] > [Gradle])の cardboard/:sdk/Tasks/build フォルダにある assemble オプションをダブルクリックします。
[Run] > [Run...] を選択して
hellocardboard-android
ターゲットを選択し、スマートフォンで HelloCardboard デモアプリを実行します。
QR コードをスキャンする
デバイス パラメータを保存するには、Cardboard ビューアの QR コードをスキャンします。
ユーザーが [スキップ] を押し、以前に保存されたパラメータがない場合、Cardboard は Google Cardboard v1(Google I/O 2014 でリリース)のパラメータを保存します。
デモを試す
HelloCardboard では、3D 空間で測地線球を探して集めます。
球を見つけて収集するには:
浮遊する図形が表示されるまで、頭を任意の方向に動かします。
球体を正面から見ます。これにより、色が変化します。
Cardboard ビューアのボタンを押して球体を「収集」します。
デバイスを設定する
ユーザーが歯車アイコンをタップして Cardboard ビューアを切り替えると、nativeSwitchViewer
メソッドが呼び出されます。nativeSwitchViewer
呼び出しは CardboardQrCode_scanQrCodeAndSaveDeviceParams
を呼び出し、視聴者の QR コードをスキャンするウィンドウを開きます。QR コードがスキャンされると、視聴者のレンズの歪みなどのパラメータが更新されます。
// Called by JNI method
void HelloCardboardApp::SwitchViewer() {
CardboardQrCode_scanQrCodeAndSaveDeviceParams();
}
Android Studio x86 エミュレータを有効にする
Android Studio x86 エミュレータ用にビルドするには、SDK と Sample の build.gradle
ファイルから次の行を削除します。
abiFilters 'armeabi-v7a', 'arm64-v8a'
これにより、すべての ABI が有効になり、生成される .aar
ファイルのサイズが大幅に増加します。詳しくは、Android ABI をご覧ください。
ヘッド トラッキング
ヘッド トラッカーを作成する
ヘッド トラッカーは 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」アプリでは、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_);