Android 图形 II-OpenGL ES

本文深入探讨了Android中OpenGL的使用,从基础概念到具体实现,包括OpenGL API的版本支持、框架接口、渲染对象坐标映射、以及OpenGL版本与设备兼容性的声明。通过实例展示了如何在GLSurfaceView中使用OpenGL进行图形绘制,并详细说明了如何调整坐标以适应不同设备屏幕比例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:

Android通过Open GraphicsLibrary(OpenGL)来支持高性能的2D和3D图像, 特别是OpenGL ES API. OpenGL是一款跨平台的图像API, 它为3D图像处理指定了标准的软件接口. OpenGL ES是OpenGL中应用于嵌入式设备的分支. Android支持多个版本的OpenGL ES API, 包括:

OpenGL ES 1.0和1.1 – 支持Android1.0及更高.

OpenGL ES 2.0 – 支持Android2.2(API8)及更高.

OpenGL ES 3.0 – Android4.3(API 18)及更高.

OpenGL ES 3.1 – Android5.0(API 21)及更高.

 

Android通过自己的framework和NDK来支持OpenGL. 这里主要介绍Androidframework接口, 关于NDK的可以查看AndroidNDK. 在Android framework中有两个基础类可以让我们使用OpenGL ESAPI创建和操作图形: GLSurfaceView和GLSurfaceView.Renderer. 如果我们想要在Android APP中使用OpenGL, 那么如何在activity中使用这两个类便成了我们第一个要了解的事情:

GLSurfaceView: 这个类是一个View, 它和SurfaceView很像, 让我们可以使用OpenGL的API来绘制和处理对象. 使用该类的时候只需要创建一个GLSurfaceView的实例, 然后将我们的Renderer传给它. 但是如果我们想要截获点击事件的话, 就得继承GLSurfaceView类, 并实现它的点击监听器才行.

GLSurfaceView.Renderer: 该接口定义了在GLSurfaceView中绘制图形的方法. 我们必须这个接口作为一个独立的类, 并通过GLSurfaceView.setRenderer()方法关联给GLSurfaceView. GLSurfaceView.Renderer接口需要我们实现这些方法:

onSurfaceCreated(): 当GLSurfaceView创建的时候, 系统会调用该方法一次. 使用该方法来处理那些只需要执行一次的操作, 比如设置OpenGL环境参数, 或者初始化OpenGL图像对象.

onDrawFrame(): 系统将会在GLSurfaceView每次重绘的时候调用该方法.应该使用该方法来绘制或者重绘图像对象.

onSurfaceChanged(): 当GLSurfaceView的几何形状发生改变的时候,系统调用该方法. 包括GLSurfaceView的尺寸变化或者设备屏幕的方向发生变化. 比如从横屏变竖屏, 该方法就会被调用. 使用该方法来响应GLSurfaceView的容器的改变.

OpenGL ES 包:

一旦我们已经确定在一个View中使用OpenGL ES, 我们可以使用下列这些类来调用OpenGL 的API:

OpenGL ES 1.0/1.1 API包:

         android.opengl:这个包为OpenGL ES1.0/1.1类提供了静态接口, 并且比下面的javax.microedition.khronos包更加稳定. 包括GLES10,GLES10Ext, GLES11, GLES11Ext.

         Javax.microediton.khronos.opengles:这个包提供了OpenGL ES 1.0/1.1的标准的实现. 包括GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack.

OpenGL ES 2.0 API类:

         android.opengl.GLES20:这个包提供了OpenGL ES 2.0接口并且从Android2.2开始可用.

OpenGL ES 3.0/3.1 API包:

         android.opengl:这个包提供了OpenGL ES 3.0/3.1类的接口. 3.0版本从Android4.3开始可用, 3.1版本则从Android5.0才开始可用. 包括GLES30, GLES31, GLES31Ext.

想了解更多的使用OpenGL ES创建app的信息, 可以参考这里.

声明OpenGL需求:

如果我们的APP需要使用的OpenGL功能并非支持所有的设备, 那么就必须包含这些需求在AndroidManifest.xml文件中. 这是一些常用的OpenGL manifest声明:

1.      OpenGL ES version requirements– 如果我们的APP需要指定OpenGL ES的版本, 就必须声明requirement. 需要增加如下的设置到manifest中:

对于OpenGL ES 2.0:

<!-- Tell the system this app requires OpenGL ES 2.0.-->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

增加这个声明的目的是Google play需要限制我们的APP不能安装在那些不支持OpenGLES 2.0的设备上. 如果APP只能支持OpenGL ES 3.0, 那么我们则需要这样指定:

对于OpenGL ES 3.0:

<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />

对于OpenGL ES 3.1:

<!-- Tell the system this app requires OpenGL ES 3.1. -->
<uses-feature android:glEsVersion="0x00030001" android:required="true" />

OpenGL ES 3.x的API对OpenGL ES2.0的API是兼容的, 这意味着我们可以使用更加灵活的方法来使用OpenGL ES. 我们可以在manifest中声明OpenGL ES 2.0, 并且默认情况下使用该版本, 但是可以在运行时查询设备是否支持3.x, 如果支持就可以使用.

2.      Texture compressionrequirements – 如果我们的APP需要使用texture compression, 那么就必须在manifest中用<support-gl-texture>声明需要支持的格式. 对此后文会有更加详细的介绍.

映射绘制对象的坐标(主要为了解决宽高比的问题):

在Android上显示图形的时候一个很基本的问题就是, 这些设备的屏幕经常是不同的大小和形状的. 默认情况下OpenGL会假设一个正方形的屏幕, 然后将图形绘制在一个通常不是正方形的屏幕上.


默认情况下OpenGL的坐标系(左)和映射在典型屏幕上之后的样子(右). 可以看到图形的比例有所变化, 为了解决这个问题, 我们可以申请OpenGL的投影模式(projection mode)和相机view(camera view)来转变坐标, 这样图形就可以显示为正确的比例了.

为了申请projection 和camera view, 我们需要创建一个projection矩阵和一个camera view矩阵并且将它们应用到OpenGL渲染管道. Projection矩阵负责重新计算图形的坐标, 使他们可以正确的映射到Android的设备屏幕上. Camera view矩阵从渲染对象的一个指定的视角创建一个变换的图形.

OpenGLES 1.0中的projection和camera view:

在ES 1.0 API中, 我们通过创建对应的矩阵来使用projection和camera view, 然后将它们添加到OpenGL环境中.

1.      Projection矩阵: 创建一个Projection以通过屏幕的几何形状来重新计算对象的坐标, 以便它们可以被正确的绘制出来. 下面的栗子演示了如何基于屏幕的宽高比修改onSurfaceChanged()方法来创建Projection矩阵, 并将其应用于OpenGL的渲染环境.

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

    // make adjustments for screen ratio
    float ratio = (float) width / height;
    gl.glMatrixMode(GL10.GL_PROJECTION);        // set matrix to projection mode
    gl.glLoadIdentity();                        // reset the matrix to its default state
    gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);  // apply the projection matrix
}

2.      Camera transformation 矩阵: 一旦用projection算出了坐标系, 下一步就必须用到cameraview了. 下面的栗子演示了如何修改GLSurfaceView.Renderer的onDrawFrame()方法来应用一个model view和使用GLU.gluLookAt()工具来创建一个view模拟一个视角.

public void onDrawFrame(GL10 gl) {
    ...
    // Set GL_MODELVIEW transformation mode
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();                      // reset the matrix to its default state

    // When using GL_MODELVIEW, you must set the camera view
    GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    ...
}

OpenGLES 2.0及更高版本中的projection和camera view:

在ES2.0和3.0的API中, 我们使用projection和camera view的时候, 首先要向图形对象的vertex shaders(顶点着色器)中添加一个矩阵成员. 通过增加这个矩阵成员, 我们就可以生成和应用projection和camera view了.

1.      向顶点着色器中添加一个矩阵– 为projection矩阵创建一个变量, 并把它作为着色器位置的乘数. 在下面的栗子中, uMVPMatrix成员变量让我们可以应用projection和camera view矩阵到使用这个着色器的对象的坐标上.

private final String vertexShaderCode =

    // This matrix member variable provides a hook to manipulate
    // the coordinates of objects that use this vertex shader.
    "uniform mat4 uMVPMatrix;   \n" +

    "attribute vec4 vPosition;  \n" +
    "void main(){               \n" +
    // The matrix must be included as part of gl_Position
    // Note that the uMVPMatrix factor *must be first* in order
    // for the matrix multiplication product to be correct.
    " gl_Position = uMVPMatrix * vPosition; \n" +

    "}  \n";

上面的栗子在顶点着色器中定义了一个变换矩阵成员. 根据APP的需求, 我们可以定义独立的projection矩阵和camera view矩阵, 这样我们就可以独立的修改它们了.

2.      访问着色器矩阵– 在顶点着色器中创建一个钩子之后, 我们就可以访问这个变量来应用projection和camera view矩阵. 下面的代码演示了如何修改GLSurfaceView.Renderer中的onSurfaceCreated()方法来访问上面栗子中的顶点着色器的矩阵变量.

public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    ...
    muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    ...
}

3.      创建projection和camera view矩阵– 生成图形对象要使用的projection和view矩阵. 下面的栗子演示了如何基于屏幕宽高比并通过GLSurfaceView.Renderer中的onSurfaceChanged()和onSurfaceCreated()方法来创建camera view矩阵和一个projection矩阵.

public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    ...
    // Create a camera view matrix
    Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}

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

    float ratio = (float) width / height;

    // create a projection matrix from device screen geometry
    Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}

4.      应用projection和camera view矩阵– 想要应用projection和camera view变换, 需要将矩阵相乘后保存在顶点着色器中. 下面的栗子中演示了如何通过onDrawFrame()方法组合projection矩阵和camera view然后将它们应用于将要被OpenGL渲染的图形对象.

public void onDrawFrame(GL10 unused) {
    ...
    // Combine the projection and camera view matrices
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

    // Apply the combined projection and camera view transformations
    GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);

    // Draw objects
    ...
}

OpenGL的版本选择:

OpenGL ES 1.0和1.1从Android1.0就已经开始支持, 从Android2.2开始, 可以支持OpenGL ES2.0. 从Android4.3开始支持OpenGL ES 3.x. 它们都提供了稳定的用于创建3D游戏的图形接口. 对于2.0和3.0版本的OpenGL, 它们的图形编程过程很相似. 3.0版本是2.0的超集. 但是1.0/1.1和2.0/3.0有很大的差别. 所以开发之前应该考虑以下的一些因素:

性能: 通常, 2.0和3.0比1.0/1.1提供了更好的图片处理性能. 但是性能同样也跟设备有关系.

设备兼容性: 开发者应该考虑到设备的类型, Android的版本和OpenGL ES的版本. 更多信息可以参考这里.

编码便利性: 1.0/1.1提供了一个固定的功能管道和便利功能, 这些东西在2.0/3.0中没有. 所以新的OpenGL的开发者可能会发现在1.0/1.1上开发更加便利和简单.

图形控制: OpenGL ES 2.0/3.0通过使用可编程管道提供了一个更高等级的控制, 通过这些, 开发者们可以比在1.0/1.1中更容易的控制图形.

Texture 支持: OpenGL ES 3.0拥有对Texture压缩最好的支持.

 

 

参考: https://developer.android.com/guide/topics/graphics/opengl.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值