左右手坐標系
眾所周知,OpenGL使用的是右手坐標系,而Direct3D使用的是左手坐標系。
除了上面Z軸的方向不一樣外,左右手坐標系的還有其他區別:
正向旋轉方向:在左手系中用Left-Hand Rule判別,正方向是順時針方向。在右手系中,用Right-Hand Rule判別,是逆時針方向。
叉積的方向:在右手坐標系中,叉積的方向通過Right-Hand Rule確定,而在左手坐標系中,叉積的方向則由Left-Hand Rule 確定。
World Space 坐標系
World Space 是定義物理世界位置的客觀空間,Left hand 和 Right hand只是提供了兩種不同的描述空間的方法,但描述的都是同一個世界。左右手坐
標系之間的轉換並沒有改變所描述的世界,真實世界中的時鍾不管是在左手系還是右手系,都是按照順時針方向旋轉的。
View坐標系
無論是左手系還是右手系,World坐標系都描述了同一個世界空間,而你這么觀察這個客觀的世界空間,就取決於你所使用的坐標系了,這是OpenGL跟
Direct3D最主要的差別。下面先看個例子,
攝像機設置都是EyePos(0, 0, 15), LookAt(0, 0, 0), UpVec(0, 1, 0),紅色方塊的位置都是(-3, 0, 0),綠色方塊的位置都是(3, 0, 0),
那么為什么OpenGL跟Direct3D是左右顛倒的呢?個人覺得是因為View坐標系是左手還是右手的關系,在Direct3D中,View空間坐標系是Left Hand,
Z軸就是ViewDirection(Lookat - EyePosition),當攝像機在(0,0,15)的地方往原點看的時候,確實是(-3,0,0)處紅色的方塊出現在(3,0,0)
出綠色方塊的右邊。而在OpenGL中,View空間坐標系是Right Hand, Z軸其實是-ViewDirection, 所以結果就是紅色的方塊出現在綠色方塊的左邊。
由此可見,左右手坐標系其實是左右顛倒的兩個世界。
左右手坐標系的轉換
上面也看到了,左右手坐標系渲染出來的2D圖像結果是不一樣的,左右顛倒,那如何使左右手坐標系渲染出相同的2D圖像呢,有兩種方法。
方法一:反轉所有Vertex的X軸坐標和Camera的X坐標,左右手坐標系其實是左右顛倒的兩個坐標系。
方法二:對用左右手坐標系渲染出來的2D圖像進行水平方向上的反轉。
在Modern OpenGL 中使用左手坐標系
眾所周知,在固定管線的OpenGL中只能使用右手坐標系,OpenGL並沒有提供API讓你在左右手坐標系中切換。而在可編程管線的OpenGL中,就沒有那么
多限制了,所有的Vertex Transform都由你來控制。事實上,你直接把左手坐標系的頂點和矩陣傳給OpenGL Shader也是沒有問題的,在Vertex Shader
使用mul(v, matrix)變換頂點,得到的會是跟Direct3D同樣的結果。注意是跟Direct3D同樣的結果,而不是跟固定管線OpenGL相同的結果,事實上,正如
上面所說,左右手坐標系渲染的結果的左右顛倒的。唯一要區別的是Viewport的Z,在OpenGL中,裁剪空間的Z坐標是[-1. 1],而在Direct3D中是[0, 1],
而經過Viewport變化后,不管是OpenGL還是Direct3D, Z都是[0, 1], OpenGL的視口變化是 Z=(Z/W + 1) / 2 ,而Direct3D是Z=(Z/W),這對Depth
Test不受影響,Depth Test只比較相對位置遠近,但Depth Buffer的值就不同了。所以需要對project matrix做一些調整,才能讓他們寫到depth buffer中
的數值相同。具體來說,如果要讓OpenGL流水線接受D3D的project matrix,就需要乘上
相當於把project space的頂點z都作了z = z * 2 – 1的操作,所以經過viewport變換就一致了。