?上一節(jié)中冗酿,借助 OpenGL ES 對二維圖形的繪制,我們了解了相關(guān)概念络断。本節(jié)裁替,我們來談一個有趣的問題,坐標(biāo)變換貌笨。
?坐標(biāo)變換對于渲染管線來說弱判,是一個非常重要的概念。我們通過它锥惋,將一個三維對象從原始的模型坐標(biāo)系中昌腰,一步一步投射到屏幕坐標(biāo)系中。而可編程的 vertex shader(頂點渲染器) 給我們自定義變換過程提供了基礎(chǔ)膀跌。
概述
?接下來遭商,我們來看看坐標(biāo)變換的流程。
?如上圖所示捅伤,modeling transformation劫流、view transformation、projection transformation 發(fā)生在頂點著色器中(vertex shader)丛忆。而 perspective division 和 view transformation 隨后發(fā)生祠汇,且不可自定義過程。不同的轉(zhuǎn)換熄诡,實際上是將要繪制的對象帶入到了不同的坐標(biāo)系中可很。我們注意到有這樣幾個坐標(biāo)系:
- 模型坐標(biāo)系:主要用以定義描述繪制對象;
- 世界坐標(biāo)系:將要繪制的對象放置到世界坐標(biāo)系中凰浮。所有的繪制對象需要一個共同的坐標(biāo)系來決定對象之間的相對位置我抠。正如一個杯子的坐標(biāo),只是用來描述杯子本身的形體袜茧,我們還需要把它們放到世界空間中菜拓,在它的旁邊,可能還有茶壺惫周、桌子等等尘惧。
- 視坐標(biāo):也叫照相機坐標(biāo)系,你可以想象我們在那個方向递递、那個位置放置了一個照相機喷橙,也可以說啥么,那是我們眼睛的位置。
- 裁剪坐標(biāo)系:將區(qū)域進行裁剪贰逾,有些東西可能在視線之外悬荣,就需要裁掉;
- 歸一化坐標(biāo)系:OpenGL 認(rèn)為它所繪制的區(qū)域是一個正方形疙剑,每個方向上范圍在[-1,1]之間氯迂。
- 屏幕坐標(biāo)系:或者說是窗口坐標(biāo)系,就是將歸一化坐標(biāo)系投射到實際屏幕上言缤。
更加形象的過程嚼蚀,如下圖所示:
接下來,我們再來詳細看看每種變換過程管挟。
模型變換
?模型變換的主要作用轿曙,是將對象從模型坐標(biāo)系中轉(zhuǎn)移到公共的世界坐標(biāo)系中,模型坐標(biāo)通常用來描述對象本身僻孝。這種變換包括了:平移导帝、旋轉(zhuǎn)、放縮穿铆,而變換本身實際上是通過與一個 4*4 的變換矩陣相乘實現(xiàn)的您单。
平移矩陣:
縮放矩陣:
沿 X 軸旋轉(zhuǎn)矩陣:
沿 Y 軸旋轉(zhuǎn)矩陣:
沿 Z 軸旋轉(zhuǎn)矩陣:
?你可能會疑惑,三維世界里的對象當(dāng)然會使用三維坐標(biāo)荞雏,為什么會使用 4*4 的變換矩陣虐秦。
?從數(shù)學(xué)上講,對于一個三維空間中的對象讯檐,旋轉(zhuǎn)羡疗、縮放這些變換是矩陣乘法問題染服,而平移是矩陣加法問題别洪。使用4個分量來描述三維坐標(biāo)就是為了能夠使得平移過程能夠以乘法表示,從而使得 p’ = m1*p + m2
(m1 旋轉(zhuǎn)縮放矩陣柳刮, m2 為平移矩陣挖垛, p 為原向量 ,p’ 為變換后的向量)秉颗。轉(zhuǎn)換到 p’ = M*p
的形式痢毒。此處還可以參考鏈接。
?同時蚕甥,我們使用四個分量來描述三維世界的坐標(biāo)系統(tǒng)實際上是個齊次坐標(biāo)系統(tǒng)哪替,它可以描述無窮遠點。
?下文引用維基百科:
一條通過原點 (0, 0) 的線之方程可寫作 nx + my = 0菇怀,其中 n 及 m 不能同時為 0凭舶。以參數(shù)表示晌块,則能寫成 x = mt, y = ? nt。令 Z=1/t帅霜,則線上的點之笛卡兒坐標(biāo)可寫作 (m/Z, ? n/Z)匆背。在齊次坐標(biāo)下,則寫成 (m, ? n, Z)身冀。當(dāng) t 趨向無限大钝尸,亦即點遠離原點時,Z 會趨近于 0搂根,而該點的齊次坐標(biāo)則會變成 (m, ?n, 0)珍促。因此,可定義 (m, ?n, 0) 為對應(yīng) nx + my = 0 這條線之方向的無窮遠點之齊次坐標(biāo)剩愧。因為歐氏平面上的每條線都會與透過原點的某一條線平行踢星,且因為平行線會有相同的無窮遠點,歐氏平面每條線上的無窮遠點都有其齊次坐標(biāo)隙咸。
?在實際操作過程中沐悦,我們并不會真的先去計算好這些矩陣,然后再進行變換五督,android.opengl.Matrix 類能夠幫助我們計算好它們藏否,具體可以去查詢相關(guān) API 。
//矩陣旋轉(zhuǎn)
Matrix.rotateM(rotationM,0,90,0,1,0);
//矩陣相乘
Matrix.multiplyMM(transformationM,0,eyesMatrix,0,rotationM,0);
視圖變換
?你可以假設(shè)將一個攝像機朝著某個方向在某個位置觀察著三維對象充包,雖然在 OpenGL ES 中并不存在這樣的設(shè)備副签。我們都知道運動是相對的,移動攝像機對物體進行拍攝基矮,相對而言淆储,也可以是移動物體,以達到通過移動攝像機而從不同角度觀察物體的目的家浇。
?這種變換本砰,從本質(zhì)上而言和矩陣的模型變換并沒有差別,實際上所執(zhí)行的都是矩陣乘法钢悲。即点额,以視圖變換矩陣和目標(biāo)對象相乘。具體的推到過程這里將省略莺琳,我們可以使用 Matrix 類提供的方法獲然估狻:
?API 原型為:
/**
* Defines a viewing transformation in terms of an eye point, a center of
* view, and an up vector.
*
* @param rm returns the result
* @param rmOffset index into rm where the result matrix starts
* @param eyeX eye point X
* @param eyeY eye point Y
* @param eyeZ eye point Z
* @param centerX center of view X
* @param centerY center of view Y
* @param centerZ center of view Z
* @param upX up vector X
* @param upY up vector Y
* @param upZ up vector Z
*/
public static void setLookAtM(float[] rm, int rmOffset,
float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ, float upX, float upY,
float upZ) {
......
}
投影變換
?投影變換,從技術(shù)上講是將對象從視坐標(biāo)系下轉(zhuǎn)義到裁剪坐標(biāo)系下惭等。它是在頂點著色器返回 gl_Position 之前進行的最后一次變換珍手,接著,是通過透視除法(w分量),將裁剪坐標(biāo)系轉(zhuǎn)到歸一化坐標(biāo)系下琳要。
?我們最常用的投影是正交投影和透視投影料扰。
?對于正交投影,使用平行光線對三維對象進行投影焙蹭,所以所投影出來的影像沒有現(xiàn)實世界中遠近的概念晒杈。在 Android 應(yīng)用開發(fā)中,可以使用以下函數(shù)來獲得變換矩陣:
public static void orthoM(float[] m, int mOffset,
float left, float right, float bottom, float top,
float near, float far)
?由上可知孔厉,至少我們可以通過正交投影來設(shè)置要顯示的區(qū)域拯钻。比如,為了讓顯示圖形在顯示的時候不發(fā)生壓縮等形變:
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
// 根據(jù)屏幕方向設(shè)置投影矩陣
float ratio= width > height ? (float)width / height : (float)height / width;
if (width > height) {
// 橫屏
Matrix.orthoM(projectionMatrix, 0, -ratio, ratio, -1, 1, 0, 5);
} else {
Matrix.orthoM(projectionMatrix, 0, -1, 1, -ratio, ratio, 0, 5);
}
}
?下面我們來看下透視投影撰豺。
?對于透視投影粪般,觀察空間是一個視椎體,遠端大近端小污桦,投射光線顯然不是平行線亩歹,這樣物體投射出來就有了遠近的概念。離眼睛越遠的地方凡橱,物體越小小作,越近,同一個物體顯示越大稼钩。
public static void frustumM(float[] m, int offset,
float left, float right, float bottom, float top,
float near, float far)
視口變換
?該過程是自動執(zhí)行且固定不變的顾稀,用以進行從歸一化坐標(biāo)系到實際屏幕坐標(biāo)系的轉(zhuǎn)換過程。
小結(jié)
?整個過程坝撑,我覺得有點像拍電影静秆。將若干人物(模型坐標(biāo))集中在一個拍攝場景(世界坐標(biāo))中,在拍攝過程中巡李,會進行鏡頭移動到一個固定角度(視圖坐標(biāo))抚笔,調(diào)好焦距(投影坐標(biāo)),進行拍攝侨拦。
參考鏈接:
GLSL Programming/Vertex Transformations
OpenGL ES 投影變換 Projection
Article - World, View and Projection Transformation Matrices
這次殊橙,徹底搞懂 OpenGL 矩陣轉(zhuǎn)換