Android OpenGL ES 4.正交投影

前言

之前我們的所有圖形效果,都是變形的,比如我們?cè)纠L制的是長(zhǎng)寬比是1:1的,結(jié)果在手機(jī)屏幕上的效果展示卻是長(zhǎng)方形。那么,本節(jié)課我們通過正交投影來解決這個(gè)問題。
本節(jié)課主要講解如何去編寫相關(guān)代碼來解決問題,而具體的原理、概念、GL坐標(biāo)體系變換等暫不做深入說明,會(huì)在之后的課程在講解。

歸一化設(shè)備坐標(biāo)

在OpenGL中,我們要渲染的所有物體都要映射到x軸、y軸、z軸上的[-1, 1]范圍內(nèi),這個(gè)范圍內(nèi)的坐標(biāo)被稱為歸一化設(shè)備坐標(biāo),其獨(dú)立于屏幕的實(shí)際尺寸或者形狀。歸一化設(shè)備坐標(biāo)假定的坐標(biāo)空間是一個(gè)正方形。如下圖


歸一化設(shè)備坐標(biāo).png

但是我們手機(jī)設(shè)備一般都不是正方形的,而是長(zhǎng)方形的。所以導(dǎo)致x和y兩個(gè)方向上,同樣的比例值,但是視覺上所占的長(zhǎng)度卻是不一樣的。如下圖,繪制一個(gè)半徑占0.5的圓時(shí),效果卻是一個(gè)橢圓。

歸一化設(shè)備坐標(biāo)實(shí)際效果.png

解決這個(gè)問題,一般我們的解決方案步驟如下:

  1. 在設(shè)置物體的坐標(biāo)、尺寸時(shí),將短邊視為標(biāo)準(zhǔn)邊,取值范圍是[-1,1],而較長(zhǎng)邊的取值范圍則是[-N,N],其中N≥1,N是長(zhǎng)邊/短邊的比例系數(shù)。
  2. 頂點(diǎn)著色器設(shè)置頂點(diǎn)參數(shù)的時(shí)候,將長(zhǎng)邊上的值從[-N,N]換算為[-1,1]的范圍內(nèi)。

步驟如下圖:


解決步驟1.png
解決步驟2.png

代碼實(shí)現(xiàn)

針對(duì)上面的解決步驟,步驟1只需要我們?cè)谠O(shè)置頂點(diǎn)的時(shí)候按照這個(gè)標(biāo)準(zhǔn)即可。而步驟2則是本課程的關(guān)鍵。
要對(duì)坐標(biāo)向量進(jìn)行換算,可以使用矩陣來解決問題。

在三維圖形學(xué)中,一般使用的是4階矩陣。OpenGL中使用的是列向量,如[xyzw]T,所以與矩陣相乘時(shí),矩陣在前,向量在后。

知道了原理之后,我們代碼實(shí)現(xiàn)上需要解決以下幾個(gè)問題:

  1. 如何獲得一個(gè)矩陣,可以把坐標(biāo)范圍從[-N,N]換算為[-1,1]的范圍內(nèi)
  2. 如何將矩陣傳遞到GLSL中
  • 對(duì)于問題1,Android提供了Matrix.orthoM這個(gè)方法來處理矩陣。
  • 對(duì)于問題2,與獲取頂點(diǎn)索引類似,可以再GLSL中聲明一個(gè)mat4類型的矩陣變量,獲取其索引,再傳遞值給她

具體代碼實(shí)現(xiàn)如下:

private static final String VERTEX_SHADER = "" +
    // mat4:4×4的矩陣
    "uniform mat4 u_Matrix;\n" +
    "attribute vec4 a_Position;\n" +
    "void main()\n" +
    "{\n" +
    // 矩陣與向量相乘得到最終的位置
    "    gl_Position = u_Matrix * a_Position;\n" +
    "}";
private int uMatrixLocation;
/**
 * 矩陣數(shù)組
 */
private final float[] mProjectionMatrix = new float[]{
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1,
};

@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
    // 省略部分代碼
    uMatrixLocation = getUniform("u_Matrix");
}

@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
    // 邊長(zhǎng)比(>=1),非寬高比
    float aspectRatio = width > height ?
            (float) width / (float) height :
            (float) height / (float) width;

    // 1. 矩陣數(shù)組
    // 2. 結(jié)果矩陣起始的偏移量
    // 3. left:x的最小值
    // 4. right:x的最大值
    // 5. bottom:y的最小值
    // 6. top:y的最大值
    // 7. near:z的最小值
    // 8. far:z的最大值
    if (width > height) {
        // 橫屏
        Matrix.orthoM(mProjectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
    } else {
        // 豎屏or正方形
        Matrix.orthoM(mProjectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
    }
    // 更新u_Matrix的值,即更新矩陣數(shù)組
    GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, mProjectionMatrix, 0);
}

參考

Android OpenGL ES學(xué)習(xí)資料所列舉的博客、資料。

GitHub代碼工程

本系列課程所有相關(guān)代碼請(qǐng)參考我的GitHub項(xiàng)目GLStudio。

課程目錄

本系列課程目錄詳見 簡(jiǎn)書 - Android OpenGL ES教程規(guī)劃

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

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

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