標籤:

Vulkan NDC 坐標系

Vulkan NDC坐標系與其它庫的坐標系有點兒不一致,特此記錄一下。

NDC坐標系(Normalized Device Coordinates)

NDC也稱之為設備空間即標準化設備坐標系,無論引擎中的坐標系是如何,有沒有使用到相機空間、世界空間、投影空間等等,最終我們都需要統一到一個統一的坐標系下,這個坐標系也可以理解為應用程序與GPU的介面。

常規NDC坐標系

為什麼我會提到常規坐標系呢,因為在開發的這幾年當中,無論是Directx、OpenGL、WebGL甚至於喜歡搞事的蘋果,它家的Metal都是一個統一的坐標系。也就是Directx、OpgnGL、WebGL、Metal它們的NDC空間都是一致的即左手系。左手系坐標軸為:X軸朝右方、Y軸朝上方、Z軸朝前方。

屏幕坐標系

有過2D開發的同學就會注意到,在2D APP開發當中,坐標系又是以左上角為原點,X軸朝右,Y軸朝下的。Directx、OpenGL、WebGL、Metal中的NDC坐標系與屏幕坐標不一致,又導致了通過它們製作的基於GPU渲染的2D UI庫坐標系又比較難處理,又是一個坑爹的毛病。比如Cocos2D-x:

https://www.cocos.com/docs/native/v3/coordinate-system/zh.html?

www.cocos.com

至於為什麼這裡不能統一,就是因為屏幕坐標系的Y軸與NDC坐標系中的Y軸剛好相反,然而我們並不能通過線性矩陣在處理投影時將它們統一起來。通俗講就是不能通過MatrixA MatrixB Projection MatrixC的方式將NDC坐標系進行翻轉,這裡不詳細討論這個問題,大家可以自行去推導,這個的緣由我只是隨手提一下。

Vulkan NDC坐標系

說完了上面的這些坐標系之後,又來講Vulkan中的NDC坐標系,說它跟其它的有點兒不一樣就是因為它使用了右手系。除了Y軸,其它兩個軸它都跟別人一致。那麼這個Y軸進行了翻轉之後,就讓廣大開發者有點兒噁心了,因為之前當其它庫的API Caller已經熟悉了它們的坐標系,現在又搞了一個不一樣的,在開發期間就增加了一些設計上的難道。可能有人認為這個很容易解決,對設計沒有任何障礙,但是其實不然,特別是現有跨平台的圖形庫,想要支持Vulkan就得要考慮坐標系的適配問題。

Vulkan三角形示例

我們先來看一下一個案例,來對比一下不同之處。以下是Vulkan裡面的一個三角形的數據定義:頂點定義代碼地址

// 前3為坐標點,後3為頂點色。
std::vector<Vertex> vertices = {
{ { 1.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
{ { -1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
{ { 0.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }
};

// 索引數據
std::vector<uint16> indices = { 0, 1, 2 };

大家注意頂點坐標以及索引順序,然後我們來看一下顯示的三角形。

OpenGL三角形示例

然後我們再來看一下OpenGL裡面的三角形定義。代碼定義

// An array of 3 vectors which represents 3 vertices
static const GLfloat g_vertex_buffer_data[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};

大家發現沒有,同樣的三角形數據定義卻不一樣。那麼這個問題如果要解決的話,要怎麼解決呢?我在這裡列舉一下常規解決方式:

解決方式1

翻轉投影矩陣,我們可以讓我們的投影矩陣Projection進行垂直翻轉,處理也很簡單,Y軸縮放乘以-1即可。但是這樣處理之後就會引入一些其它的問題,第一個是三角形剔除的問題,因為我們進行了垂直翻轉,那麼我們的三角形順序就變了,以前是順時針的現在就變成了逆時針。然而我們的正面、背面剔除又是根據這個來的,那麼我們翻轉了投影矩陣之後,我們也需要修改我們的剔除方式、順時針逆時針方向。第二問題就是法線的問題,投影矩陣只是針對頂點坐標,不是針對頂點屬性的,翻轉了投影矩陣也只是翻轉了頂點坐標系,頂點屬性並未翻轉,那麼跟頂點相關的矢量也要一併翻轉。這種方式處理起來就異常麻煩了,可能整個引擎架構都需要隨之調整。

解決方式2

既然翻轉投影矩陣異常麻煩,那麼我們可以考慮在頂點Shader裡面進行翻轉,在頂點Shader裡面,我們只需要對頂點輸入的時候做一次轉換,將頂點坐標以及附帶的矢量一起翻轉,然後將包裝之後的屬性提供出來使用。

解決方式3

由於這樣需求的人太多了,導致Vulkan在發布之後沒過多久就立馬推出了擴展,可以讓開發者將Viewport整個進行翻轉,但是這樣是個擴展,也就是Android或者IOS、MacOS不一定能用。

解決方式4

妥協,直接使用這個坐標系,可能初期不太習慣,後期就習慣了。雖然習慣了,但是這個也是比較麻煩的,對於已經開發過其它3D引擎的人來說,他們手裡面已經握著一些常用庫了。比如模型解析庫,解析出來的模型坐標系已經是跟左手細對應上了,結果現在解析出來放到Vulkan裡面卻發現模型翻轉了。只是靜態模型其實還好處理,關鍵麻煩的是我們還有骨骼動畫,還涉及到骨骼是不是需要翻轉,翻轉了之後能不能用等等問題。

目前這個問題我還沒想好我自己要怎麼處理,我可能會嘗試一下2、4的方式,看看會不會遇到一些其它的坑爹問題,如果有的話到時候再來記錄一下吧。

推薦閱讀:

第 9 章. Drawing Objects 繪製對象
入門:調試Vulkan Samples(九)——Shader
vulkan踩坑記02——嚴肅官方與函數們
vulkan踩坑記04——海般的知識
unity如何讓cg語言支持的metal和vulkan?

TAG:Vulkan | 3D引擎 |