[OpenGL]从零开始写一个Android平台下的全景视频播放器——1.3 用OpenGL ES 2.0显示一张图片(上)

来源:互联网 发布:我国茶叶出口数据2015 编辑:程序博客网 时间:2024/06/07 23:53

Github项目地址,欢迎star~!

为了方便没有准备好梯子的同学,我把项目在CSDN上打包下载,不过更新会慢一些

回到目录

本节示例代码下载地址

解决变形问题

要解决变形,我们需要引入Projection Matrix(投影矩阵)的概念,在OpenGL中,投影矩阵用来改变场景在屏幕上的显示方式(近大远小,平行投影等等)。
因为我们绘制的是二维平面,所以问题还是比较好解决的,假如我们的屏幕是16:9的(横屏情况),那么只要让OpenGL的绘制范围也是16:9的就好了,如下图所示:
这里写图片描述
可以看到,我们把OpenGL的绘制区域横向拉长了,拉长的比例就是(16/9 约1.777)

更多关于透视和投影变换可以参考这篇博文

那么代码要如何实现呢?答案是矩阵(线性代数没上90的哭晕在厕所。。),需要进行的操作是正交投影,好在我们并不需要去推导正交矩阵如何求出来(其实这个算简单的,有兴趣的可以自己查找相关资料),Android 提供的Matrix类中包含这个方法。

我们先声明一个长度16的float数组,这是Matrix的标准尺寸

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

注意,在OpenGL中,数组是row-major的,如下所示:

/** * Matrix math utilities. These methods operate on OpenGL ES format * matrices and vectors stored in float arrays. * <p> * Matrices are 4 x 4 column-vector matrices stored in column-major * order: * <pre> *  m[offset +  0] m[offset +  4] m[offset +  8] m[offset + 12] *  m[offset +  1] m[offset +  5] m[offset +  9] m[offset + 13] *  m[offset +  2] m[offset +  6] m[offset + 10] m[offset + 14] *  m[offset +  3] m[offset +  7] m[offset + 11] m[offset + 15]</pre> */

创建了projectionMatrix以后,我们还需要更新glsl中顶点着色器的代码,以便把这个变换用的矩阵传递过去,如下所示:

attribute vec4 aPosition;uniform mat4 uMatrix;void main() {  gl_Position = uMatrix*aPosition;}

uniform 是GLSL中的常量类型,之前的attribute类型是用来在Java代码和顶点着色器(Vertex Shader)传递变量用的,uniform则是给顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)传递常量用的。我们把uMatrix和aPosition做矩阵乘法,就得到了一个新的顶点位置。

类似的,我们也需要一个入口,以便给这个矩阵传递数据

uMatrixHandle=GLES20.glGetUniformLocation(programId,"uMatrix");

目前类中的全局变量如下所示

    private Context context;    private int aPositionHandle;    private int programId;    private FloatBuffer vertexBuffer;    private final float[] vertexData = {            0f,0f,0f,            1f,-1f,0f,            1f,1f,0f    };    private final float[] projectionMatrix=new float[16];    private int uMatrixHandle;

完成正交投影

@Overridepublic void onSurfaceChanged(GL10 gl, int width, int 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);}

在onSurfaceChanged中,我们获取了屏幕的宽和高,所以我们在这里计算缩放的比例,需要注意的是横屏和竖屏的时候是刚好相反的处理(一个改变x,一个改变y)

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

参数的含义:左右(x)下上(y)近远(z)

更新onDrawFrame

@Overridepublic void onDrawFrame(GL10 gl) {    GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);    GLES20.glUseProgram(programId);    GLES20.glUniformMatrix4fv(uMatrixHandle,1,false,projectionMatrix,0);    GLES20.glEnableVertexAttribArray(aPositionHandle);    GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,            12, vertexBuffer);    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);}

看一下效果吧:
这里写图片描述
如果是竖屏的情况,那么应该是这样子的:
这里写图片描述

下面我们来显示一张图片,图片可以看做一个矩形,所以我们先来画一个矩形(别急别急,后面会讲得越来越快( ⊙ o ⊙ ))
之前提到OpenGL的基本形状是三角形,一个矩形可以看成由4个三角形构成,如果我们一个一个画,那需要12个顶点,36个坐标,效率不高,所以我们采用另外一种方式——顶点索引与glDrawElements配合使用。

什么是顶点索引呢?顶点索引就是给出顶点的下标而不给出具体的顶点坐标,看代码:

private final float[] vertexData = {        0f,0f,0f,        1f,1f,0f,        -1f,1f,0f,        -1f,-1f,0f,        1f,-1f,0f};private final short[] indexData = {        0,1,2,        0,2,3,        0,3,4,        0,4,1};

我们的绘制区域是(-1,-1)到(1,1)的平面区域,vertexData给出了5个顶点,indexData给出了4个三角形的描述,如下图所示:
这里写图片描述

声明一个ShortBuffer ,用来存放顶点的索引数据

private ShortBuffer indexBuffer;
indexBuffer = ByteBuffer.allocateDirect(indexData.length * 2)        .order(ByteOrder.nativeOrder())        .asShortBuffer()        .put(indexData);indexBuffer.position(0);

这个和前面存放顶点数据的类似,就不解释了

然后,使用GLES20.glDrawElements把三角形画出来,注意如果我们之前的数组类型是byte,那么就应该使用GLES20.GL_UNSIGNED_BYTE,总之类型要对齐

GLES20.glDrawElements(GLES20.GL_TRIANGLES,indexData.length,GLES20.GL_UNSIGNED_SHORT,indexBuffer);

看一下运行效果:
这里写图片描述

下一节我们来学习如何把矩形变成一张图片

0 0