適用于iOS的Google VR SDK
借助Google VR SDK for iOS瓮顽,您可以開(kāi)發(fā)廉價(jià)的虛擬現(xiàn)實(shí)(VR)工具翔横,讓您的用戶以簡(jiǎn)單癞谒,有趣和自然的方式享受VR藤滥。 熟悉OpenGL的開(kāi)發(fā)人員可以快速開(kāi)始創(chuàng)建VR應(yīng)用程序鳖粟。 SDK簡(jiǎn)化了許多常見(jiàn)的VR開(kāi)發(fā)任務(wù),包括:
鏡頭失真校正(Lens distortion correction)
空間音頻(Spatial audio)
頭部跟蹤(Head tracking)
3D校準(zhǔn)(3D calibration)
并排渲染(Side-by-side rendering)
立體幾何配置(Stereo geometry configuration)
用戶輸入事件處理(User input event handling)
我們保持硬件和軟件開(kāi)放拙绊,鼓勵(lì)社區(qū)參與和與其他地方提供的VR內(nèi)容兼容向图。
開(kāi)始
本文檔介紹了如何使用iOS版Google VR SDK(iOS SDK)創(chuàng)建自己的虛擬現(xiàn)實(shí)(VR)體驗(yàn)泳秀。
您可以使用VR查看器(例如Google Cardboard)將智能手機(jī)變成VR平臺(tái)。您的手機(jī)可以顯示3D場(chǎng)景的雙目呈現(xiàn)榄攀,跟蹤和反應(yīng)頭部運(yùn)動(dòng)嗜傅,并通過(guò)激活觸發(fā)輸入與應(yīng)用程序交互。
注意:各個(gè)制造商的智能手機(jī)VR觀眾使用不同的方法來(lái)模擬檩赢,當(dāng)用戶點(diǎn)擊手機(jī)屏幕與應(yīng)用程序交互吕嘀。這些可以包括拉動(dòng)磁鐵并按下按鈕。在一些觀看者模型上贞瞒,用戶實(shí)際上觸摸他們的手機(jī)的屏幕偶房,因此不需要模擬。為了保持簡(jiǎn)單军浆,在這個(gè)頁(yè)面上棕洋,我們將這些方法統(tǒng)稱為“激活觸發(fā)器輸入”。
iOS SDK包含空間音頻的工具乒融,遠(yuǎn)遠(yuǎn)超出了簡(jiǎn)單的左側(cè)/右側(cè)音頻提示掰盘,可提供360度聲音。 您還可以控制聲音的音調(diào)質(zhì)量 - 例如赞季,您可以使一個(gè)小型宇宙飛船的聲音與大型庆杜,地下(還是虛擬)洞穴中的聲音截然不同。
本教程中使用的演示應(yīng)用程序“Treasure Hunt”是一款基本游戲碟摆,但它演示了Google VR SDK的核心功能晃财。 在游戲中,用戶圍繞一個(gè)虛擬世界尋找和收集對(duì)象典蜕。 它演示了一些基本的功能断盛,如照明,空間運(yùn)動(dòng)和著色愉舔。 它顯示了如何設(shè)置觸發(fā)輸入钢猛,檢測(cè)用戶是否正在查看某事,設(shè)置空間音頻轩缤,以及通過(guò)為每只眼睛提供不同的視圖來(lái)渲染圖像命迈。
在開(kāi)始之前
要構(gòu)建演示應(yīng)用程序,您必須具有以下內(nèi)容:
- Xcode 7.1或更高版本火的。
- CocoaPods壶愤。 要下載并安裝,請(qǐng)轉(zhuǎn)到cocoapods.org馏鹤。
- 運(yùn)行iOS 7或更高版本的物理iPhone征椒。
下載并構(gòu)建應(yīng)用程序
-
通過(guò)運(yùn)行以下命令從GitHub存儲(chǔ)庫(kù)克隆演示應(yīng)用程序:
git clone https://github.com/googlevr/gvr-ios-sdk.git
-
在終端中,導(dǎo)航到TreasureHunt文件夾湃累,然后運(yùn)行以下命令:
pod update
CardboardSDK CocoaPod添加到TreasureHunt項(xiàng)目中勃救。 在Xcode中碍讨,打開(kāi)TreasureHunt.xcworkspace項(xiàng)目,然后單擊Run按鈕蒙秒。
下面是加載了TreasureHunt.xcworkspace項(xiàng)目的Xcode的屏幕截圖:
您已準(zhǔn)備好使用適用于iOS的Google VR SDK勃黍!
玩游戲
游戲的目標(biāo)是在3D空間中找到立方體并收集它們。 要享受空間音頻的好處晕讲,一定要戴上耳機(jī)溉躲。
查找和收集多維數(shù)據(jù)集
-
在任何方向移動(dòng)你的頭,直到立方體進(jìn)入你的視野益兄。
-
直接看立方體。 這使它變成橙色箭券。
- 激活觸發(fā)輸入净捅。 這將收集多維數(shù)據(jù)集。
代碼概述
TreasureHunt應(yīng)用程序在VR模式下為每只眼睛渲染一個(gè)OpenGL場(chǎng)景辩块。 以下部分提供有關(guān)以下任務(wù)的詳細(xì)信息:
- 實(shí)現(xiàn)一個(gè)
UIViewController
來(lái)托管GVRCardboardView
蛔六。 - 定義渲染器以實(shí)現(xiàn)
GVRCardboardViewDelegate
協(xié)議。 - 使用
CADisplayLink
對(duì)象添加渲染循環(huán)废亭。 - 處理輸入国章。
實(shí)現(xiàn)一個(gè)UIViewController來(lái)托管GVRCardboardView
TreasureHunt應(yīng)用程序?qū)崿F(xiàn)一個(gè)UIViewController
,[TreasureHuntViewController
]類承載GVRCardboardView
類的實(shí)例豆村。 創(chuàng)建TreasureHuntRenderer
類的實(shí)例并將其設(shè)置為GVRCardboardView
的GVRCardboardViewDelegate
液兽。 此外,應(yīng)用程序提供了一個(gè)渲染循環(huán)掌动,TreasureHuntRenderLoop
類四啰,驅(qū)動(dòng)GVRCardboardView
的-render
方法。
- (void)loadView {
_treasureHuntRenderer = [[TreasureHuntRenderer alloc] init];
_treasureHuntRenderer.delegate = self;
_cardboardView = [[GVRCardboardView alloc] initWithFrame:CGRectZero];
_cardboardView.delegate = _treasureHuntRenderer;
...
_cardboardView.vrModeEnabled = YES;
...
self.view = _cardboardView;
}
定義渲染器以實(shí)現(xiàn)GVRCardboardViewDelegate協(xié)議
GVRCardboardView
提供了一個(gè)用于渲染的圖面粗恢。 它使用您的渲染代碼通過(guò)GVRCardboardViewDelegate
協(xié)議來(lái)協(xié)調(diào)繪圖柑晒。 為了實(shí)現(xiàn)這一點(diǎn),TreasureHuntRenderer
類實(shí)現(xiàn)了GVRCardboardViewDelegate
:
#import "GVRCardboardView.h"
/** TreasureHunt renderer. */
@interface TreasureHuntRenderer : NSObject<GVRCardboardViewDelegate>
@end
實(shí)施GVRCardboardViewDelegate協(xié)議
要將GL內(nèi)容繪制到GVRCardboardView
上眷射,TreasureHuntRenderer
實(shí)現(xiàn)了GVRCardboardViewDelegate
:
@protocol GVRCardboardViewDelegate<NSObject>
- (void)cardboardView:(GVRCardboardView *)cardboardView
didFireEvent:(GVRUserEvent)event;
- (void)cardboardView:(GVRCardboardView *)cardboardView
willStartDrawing:(GVRHeadTransform *)headTransform;
- (void)cardboardView:(GVRCardboardView *)cardboardView
prepareDrawFrame:(GVRHeadTransform *)headTransform;
- (void)cardboardView:(GVRCardboardView *)cardboardView
drawEye:(GVREye)eye
withHeadTransform:(GVRHeadTransform *)headTransform;
- (void)cardboardView:(GVRCardboardView *)cardboardView
shouldPauseDrawing:(BOOL)pause;
@end
下面描述了willStartDrawing匙赞,prepareDrawFrame和drawEye方法的實(shí)現(xiàn)。
實(shí)現(xiàn)willStartDrawing
要執(zhí)行一次性GL狀態(tài)初始化妖碉,請(qǐng)實(shí)現(xiàn)-cardboardView:willStartDrawing :
. 使用此機(jī)會(huì)加載著色器涌庭,初始化場(chǎng)景幾何,并綁定到GL參數(shù)欧宜。 我們還在這里初始化一個(gè)GVRCardboardAudioEngine
類的實(shí)例:
- (void)cardboardView:(GVRCardboardView *)cardboardView
willStartDrawing:(GVRHeadTransform *)headTransform {
// Load shaders and bind GL attributes.
// Load mesh and model geometry.
// Initialize GVRCardboardAudio engine.
_cardboard_audio_engine =
[[GVRCardboardAudioEngine alloc]initWithRenderingMode:
kRenderingModeBinauralHighQuality];
[_cardboard_audio_engine preloadSoundFile:kSampleFilename];
[_cardboard_audio_engine start];
...
[self spawnCube];
}
實(shí)現(xiàn)prepareDrawFrame
要在渲染單個(gè)眼睛之前設(shè)置渲染邏輯脾猛,請(qǐng)實(shí)現(xiàn)-cardboardView:prepareDrawFrame :
. 任何特定于此渲染的每幀操作都應(yīng)在此處發(fā)生。 這是更新模型并清除繪圖GL狀態(tài)的好地方鱼鸠。 該應(yīng)用程序計(jì)算頭的方向和更新音頻引擎猛拴。
- (void)cardboardView:(GVRCardboardView *)cardboardView
prepareDrawFrame:(GVRHeadTransform *)headTransform {
GLKMatrix4 head_from_start_matrix = [headTransform headPoseInStartSpace];
// Update audio listener's head rotation.
const GLKQuaternion head_rotation =
GLKQuaternionMakeWithMatrix4(GLKMatrix4Transpose(
[headTransform headPoseInStartSpace]));
[_cardboard_audio_engine setHeadRotation:head_rotation.q[0]
y:head_rotation.q[1]
z:head_rotation.q[2]
w:head_rotation.q[3]];
// Update the audio engine.
[_cardboard_audio_engine update];
// Clear the GL viewport.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
}
實(shí)現(xiàn)drawEye
這是渲染代碼的核心羹铅,非常類似于構(gòu)建常規(guī)的OpenGL ES應(yīng)用程序。 以下代碼段顯示了如何實(shí)現(xiàn)drawEye
以獲得每只眼睛的視圖變換矩陣和透視變換矩陣愉昆。 注意职员,每個(gè)眼睛都會(huì)調(diào)用這個(gè)方法。 如果GVRCardboardView
沒(méi)有啟用VR模式跛溉,則眼睛設(shè)置為中心眼睛焊切。 這對(duì)于“單聲道”渲染是有用的,其可用于提供3D場(chǎng)景的非VR視圖芳室。
- (void)cardboardView:(GVRCardboardView *)cardboardView
drawEye:(GVREye)eye
withHeadTransform:(GVRHeadTransform *)headTransform {
// Set the viewport.
CGRect viewport = [headTransform viewportForEye:eye];
glViewport(viewport.origin.x, viewport.origin.y, viewport.size.width,
viewport.size.height);
glScissor(viewport.origin.x, viewport.origin.y, viewport.size.width,
viewport.size.height);
// Get the head matrix.
const GLKMatrix4 head_from_start_matrix =
[headTransform headPoseInStartSpace];
// Get this eye's matrices.
GLKMatrix4 projection_matrix = [headTransform
projectionMatrixForEye:eye near:0.1f far:100.0f];
GLKMatrix4 eye_from_head_matrix =
headTransform eyeFromHeadMatrix:eye];
// Compute the model view projection matrix.
GLKMatrix4 model_view_projection_matrix =
GLKMatrix4Multiply(projection_matrix,
GLKMatrix4Multiply(eye_from_head_matrix, head_from_start_matrix));
// Render from this eye.
[self renderWithModelViewProjectionMatrix:model_view_projection_matrix.m];
}
從此調(diào)用返回后专肪,GVRCardboardView將場(chǎng)景呈現(xiàn)到顯示。
使用CADisplayLink
添加渲染循環(huán)
渲染需要使用CADisplayLink
通過(guò)渲染循環(huán)來(lái)驅(qū)動(dòng)堪侯。 TreasureHunt應(yīng)用程序提供了一個(gè)示例渲染循環(huán):TreasureHuntRenderLoop
嚎尤。 這需要調(diào)用GVRCardboardView類的-render
方法。 這在TreasureHuntViewController
類的-viewWillAppear:
和- viewDidDisappear:
方法中處理:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_renderLoop = [[TreasureHuntRenderLoop alloc]
initWithRenderTarget:_cardboardView selector:@selector(render)];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[_renderLoop invalidate];
_renderLoop = nil;
}
處理輸入
Google VR SDK會(huì)通過(guò)激活觸發(fā)器輸入來(lái)檢測(cè)觸發(fā)的事件伍宦。 要在發(fā)生這些事件時(shí)提供自定義行為芽死,請(qǐng)實(shí)現(xiàn)-cardboardView:didFireEvent:delegate
方法。
- (void)cardboardView:(GVRCardboardView *)cardboardView
didFireEvent:(GVRUserEvent)event {
switch (event) {
case kGVRUserEventBackButton:
// If the view controller is in a navigation stack or
// over another view controller, pop or dismiss the
// view controller here.
break;
case kGVRUserEventTrigger:
NSLog(@"User performed trigger action");
// Check whether the object is found.
if (_is_cube_focused) {
// Vibrate the device on success.
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
// Generate the next cube.
[self spawnCube];
}
break;
}
}