android-opengles3.0開發【6】使用正交投影解決圖形變形

簡介

在之前的文章中,繪製了一些圖形,但是有個問題, 以三角形爲例,根據設置的座標,應該顯示正三角形,而手機上運行卻被拉長爲等邊三角形(底邊比左右兩邊短)。

如果手機橫向防止,三角形就被壓扁了。

瞭解兩個座標系之後,這個問題的原因就清楚了。

歸一化設備座標系、屏幕座標系、虛擬座標系

opengl 的座標系是歸一化設備座標系,原點在屏幕中心,橫向是橫座標,縱向是縱座標,範圍都是[-1,1] 。

android設備的屏幕座標系遠點在屏幕左上角,橫向是橫座標,縱向是縱座標,但是縱座標的正方向向下,範圍由手機屏幕像素大小決定。

咱們先不管座標系的方向,因爲方向和拉伸/壓扁的問題沒有關係。

從兩座標系定義的範圍來看,歸一化設備座標系定義了一個正方形區域,而屏幕座標系定義的區域由具體像素決定,一般手機都是矩形。

假如,屏幕尺寸爲 1920 * 1080,opengl中的一個頂點的座標是(0.5,0.5),則換算成屏幕座標就是 (960,540)。也就是說,這個點在歸一化設備座標中,距離x、y軸的距離是一樣的,而在屏幕座標系中,到x、y軸的距離就不一樣了,也就是上文問題中拉伸、壓扁的意思。

修復這個問題很簡單,加上一個虛擬座標系。

這個虛擬座標系的範圍由屏幕尺寸計算出來,將屏幕寬高中較小的一方映射到範圍 [-1,1] 上,較大的一方映射到範圍 [-(big/small),big/small] 。

使用時,頂點的座標在虛擬座標系下定義,在着色器中使用正交投影矩陣換算爲歸一化設備座標,然後opengl渲染到屏幕座標系上,繪製出來的圖形就不會變形了。

使用示例

例如,屏幕尺寸爲 1920 * 1080,則虛擬座標系的範圍是 x:[-1920/1080,1920/1080],y:[-1,1]。

然後我們在虛擬座標系下定義頂點座標 (0.5,0.5)。

接着着色其中通過正交投影矩陣,將頂點座標換算爲 (0.28……,0.5),這裏的0.28…… 是個近似小數。

opengl 渲染到屏幕座標系上的座標是(~540,540),x座標在精度範圍內無限接近y座標,也就是說頂點在屏幕座標系下,到x、y軸的距離也是一樣了,也就不會再變形了。

下面看看正交投影是怎麼進行換算的。

正交投影

android 中 Matrix 類中 orthoM() 方法可以用來生成一個正交投影矩陣,具體參數的含義看下面的代碼註釋。

    /**
     * Computes an orthographic projection matrix.
     *
     * @param m 存放結果矩陣的數組
     * @param mOffset 結果矩陣的起始偏移值
     * @param left x軸最小範圍
     * @param right x軸最大範圍
     * @param bottom y軸最小範圍
     * @param top y軸最大範圍
     * @param near z軸最小範圍
     * @param far z軸最大範圍
     */
    public static void orthoM(float[] m, int mOffset,float left, float right, float bottom, float top,float near, float far) {}

生成的結果矩陣的元素值如下

代碼實現

計算正交投影矩陣

    public final float[] projectionMatrix = new float[16];


    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //設置視口
        glViewport(0, 0, width, height);
        //計算正交投影矩陣,修正變形
        float aspectRatio = width > height ?
                (float)width / (float)height : (float)height / (float)width;
        if (width > height){
            orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
        }else {
            orthoM(projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
        }
    }

頂點着色器中使用正交投影矩陣進行換算

#version 300 es

in vec4 vPosition;
uniform mat4 matrix;

void main()
{
    gl_Position = matrix * vPosition;
}

繪製時將計算出的矩陣傳入着色器中

        //傳入正交矩陣修復變形
        int matrixLoc = glGetUniformLocation(program, VERTEX_ATTRIB_PROJECTION_MATRIX);
        glUniformMatrix4fv(matrixLoc, 1, false, projectionMatrix, 0);

其他就和以前一樣了,可以參考之前的文章。

總結

本文梳理了歸一化設備座標系、屏幕座標系、虛擬座標系三個座標系之間的關係,通過正交投影,完成虛擬座標到歸一化設備座標的換算,進而修復圖形變形的問題。

項目地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章