坐標(biāo)系和矩陣是opengl的基礎(chǔ)也是難點尉剩,本文總結(jié)了opengl的各種坐標(biāo)系和矩陣飞苇,如果有錯誤歡迎指正入挣。
1.什么是opengl和opengl es
OpenGL(全寫Open Graphics Library)是指定義了一個跨編程語言递惋、跨平臺的編程接口規(guī)格的專業(yè)的圖形程序接口访惜。OpenGL是個與硬件無關(guān)的軟件接口,可以在不同的平臺如Windows 95宙址、Windows NT轴脐、Unix、Linux抡砂、MacOS大咱、OS/2之間進(jìn)行移植。因此注益,支持OpenGL的軟件具有很好的移植性碴巾,可以獲得非常廣泛的應(yīng)用。
Opengl2.0開始支持glsl(OpenGL Shading Language)丑搔,opengl1.x是固定渲染管線的已經(jīng)被淘汰無需考慮厦瓢。Opengles是OpenGL裁剪定制而來的,專為手機(jī)啤月,游戲機(jī)等嵌入式設(shè)備而設(shè)計煮仇,在安卓設(shè)備上的opengl就是opengles。
安卓使用GLSurfaceView封裝和簡化opengl的開發(fā)谎仲,它幫助我們建立單獨的opengl線程欺抗,并且支持連續(xù)或者按需渲染,它使用EGL來處理屏幕設(shè)置强重,Egl是OpenGL與底層窗口系統(tǒng)之間的接口绞呈。
- It provides a dedicated render thread for OpenGL so that the main thread is not stalled.
- It supports continuous or on-demand rendering.
- It takes care of the screen setup for you using EGL, the interface between OpenGL and the underlying window system.
2.opengl的各種坐標(biāo)系和矩陣
坐標(biāo)系
-
安卓設(shè)備坐標(biāo)系
屏幕左上點為原點贸人,x軸向右,y軸向下佃声,跟隨屏幕方向變化而變化艺智。
-
紋理坐標(biāo)系 uv coordinates
2d紋理坐標(biāo)系也就是UV坐標(biāo)系,水平方向是U圾亏,垂直方向是V十拣,通過這個平面的,二維的UV坐標(biāo)系志鹃。我們可以定位圖象上的任意一個象素夭问。左下角為原點,U軸向右曹铃,V軸向上缰趋,范圍[0,1]。
-
模型坐標(biāo)系 object coordinates
歸一化的右手坐標(biāo)系陕见,以模型內(nèi)某一點為原點建立的坐標(biāo)系秘血,其它模型內(nèi)的點的位置可以用相對于模型內(nèi)的原點來表示的坐標(biāo)系,該坐標(biāo)系僅在模型內(nèi)有效评甜。
-
世界坐標(biāo)系 world coordinates
右手坐標(biāo)系灰粮,屏幕中心點位原點x軸向右,y軸向上忍坷,z軸向屏幕外粘舟。可以理解為無限大的坐標(biāo)系佩研,用來描述物體或者光源的位置柑肴,跟隨屏幕方向變化而變化。韧骗。
-
視點坐標(biāo)系 eye coordinates
以視點(相機(jī))為原點,以視線的<u>負(fù)方向為Z軸正方向</u>的坐標(biāo)系零聚,注意是相機(jī)看向的其實是
-z
軸袍暴。 -
齊次坐標(biāo)系 clip coordinates
齊次坐標(biāo)就是用N+1維來代表N維坐標(biāo),在opengl中以第四個值w表示,齊次坐標(biāo)系表示同一個點并不唯一[8,4,2]隶症、[4,2,1]表示的都是二維點[4,2]政模。
齊次坐標(biāo)系可以使用(x,y,z,w)表示一個點,也可以表示向量蚂会。當(dāng)w==1時表示點(僅限eye坐標(biāo)系及之前的坐標(biāo)系)淋样,當(dāng)w==0時表示向量。
齊次坐標(biāo)系使得點和向量可以用統(tǒng)一的vec4來表示胁住。
-
opengl設(shè)備標(biāo)準(zhǔn)化坐標(biāo)系 ndc coordinates
Normalized device coordinates(ndc坐標(biāo)系)心赶,采用的是左手三維坐標(biāo)系(也就是z軸其實是指向屏幕里的)揭蜒,三個坐標(biāo)軸都是[-1,1]蛛勉,是opengl服務(wù)端唯一理解的內(nèi)部坐標(biāo)系,跟隨屏幕方向變化而變化娱挨。
opengl是server-client模式,應(yīng)用程序是客戶端捕犬,圖形硬件廠商提供的OpenGL實現(xiàn)是服務(wù)器跷坝。而client也就是應(yīng)用程序內(nèi)部采用的都是右手坐標(biāo)系,只是是在使用Ortho或者Perspective來生成投影矩陣的時候碉碉,近遠(yuǎn)視點分別加了負(fù)號做了轉(zhuǎn)換柴钻。
glViewport(x,y,width,height)用來函數(shù)指明標(biāo)準(zhǔn)坐標(biāo)系和屏幕坐標(biāo)映射計算關(guān)系,在安卓中一般用在onSurfaceChanged的回調(diào)里垢粮。一般這個計算不需要我們來做贴届,此處只是作為補(bǔ)充說明。
?
矩陣
opengl的矩陣是4*4的矩陣足丢,可以用16個元素的數(shù)組表示粱腻,opengl采用column-major order(列主序矩陣),也就是一列一列從數(shù)組讀取數(shù)據(jù)斩跌。由于列主序的存在绍些,導(dǎo)致4×4矩陣作用于vector4(例如shader中的位置、顏色等耀鸦,實際為4×1矩陣)柬批,所以vector4只能左乘變換矩陣(線性代數(shù)矩陣乘法規(guī)定,它只有在第一個矩陣的列數(shù)(column)和第二個矩陣的行數(shù)(row)相同時才有意義)袖订。
-
modelMatrix模型矩陣:
模型矩陣可以包括對模型的縮放氮帐、旋轉(zhuǎn)、平移操作洛姑。先縮放再旋轉(zhuǎn)再平移上沐,由于矩陣乘法不滿足交換律,順序不能變化楞艾。例如先向左轉(zhuǎn)再走兩步参咙,和先走兩步再像左轉(zhuǎn)是完全不一樣的結(jié)果。
<u>如果要改變順序一定要清楚其帶來的影響</u>
scaleMatrix在矩陣中的表示:
rotationMatrix矩陣中比較復(fù)雜硫眯,暫時不列出
translateMatrix矩陣變換
綜上蕴侧,最終的modelMatrix = TranslationMatrix * RotationMatrix * ScaleMatrix,從右向左乘,先變換的最先乘两入。
通過左乘模型矩陣可以將局部物體坐標(biāo)系坐標(biāo)轉(zhuǎn)化為世界坐標(biāo)系的坐標(biāo)净宵。
vec4 worldCoordinateVector = modelMatrix * modelCoordinateVector;
-
viewMatrix視圖矩陣
setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)
用來生成視圖矩陣,主要規(guī)定視點位置、焦點位置择葡、相機(jī)y軸up朝向紧武。
世界坐標(biāo)系的坐標(biāo)值左乘視圖矩陣可以將世界坐標(biāo)系轉(zhuǎn)化為視點相機(jī)坐標(biāo)系。
vec4 cameraCoordinateVector = viewMatrix * worldCoordinateVector;
理論上viewMatrix和modelMatrix是可以相互替代的刁岸,如變相機(jī)位置向左移動5厘米和改變物體位置像右移動5厘米都可以實現(xiàn)相同的結(jié)果脏里,但存在多個物體的話顯然移動物體合適。
-
projectMatrix投影矩陣
投影矩陣并不是用來計算射線碰撞的,也不是所謂的將3d物體投射到2d平面上虹曙。投影矩陣真正作用來是把相機(jī)坐映射為標(biāo)準(zhǔn)的[-1,1]的齊次坐標(biāo)迫横,也就是帶w值的標(biāo)準(zhǔn)設(shè)備坐標(biāo)系,并且可以clip視錐之內(nèi)的點酝碳,投影內(nèi)的點記為vertex(clip)矾踱。
opengl負(fù)責(zé)將齊次坐標(biāo)系轉(zhuǎn)化為標(biāo)準(zhǔn)坐標(biāo),也就是PerspectiveDivide(透視除)由opengl內(nèi)部調(diào)用疏哗,無需我們關(guān)心呛讲。透視除其實就是將x/y/z值分別除以w來得到屏幕標(biāo)準(zhǔn)坐標(biāo)。
vertex(ndc) = PerspectiveDivide(vertext(clip));
一旦拿到ndc坐標(biāo)返奉,opengl會使用公式將它映射到viewport屏幕坐標(biāo)贝搁,屏幕上就顯示了當(dāng)前點。
關(guān)于w
w主要是用在透視投影中的芽偏,在視點坐標(biāo)系下其值一般初始化為1,而經(jīng)過透視矩陣變換之后雷逆,其中會變?yōu)?z(注意負(fù)號,因為需要轉(zhuǎn)為左手坐標(biāo)系),經(jīng)過正交投影變換后其值仍是原值污尉,即1膀哲。
所謂透視矩陣,就是通過各個分量divide by w來實現(xiàn)近大遠(yuǎn)小的透視效果的被碗。w雖然對正交矩陣沒有什么用某宪,但是統(tǒng)一了正交和透視矩陣的處理,避免了直接divide z锐朴。
正交投影
orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)
用來生成正交投影,near和far分別表示近視點和遠(yuǎn)視點的距離需要為正兴喂,正交矩陣視錐是一長方體,物體沒有近大遠(yuǎn)小特性焚志,同一個物體無論遠(yuǎn)近都是一樣大小的衣迷,這看起來并不自然,但是卻可以保證物體無論遠(yuǎn)近都保持一致的外觀娩嚼,某些游戲畫面就需要這個特點蘑险。
正交矩陣應(yīng)該考慮品屏幕比例,不然會導(dǎo)致橫豎屏切換后畫面變形滴肿。
//橫屏,例如800*600岳悟,橫屏width是800,豎屏width為600 float aspectRatio = (float) width / (float) height; orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1, 1, -1, 1); //豎屏 orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1, 1, -1, 1);
安卓沒有任何配置的情況下,默認(rèn)相機(jī)處于世界坐標(biāo)系(0,0)點贵少,默認(rèn)使用的是[-1,1]的立方體正交投影呵俏,投影矩陣內(nèi)的所有點在屏幕上都可見,故可以看到(0,0,0)點的物體滔灶,即使默認(rèn)相機(jī)在(0,0,0)點也可以看到普碎。
透視投影
perspectiveM(float[] m, float yFovInDegrees, float aspect, float n, float f)
用來定義透視矩陣
透視矩陣也應(yīng)該考慮品屏幕比例,不然會導(dǎo)致橫豎屏切換后畫面變形,同樣near和far需要為正數(shù)。
//橫屏,例如800*600录平,橫屏width是800麻车,豎屏width為600 perspectiveM(projectionMatrix, 45, (float) width / (float) height, 1f, 10f);
?
投影矩陣小結(jié):
1.無論是正交還是透視投影,投影矩陣作用于EyeVertex點后斗这,會將該點轉(zhuǎn)化為齊次式表示的標(biāo)準(zhǔn)化設(shè)備坐標(biāo)动猬。使用正交投影的的w并沒有用,只是為了保持和透視投影處理的一致性表箭,w值經(jīng)過左乘之后沒有任何變化赁咙。
2.透視投影的w經(jīng)過矩陣左乘之后會變成-z。
3.正交投影和透視投影作用后的x,y,z值不相同(表達(dá)式不同)免钻,具體表達(dá)式可以參考下方鏈接彼水。
?
?
關(guān)于投影矩陣的數(shù)學(xué)計算,請參考如下網(wǎng)站极舔,講的非常清晰凤覆,耐心看完一定會理解的。
http://www.songho.ca/opengl/gl_projectionmatrix.html#perspective