本指南将向您介绍如何使用 Cardboard SDK for iOS 打造您自己的虚拟实境 (VR) 体验。
您可以使用 Cardboard SDK 将智能手机转变为 VR 平台。智能手机可以通过立体呈现来显示 3D 场景,跟踪头部移动并做出响应,并通过检测用户何时按下观看器按钮来与应用进行互动。
首先,您将使用 HelloCardboard,这是一款演示游戏,演示了 Cardboard SDK 的核心功能。在游戏中,用户环顾虚拟世界来查找和收集物品。该指南介绍了如何执行以下操作:
- 设置开发环境
- 下载并构建演示版应用
- 扫描 Cardboard 观看器的二维码以保存其参数
- 跟踪用户头部活动
- 为每只眼睛设置正确的失真,渲染立体图像
设置开发环境
硬件要求:
- 运行 iOS 12.0 或更高版本的 iPhone
- Cardboard 观看器
软件要求:
- Xcode 12.5 或更高版本
- CocoaPods 1.9 或更高版本
下载并构建演示版应用
Cardboard SDK 使用预编译的 Protocol Buffers C++ 源文件进行构建。如需查看从头开始构建源文件的步骤,请点击此处。
运行以下命令,从 GitHub 克隆 Cardboard SDK 和 Hello Cardboard 演示版应用:
git clone https://github.com/googlevr/cardboard.git
在代码库根目录下运行以下命令,将 Protocol Buffers 依赖项安装到 Xcode 项目中:
pod install
在 Xcode 中打开 Cardboard 工作区 (
Cardboard.xcworkspace
)。请更改应用的软件包 ID,以便与您的团队一起为应用签名。
依次前往 SDK > Build Phases > Link Binary With Libraries
- 选中
libPods-sdk.a
并点击“-”按钮,即可将其从列表中移除。 - 点击“+”按钮并选择“
libProtobuf-C++.a
”,将其添加到列表中。如果系统弹出提示您使用 XCFramework 的消息,请点击“仍然添加”。
- 选中
点击运行。
扫描二维码
若要保存设备参数,请在 Cardboard 观看器上扫描 QR 码:
试用演示版
在 HelloCardboard 中,你可以在 3D 空间中查找并收集测地球体。
要找到并收集球体,请执行以下操作:
朝任意方向移动头部,直到看到漂浮的球体为止。
直视球体。这会导致其更改颜色。
按下 Cardboard 眼镜按钮以“收集”球体。
配置设备
当用户点按齿轮图标切换 Cardboard 眼镜时,HelloCardboardOverlayView
中会调用 didTapSwitchButton
方法。
- (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];
}
头部跟踪
创建头部跟踪器
头部跟踪器在 HelloCardboardViewController
的 viewDidLoad
方法中创建一次:
_cardboardHeadTracker = CardboardHeadTracker_create();
暂停和恢复头部跟踪器
HelloCardboardViewController
类中的 pauseCardboard
和 resumeCardboard
方法分别用于暂停和恢复头部跟踪器。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);