Hướng dẫn nhanh về Google Cardboard dành cho Android NDK

Hướng dẫn này cho bạn biết cách sử dụng Cardboard SDK cho Android để tạo trải nghiệm Thực tế ảo (VR) của riêng bạn.

Bạn có thể dùng Cardboard SDK để biến điện thoại thông minh thành một nền tảng thực tế ảo. Điện thoại thông minh có thể hiển thị cảnh 3D bằng tính năng kết xuất lập thể, theo dõi và phản ứng với chuyển động đầu, đồng thời tương tác với các ứng dụng bằng cách phát hiện thời điểm người dùng nhấn nút xem.

Để bắt đầu, bạn sẽ sử dụng HelloCardboard, một trò chơi minh hoạ các tính năng cốt lõi của Cardboard SDK. Trong trò chơi, người dùng khám phá một thế giới ảo để tìm và thu thập các đối tượng. Tài liệu này hướng dẫn bạn cách:

  • Thiết lập môi trường phát triển
  • Tải xuống và tạo ứng dụng minh hoạ
  • Quét mã QR của một kính xem Cardboard để lưu các thông số của kính
  • Theo dõi cử động đầu của người dùng
  • Kết xuất hình ảnh lập thể bằng cách đặt ma trận phép chiếu khung hiển thị chính xác cho từng mắt

HelloCardboard sử dụng Android NDK. Mọi phương thức gốc đều:

  • Được liên kết duy nhất với một phương thức lớp HelloCardboardApp hoặc
  • Tạo hoặc xoá một thực thể của lớp đó

Thiết lập môi trường phát triển

Yêu cầu về phần cứng:

Yêu cầu về phần mềm:

  • Android Studio phiên bản 2024.1.2 "Bản cập nhật tính năng Koala" trở lên
  • SDK Android 15.0 "Vanilla Ice Cream" (API cấp 35) trở lên
  • Phiên bản mới nhất của khung Android NDK

    Để xem xét hoặc cập nhật các SDK đã cài đặt, hãy chuyển đến phần Preferences (Lựa chọn ưu tiên) > Appearance and Behavior (Giao diện và hành vi)

    System Settings (Cài đặt hệ thống) > Android SDK trong Android Studio.

Tải xuống và tạo ứng dụng minh hoạ

Cardboard SDK được tạo bằng tệp tiêu đề Vulkan được biên dịch sẵn cho từng chương trình đổ bóng. Bạn có thể xem các bước để tạo tệp tiêu đề từ đầu tại đây.

  1. Chạy lệnh sau để sao chép Cardboard SDK và ứng dụng minh hoạ HelloCardboard từ GitHub:

    git clone https://github.com/googlevr/cardboard.git
  2. Trong Android Studio, hãy chọn Open an existing Android Studio Project (Mở một dự án Android Studio hiện có), sau đó chọn thư mục mà bạn đã sao chép Cardboard SDK và ứng dụng minh hoạ HelloCardboard vào.

    Mã của bạn sẽ xuất hiện trong cửa sổ Project (Dự án) trong Android Studio.

  3. Để hợp nhất Cardboard SDK, hãy nhấp đúp vào lựa chọn assemble (hợp nhất) trong thư mục cardboard/:sdk/Tasks/build (cardboard/:sdk/Tasks/build) trong thẻ Gradle (View > Tool Windows > Gradle) (Xem > Cửa sổ công cụ > Gradle).

  4. Chạy ứng dụng minh hoạ HelloCardboard trên điện thoại bằng cách chọn Run (Chạy) > Run... (Chạy...) rồi chọn mục tiêu hellocardboard-android.

Quét mã QR

Để lưu các thông số của thiết bị, hãy quét mã QR trên kính xem Cardboard:

Nếu người dùng nhấn vào "BỎ QUA" và không có thông số nào được lưu trước đó, thì Cardboard sẽ lưu các thông số của Google Cardboard phiên bản 1 (ra mắt tại Google I/O 2014).

Xem bản minh hoạ

Trong HelloCardboard, bạn sẽ tìm kiếm và thu thập các quả cầu trắc địa trong không gian 3D.

Cách tìm và thu thập một quả cầu:

  1. Di chuyển đầu theo hướng bất kỳ cho đến khi bạn thấy một hình dạng nổi.

  2. Nhìn thẳng vào quả cầu. Việc này khiến màu sắc của biểu tượng thay đổi.

  3. Nhấn nút trên kính xem Cardboard để "thu thập" quả cầu.

Định cấu hình thiết bị

Khi người dùng nhấn vào biểu tượng bánh răng để chuyển đổi kính xem Cardboard, phương thức nativeSwitchViewer sẽ được gọi. nativeSwitchViewer cuộc gọi CardboardQrCode_scanQrCodeAndSaveDeviceParams, thao tác này sẽ mở cửa sổ để quét mã QR của người xem. Độ biến dạng ống kính và các thông số khác của người xem sẽ được cập nhật sau khi mã QR được quét.

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

Bật trình mô phỏng x86 của Android Studio

Để tạo cho trình mô phỏng x86 của Android Studio, hãy xoá dòng sau đây khỏi các tệp build.gradle trong SDKSample:

abiFilters 'armeabi-v7a', 'arm64-v8a'

Thao tác này sẽ bật tất cả ABI và làm tăng đáng kể kích thước của tệp .aar được tạo. Hãy xem bài viết ABI Android để biết thêm thông tin.

Theo dõi chuyển động của đầu

Tạo thiết bị theo dõi đầu

Trình theo dõi đầu được tạo một lần trong hàm khởi tạo của HelloCardboardApp:

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

Khi VrActivity được tạo, một thực thể của lớp HelloCardboardApp sẽ được tạo bằng cách gọi phương thức nativeOnCreate:

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

Tạm dừng và tiếp tục theo dõi đầu

Để tạm dừng, tiếp tục và huỷ trình theo dõi đầu, bạn phải gọi lần lượt CardboardHeadTracker_pause(head_tracker_), CardboardHeadTracker_resume(head_tracker_)CardboardHeadTracker_destroy(head_tracker_). Trong ứng dụng "HelloCardboard", chúng ta gọi các đối tượng này trong nativeOnPause, nativeOnResumenativeOnDestroy:

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

Biến dạng ống kính

Mỗi khi Cardboard quét một mã QR mới, mã sau đây sẽ đọc các thông số đã lưu và dùng các thông số đó để tạo đối tượng biến dạng ống kính, đối tượng này sẽ áp dụng độ biến dạng ống kính thích hợp cho nội dung được kết xuất:

CardboardQrCode_getSavedDeviceParams(&buffer, &size);

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

CardboardQrCode_destroy(buffer);

Kết xuất

Việc kết xuất nội dung trong Cardboard bao gồm những việc sau:

  • Tạo hoạ tiết
  • Lấy ma trận khung hiển thị và ma trận chiếu cho mắt trái và mắt phải
  • Tạo trình kết xuất và đặt lưới biến dạng
  • Kết xuất từng khung hình

Tạo hoạ tiết

Tất cả nội dung đều được vẽ lên một hoạ tiết, được chia thành các phần cho mắt trái và mắt phải. Các phần này được khởi chạy lần lượt trong _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");
}

Các hoạ tiết này được truyền vào dưới dạng tham số cho CardboardDistortionRenderer_renderEyeToDisplay.

Nhận ma trận khung hiển thị và ma trận chiếu cho mắt trái và mắt phải

Trước tiên, hãy truy xuất ma trận mắt cho mắt trái và mắt phải:

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

Tiếp theo, hãy lấy các lưới biến dạng cho từng mắt và truyền lưới đó đến trình kết xuất biến dạng:

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

Tạo trình kết xuất và đặt lưới biến dạng chính xác

Bạn chỉ cần khởi chạy trình kết xuất một lần. Sau khi trình kết xuất được tạo, hãy đặt lưới biến dạng mới cho mắt trái và mắt phải theo các giá trị lưới mà hàm CardboardLensDistortion_getDistortionMesh trả về.

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

Hiển thị nội dung

Đối với mỗi khung hình, hãy truy xuất hướng đầu hiện tại từ CardboardHeadTracker_getPose:

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

Sử dụng hướng đầu hiện tại với các ma trận khung hiển thị và ma trận chiếu để tạo ma trận chiếu khung hiển thị cho từng mắt và hiển thị nội dung lên màn hình:

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

Sử dụng CardboardDistortionRenderer_renderEyeToDisplay để áp dụng tính năng hiệu chỉnh độ biến dạng cho nội dung và hiển thị nội dung lên màn hình.

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