以前 Cradboard 也是支持在 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 到本地:
1
git clone https://github.com/googlesamples/cardboard-ios.git
-
在你的命令行中,進入到
CardboardSamples
裏的TreasureHunt
文件夾然後執行:1
pod update
-
這將會安裝項目所有的依賴。(注:因爲衆所周知的原因,這個步驟可能會非常緩慢)
tips:CardboardSDK
在https://www.gstatic.com/cpdc/97ceadc125bddf66-CardboardSDK-0.7.0.tar.gz
現在你應該能看見TreasureHunt.xcworkspace
文件了,用 Xcode 運行起來應該像這個樣子:
開始遊戲
現在戴上你的耳機,來在這個虛擬現實的空間裏搜尋寶物吧!
尋找寶物
代碼概覽
這個尋寶遊戲(TreasureHunt
)通過 OpenGL
來爲你的雙眼呈現不同的訊息,他們是這樣工作的:
-
一個
UIViewController
擁有一個GCSCardboardView
對象 -
一個渲染器遵循
GCSCardboardViewDelegate
協議 -
通過
CADisplayLink
對象添加一個渲染循環 - 捕獲輸入
讓 UIViewController
擁有一個 GCSCardboardView
這個尋寶遊戲定義了一個 UIViewController
,也就是 TreasureHuntViewController
,他擁有一個 GCSCardboardView
,並且有一個遵循GCSCardboardViewDelegate
協議的 TreasureHuntRenderer
的實例來成爲 GCSCardboardView
的代理。
此外,這個應用有一個渲染循環,TreasureHuntRenderLoop
這個類,他有一個 -
render
方法來GCSCardboardView
。
1 2 3 4 5 6 7 8 9 10 11 |
- (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
協議:
1 2 3 4 5 6 |
#import "GCSCardboardView.h" /** TreasureHunt renderer. */ @interface TreasureHuntRenderer : NSObject<GCSCardboardViewDelegate> @end |
聲明 GCSCardboardViewDelegate
協議中的內容
爲了在 GCSCardboardView
顯示內容,TreasureHuntRenderer
需要遵循 GCSCardboardViewDelegate
的這些協議:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@protocol GCSCardboardViewDelegate<NSObject> - (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
實例:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (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 繪製狀態等。應用將會計算頭部的方向並更新音頻引擎。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- (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 場景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
- (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:
方法中銷燬它。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (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; } |
捕獲輸入
Cradboard SDK 可以接受到輸入的事件(通常是撥動 Cardboard 上的按鈕),你要在用戶觸發這個按鈕的時候做一些事情,只需要實現-
cardboardView:didFireEvent
代理方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- (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; } } |
- 聲明: 除非註明,Tuccuay's Blog 文章均爲原創,轉載請以鏈接形式標明本文地址。
- 本博客原創文字只代表本人某一時間內的觀點或結論,與本人當時或現在所在組織沒有任何關係。
- BY-NC-SA 本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
- 第三方若用於商業用途的轉載,須取得本人授權。
- 本文作者:Tuccuay
- 本文地址:https://www.tuccuay.com/2016/03/cardboard-ios-sdk-getting-started/