文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android OpenGl 介绍(一)

2023-09-01 20:50

关注

一、OpenGl 介绍

看到这个介绍,相信大家都不会陌生,因为在平时的工作中,或多或少大家都会听说过 openGl 这个东西,而且对它的印象基本都是觉得比较高深难懂。其实这个东西也不是那么难,那么无从下手,首先必须要了解一些基本的背景和搞懂一些基本概念,然后就可以按需学习了。

首先我们来看看官方对 OpenGl 的描述为:OpenGL( Open Graphics Library 开发图形接口)是一个跨平台的图形 API,用于指定 3D 图形处理硬件中的标准软件接口。

总结的来讲,OpenGl 提供了指定图形处理的硬件接口,我们在处理图形的时候,只要按照它的规则来调用,就可以获得更加高效的图形处理方法。

为了承上启下,必须要再了解以下 OpenGl 的背景,它的前身是 SGI 公司为其图形工作站开发的 IRIS GL。由于 IRIS GL 的兼容性不好,而且不太容易移植。因此在其基础上,SGI 公司开发出了 OpenGl。OpenGl 一般用于PC端的图形工作站,在移动端使用 OpenGl 基本带不动。为此,Khronos 公司就为 OpenGl 提供了一个子集,OpenGl ES(OpenGl for Embedded System)。是不是很自然的知道 OpenGl ES是啥,接着往下看。

二、OpenGl ES 介绍

这里直接上官方解释:OpenGl ES是免费的跨平台的功能完善的 2D/3D 图形库接口的 API,是OpenGL 的一个子集。

移动端使用到的基本上都是 OpenGl ES,当然 Android 开发下还专门提供了android.opengl 包,并且提供了 GlSurfaceView,GLU,GlUtils 等工具类。相信介绍到这里,大家都知道 OpenGl ES 是干什么的,很不巧,这里又是一句承上启下的话,因为这里引出了一堆新的名词,别急,下面一一介绍,大家就可以连起来了。

三、GlSurfaceView 介绍

顾名思义,它是一个SurfaceView,看源码可知,GlSurfaceView 继承 SurfaceView 的同时,增加了 Renderer,作用就是专门为 OpenGl 显示渲染使用的。看看下面的使用方法,十分平易近人:以通过创建的实例使用这个类,并随心所欲的设计属于你的 Renderer。

GLSurfaceView glSurfaceView = new GLSurfaceView(this);glSurfaceView.setRenderer(new GLSurfaceView.Renderer() {    @Override    public void onSurfaceCreated(GL10 gl, EGLConfig config) {    }    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {    }    @Override    public void onDrawFrame(GL10 gl) {    }});

很巧,这里突然又冒出了个 GlSurfaceView.Renderer。

四、GlSurfaceView.Renderer 介绍

GlSurfaceView.Renderer 定义了用于绘制在图形所需的方法。在使用的时候,必须提供这个接口作为一个单独的类的实现,并通过 GLSurfaceView.setRenderer() 将其连接到 GLSurfaceView 使用实例,如上面的代码所示。通过 @Override,在此接口中设计随心所欲的渲染方法,当然这些方法是需要按照 OpenGl 的规则的。下面介绍以下该接口的方法的作用:

讲到这里,相信大家都有个基本概念和使用流程了,这里总结一下:

说了这么多,来个例子吧,图文并茂。

五、绘制几何图形

1、创建正方型图的类:

public class Square {        private FloatBuffer vertexBuffer;    private ShortBuffer drawListBuffer;    // 数组中每个顶点的坐标数    static final int COORDS_PER_VERTEX = 3;    static float squareCoords[] = {            -0.5f,  0.5f, 0.0f,   // 左上角            -0.5f, -0.5f, 0.0f,   // 左下角             0.5f, -0.5f, 0.0f,   // 右下角             0.5f,  0.5f, 0.0f }; // 右上角    // 绘制顶点的顺序    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 };    public Square() {        // 初始化 ByteBuffer,长度为 arr 数组的长度 * 4,因为 float 占4个字节        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(squareCoords.length * 4);        // 数组排列用 nativeOrder                byteBuffer.order(ByteOrder.nativeOrder());        // 从 ByteBuffer 创建一个浮点缓冲区        vertexBuffer = byteBuffer.asFloatBuffer();        // 将坐标添加到 FloatBuffer        vertexBuffer.put(squareCoords);        // 设置缓冲区来读取第一个坐标        vertexBuffer.position(0);        // 初始化 ByteBuffer,长度为 arr 数组的长度 * 2,因为 short 占2个字节        byteBuffer = ByteBuffer.allocateDirect(drawOrder.length * 2);        byteBuffer.order(ByteOrder.nativeOrder());        drawListBuffer = byteBuffer.asShortBuffer();        drawListBuffer.put(drawOrder);        drawListBuffer.position(0);    }}

这里要注意的是 Java 和 OpenGl 的缓冲区数据存储结构存在差异,Java 为 Big-Edian,而 OpenGl 为 Little-Edian。因此在 Android 中使用 OpenGl 的时候,需要进行数据存储格式转换。在代码中,一般会封装成工具类,这里提供几个封装好的轮子。

(1)将 int[] 数组转为 OpenGl 需要的 IntBuffer

private IntBuffer intBufferUtil(int[] intArray){    IntBuffer intBuffer;    // 初始化 ByteBuffer,长度为 intArray 数组的长度 * 4(sizeof(int)这就是4的由来)    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(intArray.length * 4);    // 数组排列用 nativeOrder    byteBuffer.order(ByteOrder.nativeOrder());    intBuffer = byteBuffer.asIntBuffer();    intBuffer.put(intArray);    intBuffer.position(0);    return intBuffer;}

(2)将 float[] 数组转为 OpenGl 需要的 FloatBuffer

private FloatBuffer floatBufferUtil(float[] floatArray){    FloatBuffer floatBuffer;    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(floatArray.length * 4);    byteBuffer.order(ByteOrder.nativeOrder());    floatBuffer = byteBuffer.asFloatBuffer();    floatBuffer.put(floatArray);    floatBuffer.position(0);    return floatArray;}

(3)将 short[] 数组转为 OpenGl 需要的 ShortBuffer

private ShortBuffer shortBufferUtil(short[] shortArray){    ShortBuffer shortBuffer;    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(shortArray.length * 2);    byteBuffer.order(ByteOrder.nativeOrder());    shortBuffer = byteBuffer.asShortBuffer();    shortBuffer.put(shortArray);    shortBuffer.position(0);    return shortBuffer;}

2、将形状渲染到 GlSurfaceView 中,主要可分为下面几步:
(1)首先我们需要在 GlSurfaceView.Renderer 中初始化需要渲染的几何图形

private Triangle mTriangle;private Square mSquare;public void onSurfaceCreated(GL10 unused, EGLConfig config) {    // 设置背景颜色    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);    // 初始化 square    mSquare = new Square();}

(2)在绘制图形前至少需要一个顶点着色器来绘制形状和一个片段着色器的颜色。这些着色器要进行编译,然后加入到一个 OpenGL ES 程序。这里有两个比较难理解的概念:

下面给 Square 类定义基本的着色器代码:

public class Square {    private final String vertexShaderCode =            "attribute vec4 vPosition;" +                    "void main() {" +                    "  gl_Position = vPosition;" +                    "}";    private final String fragmentShaderCode =            "precision mediump float;" +                    "uniform vec4 vColor;" +                    "void main() {" +                    "  gl_FragColor = vColor;" +                    "}";...}

注意:着色器的代码执行起来代价十分的高,因此多次执行是不行的,一般将执行代码的逻辑写在带图形类的构造方法中。比如上面的 Square:

private final int mProgram;public Square() {    ... ...//这里省略上面的代码    int vertexShader = OneGlRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);    int fragmentShader = OneGlRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);    // 创建空的OpenGL ES程序    mProgram = GLES20.glCreateProgram();    // 添加顶点着色器到程序中    GLES20.glAttachShader(mProgram, vertexShader);    // 添加片段着色器到程序中    GLES20.glAttachShader(mProgram, fragmentShader);    // 创建OpenGL ES程序可执行文件    GLES20.glLinkProgram(mProgram);}

到这里,所有绘制的基本配置都完成了,然后就可以开始愉快的写绘制图形的方法,在形状类(Square)中创建一个绘制的方法 onDraw(),可以在 onDraw() 方法中编写绘制的逻辑。

public Square() {    ... ...//这里省略上面的代码    public void draw() {        // 将程序添加到OpenGL ES环境        GLES20.glUseProgram(mProgram);        // 获取顶点着色器的位置的句柄        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");        // 启用三角形顶点位置的句柄        GLES20.glEnableVertexAttribArray(mPositionHandle);        //准备三角形坐标数据        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,                GLES20.GL_FLOAT, false,                vertexStride, vertexBuffer);        // 获取片段着色器的颜色的句柄        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");        // 设置绘制三角形的颜色        GLES20.glUniform4fv(mColorHandle, 1, color, 0);        // 绘制三角形        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);        // 禁用顶点数组        GLES20.glDisableVertexAttribArray(mPositionHandle);    }}

完成上面所有步骤,只需要在GlSurfaceView.Renderer的onDrawFrame()方法中调用图形类的绘制方法即可(上面的onDraw()):

@Overridepublic void onDrawFrame(GL10 unused) {    mSquare.draw();}

大功告成!!!

参考链接:

来源地址:https://blog.csdn.net/2301_76382363/article/details/129183495

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯