OpenGL之調(diào)整屏幕寬高比

OpenGL之基礎(chǔ)
OpenGL之繪制簡(jiǎn)單形狀
OpenGL之顏色

調(diào)整屏幕寬高比

不做寬高比調(diào)整導(dǎo)致的問(wèn)題

如果不做任何處理,圖像會(huì)在橫屏模式下被壓扁,因?yàn)橹爸苯影炎鴺?biāo)傳遞給OpenGL時(shí),沒(méi)有考慮屏幕的寬高比

橫屏狀態(tài) 豎屏狀態(tài)
image-20211221121517035.png
image-20211221121648424.png

解決方案

不要直接在歸一化設(shè)備坐標(biāo)上工作,而應(yīng)該先在虛擬坐標(biāo)空間里工作,然后再使用投影技術(shù)把虛擬空間坐標(biāo)轉(zhuǎn)換回歸一化設(shè)備坐標(biāo)

虛擬空間坐標(biāo)范圍

在虛擬坐標(biāo)空間里,把較小的范圍固定在 [-1,1] 內(nèi),然后按屏幕尺寸的比例調(diào)整較大的范圍

例如,手機(jī)寬度是720,高度是1280

豎屏 橫屏
寬度范圍 [-1,1] [-1280/720,1280/720] 即 [-1.78,1.78]
高度范圍 [-1.78,1.78] [-1,1]

正交投影

正交投影是投影技術(shù)的一種,使用正交投影,實(shí)際上定義了三維世界內(nèi)部的一個(gè)區(qū)域,也就是虛擬空間。在這個(gè)區(qū)域內(nèi)的所有東西都會(huì)顯示在屏幕上,而區(qū)域外的所有東西都會(huì)被裁剪掉,如下圖所示

正交投影.jpg
創(chuàng)建正交投影矩陣

Matrix.orthoM() 方法可以創(chuàng)建一個(gè)正交投影矩陣,其方法原型如下

public static void orthoM(float[] m, int mOffset,
    float left, float right, float bottom, float top,
    float near, float far) {

參數(shù)說(shuō)明

參數(shù) 說(shuō)明
m 最終生成的正交投影矩陣數(shù)組,長(zhǎng)度至少有16個(gè)元素,才能存儲(chǔ)正交投影矩陣
mOffset 正交投影矩陣起始的偏移值
left x軸的最小范圍
right x軸的最大范圍
bottom y軸的最小范圍
top y軸的最大范圍
near z軸的最小范圍
far z軸的最大范圍

上面的參數(shù)中l(wèi)eft、right、bottom、top、near、far對(duì)應(yīng)于上一幅圖中標(biāo)識(shí)的地方,也是形成虛擬坐標(biāo)空間的值,

調(diào)用這個(gè)方法的時(shí)候,會(huì)產(chǎn)生下面的正交投影矩陣:


image-20211221131722796.png

這個(gè)正交投影矩陣會(huì)把所有在左右之間、上下之間和遠(yuǎn)近之間的事物映射到歸一化設(shè)備坐標(biāo)中從-1到1的范圍,在這個(gè)范圍內(nèi)的所有事物在屏幕上都是可見(jiàn)的

歸一化設(shè)備坐標(biāo)使用的是左手坐標(biāo)系統(tǒng)(使用左手,大拇指指向x軸正值方向、食指指向y軸正值方向),而在 OpenGL的早期版本,默認(rèn)使用的卻是右手坐標(biāo)系統(tǒng),其使用z的負(fù)值增加表示距離增加。這就是為什么Android的 Matrix會(huì)默認(rèn)生成反轉(zhuǎn)z的矩陣
坐標(biāo)系統(tǒng)

左手坐標(biāo)系統(tǒng) 右手坐標(biāo)系統(tǒng)
image-20211221133109558.png
image-20211221133131305.png

加入正交投影

修改頂點(diǎn)著色器

attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_Matrix;

varying vec4 v_Color;
void main(){
    v_Color=a_Color;
    gl_Position=u_Matrix*a_Position;
    gl_PointSize=10.0;
}

添加 uniform 的 u_Matrix,代表正交投影矩陣,并把它定義為一個(gè) mat4 類型,mat4 表示這個(gè) uniform 是一個(gè) 4×4 的矩陣

將當(dāng)前頂點(diǎn)的最終位置賦值為:位置與一個(gè)正交投影矩陣的乘積。這意味著頂點(diǎn)數(shù)組不再代表歸一化設(shè)備坐標(biāo)了,其將被理解為存在于正交投影矩陣所定義的虛擬坐標(biāo)空間中,這個(gè)矩陣會(huì)把坐標(biāo)從虛擬坐標(biāo)空間變換回歸一化設(shè)備坐標(biāo)

修改渲染器

public class MyRenderer implements GLSurfaceView.Renderer {
    private final FloatBuffer mVertexBuffer;
    private Context mContext;
    private int a_color;
    private int a_position;
    private int u_matrix;
    // 保存正交投影矩陣
    private float[] projectionMatrix = new float[16];

    public MyRenderer(Context context) {
        mContext = context;
        // 頂點(diǎn)位置有改動(dòng),因?yàn)楝F(xiàn)在代表虛擬空間中的坐標(biāo)了,范圍也不再是 [-1,1],
        // 不過(guò)要注意的是,豎屏?xí)r,如果較大范圍軸(比如豎屏?xí)r的y軸)超過(guò)了1,在橫屏?xí)r會(huì)被截?cái)嗫床坏?        float[] vertex = new float[]{
                0f, 0f, 1f, 1f, 1f,
                -0.5f, -0.8f, 0f, 0f, 0f,
                0.5f, -0.8f, 0f, 0f, 0f,
                0.5f, 0.8f, 0f, 0f, 0f,
                -0.5f, 0.8f, 0f, 0f, 0f,
                -0.5f, -0.8f, 0f, 0f, 0f,

                -0.5f, 0f, 1f, 0f, 0f,
                0.5f, 0f, 1f, 0f, 0f,

                0f, -0.5f, 0f, 1f, 0f,
                0f, 0.5f, 0f, 0f, 1f
        };
        mVertexBuffer = ByteBuffer.allocateDirect(vertex.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertex);
        mVertexBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        glClearColor(1f, 1f, 1f, 1f);

        int vertexShader = ShaderHelper.compileVertexShader(mContext, R.raw.ratio3_vertex);
        int fragmentShader = ShaderHelper.compileFragmentShader(mContext, R.raw.ratio3_fragment);
        int program = ProgramHelper.getProgram(vertexShader, fragmentShader);
        glUseProgram(program);

        a_color = glGetAttribLocation(program, "a_Color");
        a_position = glGetAttribLocation(program, "a_Position");
        // 獲取正交投影矩陣位置
        u_matrix = glGetUniformLocation(program, "u_Matrix");

        mVertexBuffer.position(0);
        glVertexAttribPointer(a_position, POSITION_COMPONENT_COUNT, GL_FLOAT, false, STRIDE, mVertexBuffer);
        glEnableVertexAttribArray(a_position);

        mVertexBuffer.position(POSITION_COMPONENT_COUNT);
        glVertexAttribPointer(a_color, COLOR_COMPONENT_COUNT, GL_FLOAT, false, STRIDE, mVertexBuffer);
        glEnableVertexAttribArray(a_color);
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
        glViewport(0, 0, width, height);
        // 生成正交投影矩陣
        float ratio = width > height ? (float) width / height : (float) height / width;
        if (width > height) {
            // 橫屏
            Matrix.orthoM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, -1f, 1f);
        } else {
            // 豎屏
            Matrix.orthoM(projectionMatrix, 0, -1f, 1f, -ratio, ratio, -1f, 1f);
        }
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        glClear(GL_COLOR_BUFFER_BIT);
        // 向著色器傳遞 正交投影矩陣
        glUniformMatrix4fv(u_matrix, 1, false, projectionMatrix, 0);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 6);

        glDrawArrays(GL_LINES, 6, 2);

        glDrawArrays(GL_POINTS, 8, 1);
        glDrawArrays(GL_POINTS, 9, 1);
    }
}

效果

豎屏 橫屏
image-20211221140409098.png
image-20211221140436697.png
最后編輯于
?著作權(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)容