從零開始手敲次世代遊戲引擎(四十九)

最近有些忙呀。。。首先書已經寫了3章了,現在在寫第四章。預計明年可以出版。

Android平台好久沒更新了,如鯁在喉,一直都覺得欠啥似的。那麼本篇就將從零開始手敲次世代遊戲引擎(Android特別篇)-3所寫內容同步到最新的版本。下面是在我的華為P9上的執行效果:

https://www.zhihu.com/video/969614772530102272 https://www.zhihu.com/video/969617871265107968

OpenGL ES RHI的實現

在從零開始手敲次世代遊戲引擎(Android特別篇)-3當中,我們只是實現了一個最基本的OpenGLES RHI,無法實現場景的渲染。因此,我們首先需要參照RHI/OpenGL的實現,來編寫OpenGL ES的RHI層。

OpenGL ES可以理解為OpenGL在面向嵌入式設備的一個裁剪版本。雖然由於移動市場的快速發展,手持設備的性能已經越來越強大,基本到了可以媲美早期奔騰系列個人電腦的程度。然而,手持設備的體積和能源問題並沒有發生根本性的變化,相對於同時代的桌面設備來說,性能方面仍然是比較弱的。OpenGL ES就是考慮到這些情況所設計的一套API。

總的來說OpenGL ES與OpenGL很像,因此我們可以直接將OpenGL的代碼拷貝過來,改個名字,然後嘗試編譯。然後根據報錯進行修改。

不同的版本的OpenGL也對應不同版本的OpenGL ES。為了使得移植工作儘可能簡單,我們選取OpenGL ES 3.2作為我們的目標版本。這個版本與OpenGL的兼容性較好。

事實上,在這樣的版本條件下,我們的OpenGLGraphicsManager只需要幾個非常細微的修改就可以變成OpenGLESGraphicsManager,主要的變更包括:

  1. 頭文件的變更。在我們的引擎當中,我們使用glad來動態載入OpenGL的API。但是對於嵌入式設備來說,由於其硬體固定,平台提供了GLES的頭文件,我們可以直接include這個頭文件,然後連接GLES的庫就可以了。這裡需要注意的是選擇正確的版本的頭文件;
  2. 去掉glad初始化代碼和到glad的鏈接;
  3. 將glClearDepth()改為glClearDepthf();
  4. 去掉雙精度格式的頂點緩衝區相關的代碼。GLES只支持單精度浮點;

好了,這基本上就是全部了。

除了API層面的不同之外,我們還需要修改Shader。GLES的Shader和GL的Shader是不同的,雖然其實也是很像。首先第一個不同就是,首行必須進行GLES版本的聲明,且不能在此行之前加入任何注釋和空行。

比如,下面這個是OpenGL的Shader,頭部可以有注釋:

////////////////////////////////////////////////////////////////////////////////// Filename: basic.vs////////////////////////////////////////////////////////////////////////////////#version 150

下面這個是OpenGL ES的Shader,頭部必須為版本聲明:

#version 300 es/////////////////////// INPUT VARIABLES ///////////////////////

這裡需要注意的是OpenGL與OpenGL ES在GLSL版本號系列上的區別。OpenGL GLSL的150並不代表1.5。具體的請查看官方文檔。

接下來,Shader裡面還有一個主要的區別就是對於浮點數精度的聲明。OpenGL GLSL不需要對浮點數進行精度聲明,而ES的Shader(僅限Fragment Shader,也就是PS Shader)當中需要對浮點數的精度作出聲明。

precision highp float;

這個聲明可以在Shader的頭部作為全局聲明定義,也可以在每個類型為float變數的地方進行單獨聲明。個人覺得這個功能其實對於當代的移動設備已經有些雞肋了,所以如果不是內存/帶寬十分吃緊,可以全局聲明為highp,就像我這樣。還有兩個可能的選項是mediump和lowp。

好了,經過如上的變化之後,基本上我們OpenGL到OpenGL ES的porting就完成了。理論上這樣就可以達到和目前為止PC版同樣的渲染效果了。只不過其實之前寫的代碼因為沒有實際的場景輸出,裡面包括了很多bug。所以讀者如果去實際看本篇對應的代碼的話,會發現其實還作了一些源代碼級別的修改。這些修改大致包括下面這幾類:

  1. 對Android Build系統的修改。比如我們的ispc所寫的數學庫,之前的文章也提到了,有浮點數是hardfloat還是softfloat的兼容性問題。這個問題現在在最新的ispc的GitHub當中有討論,有人給出了先使用ispc將代碼編譯成llvm的中間文件(bytecode).bc,然後用llvm的編譯器llc將其編譯成asm,再和C/C++一起編譯的方法。在本篇所對應的代碼分支當中我吸收了這個做法,但是省略了中間的asm,直接由.bc編譯成為.o文件,然後壓成library供C++部分鏈接。具體的請參考本篇代碼當中Framework/GeomMath/iscp/CMakeLists.txt的變化。注意從最終效果來說,其實和修改之前是一樣的。修改之前我們通過編譯器選項強制忽略了hardfloat與softfloat的ABI不兼容問題。這個問題雖然是致命的,但是實際上我們沒有在Java的虛擬機和我們的引擎之間直接傳遞浮點數,所以這個問題不會被觸發。但是現在既然有了更好的解決方式,我們就採用了這個方式。不過這個方式導致我們ispc部分的代碼也是採用softfloat方式編譯,從理論上來講,每次調用ispc所寫函數的時候的性能要差一些。
  2. 對原有代碼當中存在的bug的修正。
  3. 將gdbserver打包到apk當中。這是因為雖然在從零開始手敲次世代遊戲引擎(Android特別篇)-3當中我們介紹了使用gdb對程序進行遠程(非本地)調試的方法,但是實際上對於未root過的物理機,按照那個流程我們是無法將gdbserver附加到我們要調試的程序上去的。Android的安全模型使得每個apk安裝的程序都使用一個獨立的Linux賬號進行運行,因此在沒有root許可權的情況下,我們必須使用同一個賬號啟動gdbserver才可以attach上去。adb提供了run-as命令可以指定使用哪個賬號(未root時,非特權賬號)運行程序,然而每個apk對應的賬號只有執行其安裝目錄內文件的權力。所以我們必須將gdbserver也打包到apk當中,讓它也安裝到apk的安裝目錄下,才能使用那個賬號啟動它。更為系統的解釋在參考引用1當中。

代碼在參考引用2當中。

示常式序安裝包下載地址

1092-100656491-gh.circle-artifacts.com

觸摸屏事件還沒有對接,所以只能看,沒法互動。

NDK debugging without root access?

www.voidcn.com

netwarm007/GameEngineFromScratch?

github.com圖標
推薦閱讀:

TAG:遊戲引擎 | Android開發 | 嵌入式開發 |