原文鏈接 : Getting Started | Cardboard | Google Developers,譯者:Tuccuay
以前 Cardboard 也是支持在 iOS 上使用的,依靠 Unity 來實現,所以你需要用 C# 來編寫 iOS app(聽起來很奇怪對不對?) 而今天在 GDG China 看見 全新 VR 視圖:讓你的應用和網站嵌入沉浸式內容,當然迫不及待的想嘗試一下,於是翻譯了最新的文檔,大家一起來體驗一下在 iOS 上實現虛擬現實的新方式吧。
Cardboard 是 Google 一款能夠很方便讓你的手機變身 VR 設備的產品,如果你還沒有擁有它,可以去某寶買一個 ;)
這個 Cardboard SDK 可以讓你很方便的控制音頻的空間感(例如左右聲道),也可以控制響度,所以你可以讓一段對話在一個小飛船中或者一個很大的地下洞穴中表現得很不一樣。
在這個示例程序中我們完成了一個尋寶游戲,他演示了 Cardboard 的核心功能。玩家將會在一個虛擬的世界中尋找寶物。你將會學習如何使用光照、空間運動和著色等基本功能如果玩家看見了他要找的東西,將會觸發空間音效和視差效果。
基本要求
為了能夠運行這個示例程序,你至少需要滿足以下條件:
Xcode 7.1 或更高版本
CocoaPods, 訪問 CocoaPods 來安裝。
一部運行 iOS 7.0 或更高版本的 iPhone。
下載並構建 app
首先將項目 clone 到本地:
git clone https://github.com/googlesamples/cardboard-ios.git
在你的命令行中,進入到 CardboardSamples 裡的 TreasureHunt 文件夾然後執行:
pod update
這將會安裝項目所有的依賴。(注:因為眾所周知的原因,這個步驟可能會非常緩慢)
tips: CardboardSDK 在 https://www.gstatic.com/cpdc/97ceadc125bddf66-CardboardSDK-0.7.0.tar.gz
現在你應該能看見 TreasureHunt.xcworkspace 文件了,用 Xcode 運行起來應該像這個樣子:
在 Xcode 上運行 TreasureHunt
開始游戲
現在戴上你的耳機,來在這個虛擬現實的空間裡搜尋寶物吧!
尋找寶物
四處移動你的方向,直到寶物進入你的視野:
寶物已經在視野中顯示了
直視這個寶物,他將會變成橘色:
直視寶物的時候它變成橘色了
激活開關就可以收集寶物(根據 Cardboard 的不同,可能是撥動物理按鈕也可能是觸碰屏幕之類的):
代碼概覽
這個尋寶游戲(TreasureHunt)通過 OpenGL 來為你的雙眼呈現不同的訊息,他們是這樣工作的:
一個 UIViewController 擁有一個 GCSCardboardView 對象
一個渲染器遵循 GCSCardboardViewDelegate 協議
通過 CADisplayLink 對象添加一個渲染循環
捕獲輸入
讓 UIViewController 擁有一個 GCSCardboardView
這個尋寶游戲定義了一個 UIViewController,也就是 TreasureHuntViewController,他擁有一個 GCSCardboardView,並且有一個遵循 GCSCardboardViewDelegate 協議的 TreasureHuntRenderer 的實例來成為 GCSCardboardView 的代理。 此外,這個應用有一個渲染循環,TreasureHuntRenderLoop 這個類,他有一個 - render 方法來GCSCardboardView。
- (void)loadView { _treasureHuntRenderer = [[TreasureHuntRenderer alloc] init]; _treasureHuntRenderer.delegate = self; _cardboardView = [[GCSCardboardView alloc] initWithFrame:CGRectZero]; _cardboardView.delegate = _treasureHuntRenderer; ... _cardboardView.vrModeEnabled = YES; ... self.view = _cardboardView; }
定義一個遵循 GCSCardboardViewDelegate 協議的渲染器
GCSCardboardView 將會用於向你展示內容,他通過 GCSCardboardViewDelegate 協議來完成這些工作,所以 TreasureHuntRenderer 將會遵循 GCSCardboardViewDelegate協議:
#import "GCSCardboardView.h" /** TreasureHunt renderer. */ @interface TreasureHuntRenderer : NSObject@end
聲明 GCSCardboardViewDelegate 協議中的內容
為了在 GCSCardboardView 顯示內容,TreasureHuntRenderer 需要遵循 GCSCardboardViewDelegate 的這些協議:
@protocol GCSCardboardViewDelegate- (void)cardboardView:(GCSCardboardView *)cardboardView didFireEvent:(GCSUserEvent)event; - (void)cardboardView:(GCSCardboardView *)cardboardView willStartDrawing:(GCSHeadTransform *)headTransform; - (void)cardboardView:(GCSCardboardView *)cardboardView prepareDrawFrame:(GCSHeadTransform *)headTransform; - (void)cardboardView:(GCSCardboardView *)cardboardView drawEye:(GCSEye)eye withHeadTransform:(GCSHeadTransform *)headTransform; - (void)cardboardView:(GCSCardboardView *)cardboardView shouldPauseDrawing:(BOOL)pause; @end
接下來我們將實現 willStartDrawing,prepareDrawFrame,和 drawEye 方法。
實現 willStartDrawing 方法
要執行 GL(Graphics Library) 一次性初始化,實現 - cardboardView:willStartDrawing: 方法,並在其中來加載著色器初始化集合場景並添加到 GL 的參數中,並且還初始化了一個 GCSCardboardAudioEngine 實例:
- (void)cardboardView:(GCSCardboardView *)cardboardView willStartDrawing:(GCSHeadTransform *)headTransform { // Load shaders and bind GL attributes. // Load mesh and model geometry. // Initialize GCSCardboardAudio engine. _cardboard_audio_engine = [[GCSCardboardAudioEngine alloc]initWithRenderingMode: kRenderingModeBinauralHighQuality]; [_cardboard_audio_engine preloadSoundFile:kSampleFilename]; [_cardboard_audio_engine start]; ... [self spawnCube]; }
實現 prepareDrawFrame 方法
通過實現 - cardboardView:prepareDrawFrame: 方法,將可以決定將要呈現在人眼前內容的邏輯。任何對於特定幀內容的操作應該在這裡實現,在這裡更新模型並清除 GL 繪制狀態等。應用將會計算頭部的方向並更新音頻引擎。
- (void)cardboardView:(GCSCardboardView *)cardboardView prepareDrawFrame:(GCSHeadTransform *)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); }
實現 drawEye 方法
這裡將會是整個渲染代碼的核心,就像你建立一個常規的 OpenGL ES 應用一樣。下面這段代碼將為你展示如何在 - drawEye 方法中為 每個 眼球呈現場景的變換和透視效果。注意,這個方法會為每一個眼球調用,如果 GCSCardboardView 沒有啟用 VR 模式,那麼眼球將會被設置為最中間。這種單眼渲染模式也是有用的,他能在非 VR 視圖下也展現 3D 場景。
- (void)cardboardView:(GCSCardboardView *)cardboardView drawEye:(GCSEye)eye withHeadTransform:(GCSHeadTransform *)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]; }
返回這個方法的調用以後,GCSCardboardView 會將它渲染到屏幕上。
用 CADisplayLink 添加渲染循環
為了渲染內容,我們需要 CADisplayLink 來驅動一個渲染循環。 在這個尋寶游戲中,我們用到了 TreasureHuntRenderLoop 來實現這個渲染循環。 這需要調用 GCSCardboardView 中的 - render 方法。 我們在 TreasureHuntViewController 的 - viewWillAppear: and - viewDidDisappear: 方法中生成它並且在 - 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; }
捕獲輸入
Cardboard SDK 可以接受到輸入的事件(通常是撥動 Cardboard 上的按鈕),你要在用戶觸發這個按鈕的時候做一些事情,只需要實現 - cardboardView:didFireEvent 代理方法。
- (void)cardboardView:(GCSCardboardView *)cardboardView didFireEvent:(GCSUserEvent)event { switch (event) { case kGCSUserEventBackButton: // If the view controller is in a navigation stack or // over another view controller, pop or dismiss the // view controller here. break; case kGCSUserEventTrigger: 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; } }