【Oculus SDK】星空 VR
星空 VR
介紹
想做一個,類似於造物主的遊戲(所以英文叫demiurge)。玩家能夠往這個地方扔星球。本來希望有好一點的互動碰撞效果結果後來懶了(……)所以最後做出來不太像遊戲了233也是很慘的。
實現
環境配置
一開始在選擇配置的時候考慮的一個是Oculus提供的官方SDK,另一個就是基於SteamVR的OpenVR SDK。
適當權衡之後,選擇了Oculus官方提供的SDK。因為看起來文件相對詳細而且排版比較清楚,有比較具體的例程。
當然Oculus官網全程需要翻牆……這一點確實非常的不友好。客戶端大概下載了五六個小時,後面使用者登入又折騰了蠻久。確實對於國內開發者不太友好。
此外,Oculus有一系列的Unity的中文教程,但是基本上沒有PC SDK的中文資料料。整個過程的主要參考是官網文件和官網提供的教程。
模型建立
球
我採用對球分段的方式,用四邊形(三角面片)去逼近球。
生成球的函式,傳入引數是球的中心、球的半徑、theta方向上的分段數和phi方向上的分段數。theta和phi的值和座標的對應關係為:
在theta和phi分段都為5的時候,能夠清楚的看到扭曲的輪廓這個數值設定為20之後,就能感覺到這是一個非常完整的球。
由於這裡沒有陰影所以表現效果會差一點orz
LOD
因為在這個場景中,球是用三角面片去逼近的,所以一旦球在近處,失真會非常的明顯。
所以這裡決定做一下離散的LOD。離散的相對實現比較容易。實現的時候,預先儲存多個模型,可以直接根據之前到相機的位置以及球的半徑確定採用哪個模型。
因為半徑在螢幕上決定了三角面片的邊的長度,所以取
由k的取值,來確定模型的精度。當然也遇到了離散模型所可能會遇到的突變的問題。 下圖是在測試場景中,極小的位移中LOD變化(最後實現的時候對數值做了一定的調整所以不會那麼明顯)。
相機
VR遊戲中的相機,和常規的3D遊戲不同之處在於需要單獨確定兩個相機的位置。根據玩家左眼和右眼的位置,單獨確定左眼和右眼看到的物體,分別渲染。 但是這一步相對比較常規,在例程中提供了左右眼單獨渲染以及提供視覺化視窗的操作。
此外在具體實現的時候,對於一些特殊物體,比如天空盒和UI,這種位置會跟隨玩家的位置變化的物體(但是旋轉不會)。這就需要我們操作觀察矩陣。需要將光差矩陣移除位移部分但是保留矩陣的旋轉部分。
glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
我們通過取4x4矩陣左上角的3x3矩陣來移除變換矩陣的位移部分。我們可以將觀察矩陣轉換為3x3矩陣(移除位移),再將其轉換回4x4矩陣,來達到類似的效果。
紋理生成及對映
紋理生成
紋理主要採用的是perlin噪聲。perlin噪聲是一種經典的自然噪聲生成演算法。 我們給影象賦予一個底色,然後利用噪聲的性質,我們使用2D和1D的perlin噪聲,分別生成不同的圖。
1D的perlin噪聲:
2D的perlin噪聲:
使用者可以通過介面選擇分段、顏色、以及選擇不同的隨機數來生成不同的紋理。 隨機數的設定,主要是基於perlin噪聲本身三維的特性,對於確定好的xy設定不同的z能夠得到不同的值。
perlin噪聲用到是stb庫。這個庫同時也用於這個專案中的影象讀寫。
紋理對映
由於之前在生成紋理的時候,通過下述公式
已經把球上的點和theta和phi對應起來。只需要簡單的把theta和phi對映到[0,1]區間內,就可以實現紋理的對映。我們分別對已有的紋理以及我們生成的紋理進行了測試,對映結果如下:
地球的對映紋理
太陽的對映紋理
1D perlin噪聲
2D Perlin噪聲
互動設計
互動介面
在設計介面的形式的時候,嘗試過多種形式。
第一種是在視線的右上方之類的懸浮一個對話方塊,這樣使用者可選擇。 第二種是固定在相對使用者的某個特定位置上,類似於天空盒。穩定的位於使用者某個特定方向的特定距離上。 第三種是位於某個固定位置上,但是條件轉向使得其始終面向使用者。
主流的VR遊戲中這三種都有用到。在嘗試的時候覺得第一種相對會讓使用者感覺不真實,所以在這個過程中使用了後面兩種方法。
在該介面是長期需要使用的時候,採用第二種形式。比如幫助介面和星球設定的介面。 這個介面一直位於使用者的一側。
在有非常明確的對應關係,比如顯示星球的速度的時候,採用第三種方式。 這個速度條一直位於星球的下方,會隨著使用者運動轉向保證正對使用者。
此外這裡的感受就是,一定不要拿著個頭盔直接通過螢幕的輸出來判斷是否合適。一定要自己戴上頭盔去試一試,這樣才能知道是不是真的合適。有的時候在螢幕上看感覺大小蠻合理的,但是戴上頭盔之後發現這個UI鋪天蓋地我還得轉頭才能看清楚整個UI介面。
這裡還有遇到的一個問題,我用的是stb庫做的影象讀寫,但是到文字輸出那一塊發現找不到對應的標頭檔案。考慮用SDL庫做輸出,但是後期轉到這裡成本也比較高就放棄了……所以最後看到的文字都是用影象的形式預先計算的。
GUI雖然難度不大,但是計算和設計非常的麻煩,需要一些複雜的定位以及和美工的不斷協調。
手柄事件
我們在設計的時候,決定左手確定使用者的移動,右手確定互動和響應。
Oculus Touch手柄如下
左手上方的撥盤決定使用者的前進/後退移動。
右手撥盤來調整使用者的選擇,左右開啟下一級目錄,上下選擇這一級目錄的不同內容,最後通過AB按鍵來調整現在選中目標的數值。 我們的右手手柄的響應分為兩段。用右手手柄操作星球設定的時候,為了避免在一次按鍵的過程中撥盤的多次響應(因為它主要作用可能不是這樣的選擇),設定一定的時間響應一次,這樣可以避免選中區域瘋狂跳動。而在用手柄設定星球的位置和速度的時候,連續的響應能給使用者更好的體驗。
左手只有相對簡單的移動事件。
模擬
運動
使用者可以在互動介面上自己定義自轉速度和星球的體積,這之後星球會按照預定好的資訊自傳。
接下來使用者可以設定星球到主天體(這個宇宙的中心,類似於太陽對太陽系的概念)的距離以及星球的初始速度。
然後會按照
的基本公式計算該星球繞主天體運動的軌道。在每次計算的時候不斷根據加速度更新速度,再根據速度更新位置。得到的結果會是一個比較接近真實情況的橢圓。測試的時候單個星球的結果如下:
後面
說來蠻慚愧的,畢竟數字媒體這個專業圖形學相關的課程非常的多,但這卻是我第一次用可程式設計管線寫的專案。果不其然被shader弄的很狼狽,因為完全沒有經驗根本無從知道到底是哪一個部分出現了疏忽。再加上我選擇從例程入手,這樣一方面確實減少了學習Oculus SDK相關的工作量,卻也被這份可擴充套件性較差的程式碼弄得非常頭大。
感覺選擇用OpenGL從底層寫起,初期確實壓力比較大,而且最後實現效果受到各方面的約束,我們必須純靠程式碼來實現每個功能,但是個人覺得這是一次不錯的嘗試。
當然也有很多的不足。我覺得我們這個設定一個架空的宇宙為背景有一定的取巧,因為我衡量自己現有的能力還不足以寫出真實感相對較強的VR專案。
不足及改進
感覺最後還有很多欠缺的地方。這部分的欠缺主要原因是經驗不足。
Shader
Shader的問題很多。最後實現的時候我們幾乎沒有調整已有的shader,所以也就沒有實現更加具體效果,比如陰影、光照貼圖等種種;而且計算效率相對較低,如果能夠熟悉shader的語法的話很多CPU的運算可以轉移到GPU當中,這樣肯定會提高渲染的效率。
這一部分主要的限制在於Oculus的例程提供的是GLSL1.50,而我之前所接觸的是3.30,調整的時候遇到蠻多問題。並且shader的顯示比較難,很難知道具體是哪一部分出現的問題。
此外還有原因是,例程的可拓展性較差,而我們的總的結構是參考例程的。如果下一次要寫這樣的程式,應該會考慮自己重新打整個程式的框架。
加速
我在寫之前估計,對於星球的物理運動可以和渲染顯示分成不同的執行緒。Oculus官方提供了計算幀率等等的文件以及對於加速的一些建議。但是因為這個專案的話幀率並不是主要的瓶頸,所以沒有很注意做這些方面。如果以後開發大型專案的話這確實是需要考慮的一點。I/O和記憶體分配都還有很大的進步空間。