欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

安卓OpenGL ES—---正交投影

程序员文章站 2024-03-22 17:07:10
...

本文通过学习《OpenGL ES应用开发实践指南》,了解正交投影并使用

上一篇安卓OpenGL ES 2.0 入门
总结如何在安卓上创建一个OpenGL程序,并且在屏幕上绘制图案

一、直接绘制一个正方形

  • 直接绘制一个正方形,由两个三角形组成,所以顶点数据为:
    private static final float[] VERTEX = {
            //Triangle 1
            -0.5f, -0.5f,
            0.5f, 0.5f,
            -0.5f, 0.5f,

            //Triangle 2
            -0.5f, -0.5f,
            0.5f, -0.5f,
            0.5f, 0.5f
    };
  • 理想情况是在屏幕中绘制出一个正方形,而手机屏幕的分辨率是1280*720,即在竖屏模式下【-1,1】的范围对应1280像素高和720像素宽,并且横屏模式高和宽像素颠倒

竖屏:
安卓OpenGL ES—---正交投影
横屏:
安卓OpenGL ES—---正交投影

造成竖屏和横屏两个模式下绘制出来的正方形是被拉伸和被挤压的形状

二、使用正交投影

  • 在OpenGL里,要渲染的一切物体都映射到x轴、y轴和z轴上[-1,1]的范围里,这个范围坐标称为归一化设备坐标

  • 正因为归一化设备坐标假定坐标空间是一个正方形,而实际的设备屏幕不是正方形,因为宽高之间的比例,使得绘制结果与预料结果不一致

  • 正交投影通俗来说就是平行投影,把事物投影到了一个面上

  • 比如一个立方体内场景的一个正交投影例子:
    安卓OpenGL ES—---正交投影
    安卓OpenGL ES—---正交投影

定义正交投影

使用android.opengl包中的Matrix类里的orthoM()方法,可以生成一个正交投影

  • orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)

    float[] m:目标数组,数组至少有16个元素
    int mOffset:结果矩阵起始的偏移值
    float left:x轴的最小范围
    float right: x轴的最大范围
    float bottom:y轴的最小范围
    float top: y轴的最大范围
    float near:z轴的最小范围
    float far: z轴的最大范围
    

    调用orthoM()方法时,生成如下正交投影矩阵:
    安卓OpenGL ES—---正交投影
    相当于把所有上下左右前后的事物映射到归一化设备坐标[-1,1]内

程序中使用正交投影

1. 更新顶点着色器

uniform mat4 u_Matrix;
attribute vec4 a_Position;
void main()
{
    gl_Position = u_Matrix * a_Position;
    gl_PointSize = 10.0;
}

新的uniform定义“u_Matrix”,代表一个4*4的矩阵,这个矩阵把坐标从理想坐标空间转化成归一化设备坐标

2. 增加矩阵数组和一个新的uniform

private static final String U_MATRIX = "u_Matrix";
private final float[] projectionMatrix = new float[16];
private int uMatrixLocation;

2. 更改渲染器三个接口方法

onSurfaceCreated:

public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        GLES20.glClearColor(0.0f,0.0f,0.0f,0.0f);

        program = GLES20.glCreateProgram();
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
        GLES20.glAttachShader(program, vertexShader);
        GLES20.glAttachShader(program, fragmentShader);
        GLES20.glLinkProgram(program);

        glUseProgram(program);

        uColorLocation = glGetUniformLocation(program,U_COLOR);
        aPositionLocation = glGetAttribLocation(program,A_POSITION);
        uMatrixLocation = glGetUniformLocation(program,U_MATRIX);

        vertexData.position(0);
        glVertexAttribPointer(aPositionLocation,POSITION_COMPONENT_COUNT,GL_FLOAT,false,0,vertexData);
        glEnableVertexAttribArray(aPositionLocation);
        }

增加了寻找uniform(u_Matrix)位置

onSurfaceChanged:

public void onSurfaceChanged(GL10 gl10, int width, int height) {
        GLES20.glViewport(0,0,width,height);

        final 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);
        }

    }

创建了正交投影矩阵:
- final float aspectRatio = width > height ? (float)width / (float)height : (float)height / (float)width;:

    计算宽高比,使用宽高中大值除小值

- orthoM(projectionMatrix,0,-aspectRatio,aspectRatio,-1f,1f,-1f,1f):

    如果是竖屏,则扩展高度的取值范围,从-aspectRatio到aspectRatio,保持宽度和深度不变
  • orthoM(projectionMatrix,0,-1f,1f,-aspectRatio,aspectRatio,-1f,1f);:

    如果是横屏,则扩展宽度的取值范围,从-aspectRatio到aspectRatio,保持高度和深度不变
    

onDrawFrame:

public void onDrawFrame(GL10 gl10) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        glUniformMatrix4fv(uMatrixLocation,1,false,projectionMatrix,0);

        glUniform4f(uColorLocation,1.0f,1.0f,1.0f,1.0f);
        glDrawArrays(GL_TRIANGLES,0,6);

    }

增加了glUniformMatrix4fv(uMatrixLocation,1,false,projectionMatrix,0);,更新传递给着色器的正交投影矩阵

三、结果

竖屏:
安卓OpenGL ES—---正交投影
横屏:
安卓OpenGL ES—---正交投影
可以看出来都是同样大小的正方形