Viewing 和 Modeling 變換關系緊密,對應到相機拍照為放置三角架和調整被拍物體位置及角度,通常將這兩個變換使用一個 modelview 變換矩陣來定義。對于同一個坐標變換,可以使用不同的方法來想象這個變換,比如將相機向某個方向平移一段距離,效果等同于將被拍攝的模型(model)向相反的方向平移同樣的距離(相對運動)。兩個不同的空間想象方法對于理解坐標變換各有其優缺點。你可以使用適合自己理解能力的方法來想象空間坐標變換。
下面我們使用一個由兩個坐標變換組成的簡單例子開始介紹 MODELVIEW 變換:一個變換為逆時針繞Z軸旋轉45度,另一個變換為為沿X軸平移。 假定需要繪制的物體的尺寸和平移的距離相比要小的多從而你可以跟容易的看到平移效果。這個物體初始位置在坐標系的原點。
如果先旋轉物體然后再平移,旋轉后的物體的位置在 X 軸上面,但如果先平移后繞原點旋轉物體,最終物體會出現在 y=x 的直線上:
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/74.png" alt="" />
可以看到坐標變換的次序直接影響到最終的變換結果。所有的 Viewing 和 Modeling 變換操作都可以使用一個4X4 的矩陣來表示,所有后續的 glMultMatrix*() 或其它坐標變換指令 會使用一個新的變換矩陣M于當前 modelview 矩陣 C 相乘得到一個新的變換矩陣 CM。然后所有頂點坐標v 都會和這個新的變換矩陣相乘。 這個過程意味著最后發出的坐標變換指令實際上是第一個應用到頂點上的:CMv 。因此一種來理解坐標變換次序的方法是:使用逆序來指定坐標變換。
比如下面代碼:
gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity();
//apply transformation N
gl.glMultMatrixf(N);
//apply transformation M
glMultMatrixf(M);
//apply transformation L
gl.glMultMatrixf(L);
//draw vertex
...
上面代碼, modelview 矩陣依次為I(單位矩陣),N,NM 最終為 NML ,最終坐標變換的結果為 NMLv ,也就是N(M(Lv)) ,v 首先與 L 相乘,結果 Lv 再和M相乘,所得結果 MLv 再和N相乘??梢钥吹阶鴺俗儞Q的次序和指令指定的次序正好相反。而實際代碼運行時,坐標無需進行三次變換運算,頂點 v 只需和計算出的最終變換矩陣 NML 相乘一次就可以了。
因此如果你采用世界坐標系(原點和X,Y,Z軸方向固定)來思考坐標變換,代碼中坐標變換指令的次序和 頂點和矩陣相乘的次序相反。比如,還是上面的例子,如果你想最終的物體出現在X軸上,此時必須是先旋轉后平移,可以使用如下代碼(R 代表選擇矩陣,T 代表平移矩陣)
gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity();
//translation
gl.glMultMatrixf(T);
//rotation
gl.glMultMatrixf(R);
draw_the_object()
另外一種想象坐標變換的方法是忘記這種固定的坐標系統,而是使用物體本身的局部坐標系統,這種局部坐標系和物體的相對位置是固定的。 所有的坐標變換操作都是針對物體的局部坐標系。使用這種方法,代碼中矩陣相乘的次序和相對局部坐標系坐標變換的次序是一致的。(不關使用哪種方法來想象坐標變換,最終同樣變換結果代碼都是一樣的,只是理解的方法不同)。還是使用上面旋轉平移的例子。想象一個和物體連接一起的局部坐標系,如下圖紅色的坐標系,想象所有的坐標變換都是相對這個局部坐標系的,要使物體最終出現在 y=x 上,可以想象先轉動物體及其局部坐標系(R),然后再平移物體及其局部坐標系(T),這時代碼的順序和相對于物體局部坐標系的次序是相同的。
gl.glMatrixMode(GL_MODELVIEW);
gl.glLoadIdentity();
//rotation
gl.glMultMatrixf(R);
//translation
gl.glMultMatrixf(T);
draw_the_object()
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/75.png" alt="" />
使用物體局部坐標系,可以更好的了解理解如機械手和太陽系之類的圖形系統。
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/76.png" alt="" />
Android OpenGL ES 的 GLU 包有一個輔助函數 gluLookAt 提供一個更直觀的方法來設置modelview 變換矩陣:
void gluLookAt(GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)
http://wiki.jikexueyuan.com/project/opengl-es-guide/images/77.png" alt="" />
注意: 這些坐標都采用世界坐標系。