iOS 版 Google Cardboard 快速入门

本指南将向您介绍如何使用 Cardboard SDK for iOS 创建您自己的虚拟 现实 (VR) 体验。

您可以使用 Cardboard SDK 将智能手机转变为 VR 平台。智能手机 可以显示具有立体渲染效果的 3D 场景,跟踪头部动作并做出反应, 通过检测用户何时按下观看器按钮,与应用进行交互。

首先,您将使用 HelloCardboard,这是一个演示游戏,演示了应用的核心功能 。在游戏中,用户环顾虚拟世界寻找和收集 对象的操作。该指南将介绍如何执行以下操作:

  • 设置您的开发环境
  • 下载并构建演示版应用
  • 扫描 Cardboard 观看器的二维码以保存其参数
  • 跟踪用户头部的移动
  • 通过为每只眼睛设置正确的失真,渲染立体图像

设置您的开发环境

硬件要求:

软件要求:

下载并构建演示版应用

Cardboard SDK 是使用预编译的 Protocol Buffers 构建的。 C++ 源文件。如需从头开始构建源文件的步骤,请参阅 此处

  1. 通过以下方法从 GitHub 克隆 Cardboard SDK 和 Hello Cardboard 演示版应用: 运行以下命令:

    git clone https://github.com/googlevr/cardboard.git
  2. 在代码库根目录下运行以下命令,将 Protocol Buffers 依赖项安装到 Xcode 项目中:

    pod install
  3. 在 Xcode 中打开 Cardboard 工作区 (Cardboard.xcworkspace)。

  4. 更改应用的软件包 ID,以便与您的团队一起为应用签名。

  5. 前往 SDK >构建阶段 >将二进制文件与库关联

    1. 选择 libPods-sdk.a 并点击“-”,将其从列表中移除按钮。
    2. 点击“+”图标,将 libProtobuf-C++.a 添加到列表中按钮并将其选中。如果弹出建议使用 XCFramework 的消息,请点击“Add Anyway”。
  6. 点击运行

扫描二维码

要保存设备参数,请扫描 Cardboard 观看器上的二维码:

试用演示

在 HelloCardboard 中,您将在 3D 空间中查找和收集测地球体。

要查找并收集球体,请执行以下操作:

  1. 向任意方向转动头部,直到您看到一个浮动的球体。

  2. 直视球体。这会使它更改颜色。

  3. 按下 Cardboard 眼镜按钮即可“收集”球体。

配置设备

当用户点按齿轮图标切换 Cardboard 眼镜时,didTapSwitchButton 方法在 HelloCardboardOverlayView 中调用。

- (void)didTapSwitchButton:(id)sender {
  if ([self.delegate respondsToSelector:@selector(didTapBackButton)]) {
    [self.delegate didChangeViewerProfile];
  }
  self.settingsBackgroundView.hidden = YES;
}

这将调用 CardboardQrCode_scanQrCodeAndSaveDeviceParams,这会打开 用于扫描观看者二维码的窗口。当用户扫描二维码时,设备的 并更新失真参数。

- (void)switchViewer {
  CardboardQrCode_scanQrCodeAndSaveDeviceParams();
}

- (void)didChangeViewerProfile {
  [self pauseCardboard];
  [self switchViewer];
  [self resumeCardboard];
}

头部跟踪

创建头部跟踪器

头部跟踪器应在 HelloCardboardViewControllerviewDidLoad 方法中创建一次:

_cardboardHeadTracker = CardboardHeadTracker_create();

暂停和恢复头部跟踪器

pauseCardboardresumeCardboard 方法(在 HelloCardboardViewController 类用于暂停和恢复头部跟踪器, 。resumeCardboard 还会设置 _updateParams 标志,这会导致 要在下一个绘制调用中更新的设备参数。

- (void)pauseCardboard {
  self.paused = true;
  CardboardHeadTracker_pause(_cardboardHeadTracker);
}

- (void)resumeCardboard {
  // Parameters may have changed.
  _updateParams = YES;

  // Check for device parameters existence in app storage. If they're missing,
  // we must scan a Cardboard QR code and save the obtained parameters.
  uint8_t *buffer;
  int size;
  CardboardQrCode_getSavedDeviceParams(&buffer, &size);
  if (size == 0) {
    [self switchViewer];
  }
  CardboardQrCode_destroy(buffer);

  CardboardHeadTracker_resume(_cardboardHeadTracker);
  self.paused = false;
}

镜头失真

每次 Cardboard 扫描新的二维码时,下面的代码都会读取已保存的参数 然后使用这些物体来创建镜头失真对象,从而应用适当的镜头失真效果 附加到渲染内容:

CardboardQrCode_getSavedDeviceParams(&encodedDeviceParams, &size);

// Create CardboardLensDistortion.
CardboardLensDistortion_destroy(_cardboardLensDistortion);
_cardboardLensDistortion =
    CardboardLensDistortion_create(encodedDeviceParams, size, width, height);

// Initialize HelloCardboardRenderer.
_renderer.reset(new cardboard::hello_cardboard::HelloCardboardRenderer(
      _cardboardLensDistortion, _cardboardHeadTracker, width, height));

渲染

在 Cardboard 中呈现内容涉及以下步骤:

  • 创建纹理
  • 获取左眼和右眼的视图和投影矩阵
  • 创建渲染程序并设置失真网格
  • 渲染每一帧

创建纹理

内容会绘制到纹理上,而该纹理会拆分为左右眼区段。 这些部分分别在 _leftEyeTexture_rightEyeTexture 中进行初始化。 示例应用为双眼使用同一种纹理,但也可以为双眼创建一个单独的纹理, 呈现不同的纹理。

// Generate texture to render left and right eyes.
glGenTextures(1, &_eyeTexture);
glBindTexture(GL_TEXTURE_2D, _eyeTexture);
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, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);

_leftEyeTexture.texture = _eyeTexture;
_leftEyeTexture.left_u = 0;
_leftEyeTexture.right_u = 0.5;
_leftEyeTexture.top_v = 1;
_leftEyeTexture.bottom_v = 0;

_rightEyeTexture.texture = _eyeTexture;
_rightEyeTexture.left_u = 0.5;
_rightEyeTexture.right_u = 1;
_rightEyeTexture.top_v = 1;
_rightEyeTexture.bottom_v = 0;
CheckGLError("Create Eye textures");

这些纹理将作为参数传入 CardboardDistortionRenderer_renderEyeToDisplay

获取左眼和右眼的视图和投影矩阵

首先,检索左眼和右眼的眼睛矩阵:

CardboardLensDistortion_getEyeFromHeadMatrix(_lensDistortion, kLeft, _eyeMatrices[kLeft]);
CardboardLensDistortion_getEyeFromHeadMatrix(_lensDistortion, kRight, _eyeMatrices[kRight]);
CardboardLensDistortion_getProjectionMatrix(_lensDistortion, kLeft, kZNear, kZFar,
                                            _projMatrices[kLeft]);
CardboardLensDistortion_getProjectionMatrix(_lensDistortion, kRight, kZNear, kZFar,
                                            _projMatrices[kRight]);

接下来,获取每只眼睛的失真网格,并将其传递给失真渲染程序:

CardboardLensDistortion_getDistortionMesh(_lensDistortion, kLeft, &leftMesh);
CardboardLensDistortion_getDistortionMesh(_lensDistortion, kRight, &rightMesh);

创建渲染程序并设置正确的失真网格

渲染程序只需初始化一次。创建渲染程序后,设置新的 左眼和右眼的失真网格, CardboardLensDistortion_getDistortionMesh 函数。

_distortionRenderer = CardboardOpenGlEs2DistortionRenderer_create();
CardboardDistortionRenderer_setMesh(_distortionRenderer, &leftMesh, kLeft);
CardboardDistortionRenderer_setMesh(_distortionRenderer, &rightMesh, kRight);

呈现内容

CardboardHeadTracker_getPose 检索当前的头部方向:

CardboardHeadTracker_getPose(_headTracker, targetTime, position, orientation);
_headView =
    GLKMatrix4Multiply(GLKMatrix4MakeTranslation(position[0], position[1], position[2]),
                       GLKMatrix4MakeWithQuaternion(GLKQuaternionMakeWithArray(orientation)));

将当前的头部方向与视图和投影矩阵结合使用以组成视图 投影矩阵,然后使用它们来为每只眼睛渲染世界内容:

// Draw left eye.
glViewport(0, 0, _width / 2.0, _height);
glScissor(0, 0, _width / 2.0, _height);
DrawWorld(_leftEyeViewPose, GLKMatrix4MakeWithArray(_projMatrices[kLeft]));

// Draw right eye.
glViewport(_width / 2.0, 0, _width / 2.0, _height);
glScissor(_width / 2.0, 0, _width / 2.0, _height);
DrawWorld(_rightEyeViewPose, GLKMatrix4MakeWithArray(_projMatrices[kRight]));

使用 CardboardDistortionRenderer_renderEyeToDisplay 应用失真 更正内容,并将内容呈现到屏幕上。

CardboardDistortionRenderer_renderEyeToDisplay(_distortionRenderer, renderTarget, /*x=*/0,
                                               /*y=*/0, _width, _height, &_leftEyeTexture,
                                               &_rightEyeTexture);