解決android opengl es繪製物體屏幕橫豎屏切換物體壓扁形變以及矩陣相關知識

在android上如果手機屏幕由豎屏切換到橫屏,這個時候Ui會發生變化,變形,通常在Android中我們可以通過寫不同的xml來適配這種屏幕變化引起的ui佈局變化。然而,在opengl es也會存在由於屏幕切換導致ui問題,爲此,opengl提出了歸一化座標來解決這個問題。

歸一化設備座標:
具體說來就是把較小(端)邊固定在[-1,1]範圍,而較大邊(端)按屏幕尺寸比例縮放。
比如1280x720的手機屏幕,在豎屏時,我們把較小邊即寬範圍限定在【-1,1】,則高在範圍[-1280/720,1280/720]或[-1.78,1.78]範圍內。同理在橫屏時把較小邊即高範圍限定在【-1,1】,則寬在範圍[-1280/720,1280/720]或[-1.78,1.78]。很明顯這樣座標系發生了變化,
這樣無論屏幕是橫屏還是豎屏,物體看上去都是一樣的。
opengl兩種投影:正交投影和透視投影。正交投影,不管遠近物體大小不會發生變化即不會因爲是距離遠而物體投影變小,是平行的。

向量:
一個向量是一個有多個元素的數組。在opengl中,一個位置通常是一個4維的向量通常是x,y,z,w這4個分量,顏色也是4維(紅,綠,藍,alpha)。
矩陣與矩陣/向量相乘:
滿足第一個矩陣的列數等於第二個矩陣的行數,相乘後的新矩陣行數等於第一個矩陣的行數,列數等於第二個矩陣列數。如圖:
這裏寫圖片描述

單位矩陣:
主對角線上的元素都爲1,其餘元素全爲0的n階矩陣稱爲n階單位矩陣。任何矩陣和單位矩陣相乘都是它本身。
這裏寫圖片描述
平移矩陣:
假定有座標點(2,2,0,1)沿x平移3單位,y也是3個單位,z還是0,w默認是1,opengl默認w分量是1,則平移公式是:
這裏寫圖片描述

所以可以得出平移矩陣,

這裏寫圖片描述
再說一次,z下面w分量是1,是因爲opengl默認w分量是1。
其實,正交投影可以理解爲一種平移矩陣,所以正交投影又叫平行投影。
opengl兩種投影:
正交投影和透視投影,正交投影不會因爲遠近而物體投影大小發生變化,是一種平行投影。在Android opengl中如本例代碼AirHockeyRenderer類的onSurfaceChanged方法中android.opengl.Matrix.orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)中float[] m爲正交投影矩陣座標,按照上面之前分析至少需要16個長度才能存的下正交投影座標值,其他參數意義如下貼圖:
這裏寫圖片描述
即android.opengl.Matrix.orthoM方法相當於進行了如上正交矩陣運算,這將使物體左右,上下,前後座標歸一化設備座標後始終在[-1,1]之間,相當於做了這種正交投影做了一種關係映射,把物體座標歸一化到[-1,1]之間,當然不在這之間的是看不到的。
分析了理論,接下來是具體怎麼代碼使用?
爲了使用這種正交投影,打開本節的simple_vertex_shader.glsl文件,發現有個uniform mat4 u_Matrix;這正是我們在頂點着色器裏面來使用它,代表一個4x4矩陣。
在AirHockeyRenderer類,定義了String U_MATRIX = “u_Matrix”,來保持頂點着色器裏面定義的uniform 名字, private final float[] projectionMatrix = new float[16];這個用於存儲正交投影矩陣,private int uMatrixLocation;存儲矩陣的位置。在 onSurfaceCreated去獲取這個位置uMatrixLocation = glGetUniformLocation(program, U_MATRIX)。

    /**
     * onSurfaceChanged is called whenever the surface has changed. This is
     * called at least once when the surface is initialized. Keep in mind that
     * Android normally restarts an Activity on rotation, and in that case, the
     * renderer will be destroyed and a new one created.
     * 
     * @param width
     *            The new width, in pixels.
     * @param height
     *            The new height, in pixels.
     */
    @Override
    public void onSurfaceChanged(GL10 glUnused, int width, int height) {
        // Set the OpenGL viewport to fill the entire surface.
        glViewport(0, 0, width, height);

        final float aspectRatio = width > height ? 
            (float) width / (float) height : 
            (float) height / (float) width;

        if (width > height) {
            // Landscape
            orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
        } else {
            // Portrait or square
            orthoM(projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
        }   
    }

這端代碼會把屏幕當前方向計算在內創建正交投影建立這種映射。如果橫屏模式下,會擴展寬度座標空間到【-aspectRatio, aspectRatio】同時高度範圍在【-1,1】。如果豎屏模式下,會擴展高度座標空間到【-aspectRatio, aspectRatio】同時寬度範圍在【-1,1】。這個實質是物體座標其實無論橫豎都沒有變化,只是在橫豎屏發生後座標系發生了變化,看上去的效果物體的寬高還是沒有變化,這就達到了不會因爲橫豎屏導致物體被壓扁的視覺效果。

最後一步在onDrawFrame中 // Assign the matrix
glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0)是將正交投影矩陣傳遞給着色器。
到此,本文完成了橫豎屏切換物體變形的問題。本文代碼如下:
https://github.com/pangrui201/OpenGlesProject/tree/master/OpenGlesProject_lesson4

發佈了46 篇原創文章 · 獲贊 22 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章