OpenGL 3D世界

3D應用程序開發(fā)中,一項很重要的工作就是對場景中的物體進行各種投影與變換。相比于OpenGL ES 1.x的封閉模式,OpenGL ES 2.0在變換方面采取了開放模式,其API中不再提供完成各種變換的方法,變換所有的矩陣都由開發(fā)人員直接提供給渲染管線。進行投影與變換時,需要一些線性代數(shù)的知識。

OpenGL映屏坐標系

在Android系統(tǒng)中,普通View的坐標系是把屏幕的左上角當作坐標原點,向右是x的正方向,向下是y的正方向。OpenGL映屏坐標系是標準的數(shù)學坐標系(笛卡爾坐標系),坐標原點是屏幕的左下方。
向右是x的正方向,向上是y的正方向。如下圖:

坐標對比.png

OpenGL統(tǒng)一視圖

OpenGL使用一個規(guī)格化正方形來表示統(tǒng)一視圖,其實就是一個(-1.0,-1.0,1.0,1.0)的Rect.


Screen Shot 2018-12-16 at 4.23.08 PM.png

所有圖像都是首先映射到這個統(tǒng)一視圖中,然后等比例投影到手機的屏幕上。當然也可以理解,所有屏的大小就是一個規(guī)格化的(-1.0,-1.0,1.0,1.0)的Rect.

攝像機的設置

在真實的世界,我們用眼睛看物體,眼睛的位置、姿態(tài)的不同,就算是對同一場景進行觀察,我們所看到景像也是不一樣的。在OpenGL虛擬世界中,會虛擬一個攝像機,來表示人眼觀察世界。攝像機的設置來模擬人眼的位置、姿態(tài)等信息。

攝像機的設置主要需要給出3方面的信息,包括攝像機的位置、觀察的方向以及up方向。

Screen Shot 2018-12-16 at 4.51.06 PM.png
  • 攝像機的位置很容易理解,可以用3D空間的坐標表示。

  • 攝像機的觀察的方向可以理解為攝像機鏡頭的指向,可以用攝像機的位置和觀察目標點確定的向量表示。

  • 攝像機的up方向可以理解攝像機為了確定視角的向量。

人眼觀察現(xiàn)實世界,也是這樣的,如下圖:

Screen Shot 2018-12-16 at 10.53.38 PM.png

從上圖可以看出,OpenGL虛擬世界模擬現(xiàn)實世界時,攝像機的位置、朝向、up方向可以有很多不同的組合。同樣的位置可以有不同的朝向、不同的up方向;不同的位置也可以具有相同的朝向,相同的up方向等。

OpenGL可以通過調(diào)用Matrix類的setLookAtM方法來完成對攝像機的設置,代碼如下:


Matrix.setLookAtM (
mVMatrix,   // 攝像機矩陣
0,                // 起始偏移量
cx,cy,cz,     // 攝像機的位置 x、y、z的坐標
tx,ty,tz,       // 觀察目標點 x、y、z的坐標
upx,upy,upz // up向量在x、y、z軸上的分量
);

投影方式

在《OpenGL ES基礎》的介紹中,我們知道在圖元裝配之后光柵化階段前,首先需要將虛擬3D世界中的物體投影到二維平面上。OpenGL ES 2.0中常用的投影模式有兩種,分別為正交投影與透視投影。

投影概念

OpenGL中,根據(jù)應用程序提供的投影矩陣,管線會確定一個可視空間區(qū)域,稱之為視景體。視景體是由6個平面確定的,這6個平面分別為:上平面(up)、下平面(down)、左平面(left) 、右平面(right)、遠平面(far)、近平面(near)。

Screen Shot 2018-12-16 at 11.24.39 PM.png

場景中處于視景體內(nèi)的的物體會被投影到近平面上,視景體外面的物體將被裁剪掉。然后再將近平面的上投影出內(nèi)容映射到屏幕上的視口中。

視景體_20190103203905.jpg

正交投影

正交投影是平行投影的一種,其投影線(物體的頂點與近平面上投影點的連線)是平行的,因此它的視景體為長方體,投影到近平面上的圖形就是物體的平面鏡像。

正交投影的視景體如下圖:

Screen Shot 2018-12-16 at 11.46.08 PM.png

正交投影的圖形效果如下圖:

Screen Shot 2018-12-16 at 11.47.45 PM.png

設置正交投影的代碼:

Matrix.orthoM (
mProjectMatrix,           // 投影矩陣
0,                              // 起始偏移量
left,right,                      // near平面的left、right
bottom,top,                  // near平面的bottom、top
near,far                       // near平面、far平面與視點的距離
);

透視投影

現(xiàn)實世界中人眼觀察物體會有“近大遠小”的效果,因此要想開發(fā)出更加真實的場景,僅使用正交投影是遠遠不夠的,這時可以采用透視投影。透視投影的投影線是不平行的,他們相交于視點。通過透視投影,可以產(chǎn)生現(xiàn)實世界中“近大遠小”的效果,大部分3D圖形采用的都是透視投影。

透視投影的視景體是錐形體。上面介紹投影概念時,使用的就是透視投影。視景體不再復畫,投影效果如下:

透視投影效果.png

透視投影的設置代碼:

Matrix.frustumM (
mProjectionMatrix,             // 投影矩陣
0 ,                                    // 起始偏移量
left,right,                             // near平面的left、right
bottom,top,                         // near平面的bottom、?top
near,far                              // near平面、far平面與視點的矩離
) ;

Matrix變換

在《OpenGL ES基礎》中說過,數(shù)字圖像的本質(zhì)就是一個像素矩陣。因此對數(shù)字圖像的處理,可以理解為是對矩陣的各種變換。Matrix變換是非常重要的。

Matrix變換的數(shù)學知識

Matrix變換都是通過將表示點坐標的向量與特定的變換矩陣相乘完成的,進行變換時,三維空間中點的位置需要表示成齊次坐標形式。所謂齊次坐標形式也就是在x、y、z 3個坐標值后面增加第四個量w,未變換時w值一般為1。這樣設計是為了計算方便。

例如:點P(Px,Py,Pz,1)與一個矩陣相乘即可完成一次變換,得到變換后的點Q(Qx,Qy,Qz,1)。

矩陣變換.png

當矩陣M中的元素取適當?shù)闹禃r,等式Q=MP就會有其特殊的幾何意義,例如,可以將三維空間中的點P平移、旋轉(zhuǎn)、縮放到點Q。這些變換的具體信息就存放在矩陣M中,因此通常矩陣M為變換矩陣。當對一個圖像進行變換時,讓圖像的所有像素點乘以一個變換矩陣,就是可以得到變換后的另一個圖像。

平移變換

平移變換.png

縮放變換

縮放變換.png

放轉(zhuǎn)變換

介紹旋轉(zhuǎn)變換的矩陣格式前,首先需要知道繞坐標軸或任意軸旋轉(zhuǎn)的一些規(guī)則。OpenGL中,旋轉(zhuǎn)角度的正負可以用右手螺旋定則來確定。螺旋定則:右手握住旋轉(zhuǎn)軸,使大姆指指向旋轉(zhuǎn)軸的正方向,4指環(huán)繞的方向即為旋轉(zhuǎn)的正方向,也就是旋轉(zhuǎn)角度為正值。下圖為繞任意軸r的旋轉(zhuǎn)Φ的矩陣:

1342760515_3105.jpg

具體推導過程可以參考:

http://www.cppblog.com/lovedday/archive/2008/01/12/41031.html

一般情況下,對繞任意軸的旋轉(zhuǎn)變化,可以拆分為繞x、y、z軸的旋轉(zhuǎn)變化的組合。

操作代碼

/**
     * Translates matrix m by x, y, and z in place.
     *
     * @param m matrix
     * @param mOffset index into m where the matrix starts
     * @param x translation factor x
     * @param y translation factor y
     * @param z translation factor z
     */
    public static void translateM(
            float[] m, int mOffset,
            float x, float y, float z) {
        for (int i=0 ; i<4 ; i++) {
            int mi = mOffset + i;
            m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
        }
    }

/**
     * Scales matrix m in place by sx, sy, and sz.
     *
     * @param m matrix to scale
     * @param mOffset index into m where the matrix starts
     * @param x scale factor x
     * @param y scale factor y
     * @param z scale factor z
     */

public static void scaleM(float[] m, int mOffset,
            float x, float y, float z) {
        for (int i=0 ; i<4 ; i++) {
            int mi = mOffset + i;
            m[     mi] *= x;
            m[ 4 + mi] *= y;
            m[ 8 + mi] *= z;
        }
    }

/**
     * Rotates matrix m in place by angle a (in degrees)
     * around the axis (x, y, z).
     *
     * @param m source matrix
     * @param mOffset index into m where the matrix starts
     * @param a angle to rotate in degrees
     * @param x X axis component
     * @param y Y axis component
     * @param z Z axis component
     */

public static void rotateM(float[] m, int mOffset,
            float a, float x, float y, float z) {
        synchronized(sTemp) {
            setRotateM(sTemp, 0, a, x, y, z);
            multiplyMM(sTemp, 16, m, mOffset, sTemp, 0);
            System.arraycopy(sTemp, 16, m, mOffset, 16);
        }
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容