Android OpenGL ES 對于不同坐標系下坐標變換,大都使用矩陣運算的方法來定義和實現的。這里介紹對應指定的坐標系(比如viewmodel, projection或是viewport) Android OpenGL ES支持的一些矩陣運算及操作。
OpenGL ES 中使用四個分量(x,y,z,w)來定義空間一個點,使用 4 個分量來描述 3D 坐標稱為齊次坐標 :所謂齊次坐標就是將一個原本是n維的向量用一個 n+1 維向量來表示。 它有什么優點呢?許多圖形應用涉及到幾何變換,主要包括平移、旋轉、縮放。以矩陣表達式來計算這些變換時,平移是矩陣相加,旋轉和縮放則是矩陣相乘,綜合起來可以表示為 p’ = m1\*p + m2
(m1 旋轉縮放矩陣, m2 為平移矩陣, p 為原向量 ,p’ 為變換后的向量)。引入齊次坐標的目的主要是合并矩陣運算中的乘法和加法,表示為 p’ = M\*p
的形式。即它提供了用矩陣運算把二維、三維甚至高維空間中的一個點集從一個坐標系變換到另一個坐標系的有效方法。 它可以表示無窮遠的點。n+1 維的齊次坐標中如果h=0,實際上就表示了 n 維空間的一個無窮遠點。對于齊次坐標[a,b,h],保持 a,b 不變,|V|=(x1*x1,y1*y1,z1*z1)^1/2
的過程就表示了標準坐標系中的一個點沿直線 ax+by=0 逐漸走向無窮遠處的過程。
為了實現 viewing, modeling, projection 坐標變換,需要構造一個4X4 的矩陣 M,對應空間中任意一個頂點 vertex v , 經過坐標變換后的坐標 v’=Mv
矩陣本身可以支持加減乘除,對角線全為 1 的 4X4 矩陣成為單位矩陣 Identity Matrix 。
OpenGL 使用了右手坐標系統,右手坐標系判斷方法:在空間直角坐標系中,讓右手拇指指向x軸的正方向,食指指向y軸的正方向,如果中指能指向z軸的正方向,則稱這個坐標系為右手直角坐標系。
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/64.png" alt="" />
方法 public abstract void glTranslatef (float x, float y, float z) 用于坐標平移變換。
在上個例子中我們把需要顯示的正方形后移了4個單位,就是使用的坐標的平移變換,可以進行多次平移變換,其結果為多個平移矩陣的累計結果,矩陣的順序不重要,可以互換。
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/65.png" alt="" />
方法 public abstract void glRotatef(float angle, float x, float y, float z)用來實現選擇坐標變換,單位為角度。 (x,y,z)定義旋轉的參照矢量方向。多次旋轉的順序非常重要。
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/66.png" alt="" />
比如你選擇一個骰子,首先按下列順序選擇3次:
gl.glRotatef(90f, 1.0f, 0.0f, 0.0f);
gl.glRotatef(90f, 0.0f, 1.0f, 0.0f);
gl.glRotatef(90f, 0.0f, 0.0f, 1.0f);
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/67.png" alt="" />
然后打算逆向旋轉回原先的初始狀態,需要有如下旋轉:
gl.glRotatef(90f, -1.0f, 0.0f, 0.0f);
gl.glRotatef(90f, 0.0f, -1.0f, 0.0f);
gl.glRotatef(90f, 0.0f, 0.0f, -1.0f);
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/68.png" alt="" />
或者如下旋轉:
gl.glRotatef(90f, 0.0f, 0.0f, -1.0f);
gl.glRotatef(90f, 0.0f, -1.0f, 0.0f);
gl.glRotatef(90f, -1.0f, 0.0f, 0.0f);
旋轉變換 glRotatef(angle, -x, -y, -z) 和 glRotatef(-angle, x, y, z)是等價的,但選擇變換的順序直接影響最終坐標變換的結果。 角度為正時表示逆時針方向。
在對 Mesh(網格,構成三維形體的基本單位)同時進行平移和選擇變換時,坐標變換的順序也直接影響最終的結果。
比如:先平移后旋轉, 旋轉的中心為平移后的坐標。
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/69.png" alt="" />
先選擇后平移: 平移在則相對于旋轉后的坐標系:
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/70.png" alt="" />
一個基本原則是,坐標變換都是相對于變換的 Mesh 本身的坐標系而進行的。
方法 public abstract void glScalef (float x, float y, float z)用于縮放變換。
下圖為使用 gl.glScalef(2f, 2f, 2f) 變換后的基本,相當于把每個坐標值都乘以2.
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/71.png" alt="" />
同樣當需要平移和縮放時,變換的順序也會影響最終結果。
比如先平移后縮放:
gl.glTranslatef(2, 0, 0);
gl.glScalef(0.5f, 0.5f, 0.5f);
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/72.png" alt="" />
如果調換一下順序:
gl.glScalef(0.5f, 0.5f, 0.5f);
gl.glTranslatef(2, 0, 0);
結果就有所不同:
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/73.png" alt="" />
在進行平移,旋轉,縮放變換時,所有的變換都是針對當前的矩陣(與當前矩陣相乘),如果需要將當前矩陣回復最初的無變換的矩陣,可以使用單位矩陣(無平移,縮放,旋轉)。
public abstract void glLoadIdentity()。
在棧中保存當前矩陣和從棧中恢復所存矩陣,可以使用
public abstract void glPushMatrix()
和
public abstract void glPopMatrix()。
在進行坐標變換的一個好習慣是在變換前使用 glPushMatrix 保存當前矩陣,完成坐標變換操作后,再調用 glPopMatrix 恢復原先的矩陣設置。