天天看点

Android音视频 - OpenGL ES 环境搭建与简单三角形的绘制OpenGL ES Android 环境搭建简单三角形的绘制总结

OpenGL ES Android 环境搭建

EGL

OpenGL 是一个跨平台的API,而不同的操作系统(Windows,Android,IOS)各有自己的屏幕渲染实现。所以OpenGL定义了一个中间接口层EGL(Embedded Graphics Library)标准,具体实现交给各个操作系统本身。关于EGL我们后面会仔细介绍,现在先有个概念。

基于EGL的GLSurfaceView

Android 框架中有如下两个基本类,用于通过 OpenGL ES API 来创建和操控图形:

[GLSurfaceView](https://developer.android.com/reference/android/opengl/GLSurfaceView)

[GLSurfaceView.Renderer](https://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer)

。如果您要在 Android 应用中使用 OpenGL,那么了解如何在 Activity 中实现这些类应是您的首要任务。

GLSurfaceView 以及 GLSurfaceView.Renderer

GLSurfaceView作为Android中的View组件,在View的属性上与其他组件并无不同,不过在其中实现了我们上面所说的EGL接口,GLSurfaceView.Renderer是一个接口,用来给用户自己处理回调

总体流程如下图所示:

Android音视频 - OpenGL ES 环境搭建与简单三角形的绘制OpenGL ES Android 环境搭建简单三角形的绘制总结

下面给出核心代码,完整代码链接放在文末

public class DemoActivity extends AppCompatActivity {
    @BindView(R.id.gl_surface)
    GLSurfaceView glSurface;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_demo);
         ButterKnife.bind(this);
         MyGLRender renderer = new MyGLRender();
         renderer.setDrawer(createTriangleDrawer());
         glSurface.setEGLContextClientVersion(2);
         glSurface.setRenderer(renderer);
    }
}
           
private static class MyGLRender implements GLSurfaceView.Renderer {
    private int iDrawer = -1;
     @Override
     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            GLES20.glClearColor(0f, 0f, 0f, 0f);
     //------开启混合,即半透明---------
     // 开启很混合模式
     GLES20.glEnable(GLES20.GL_BLEND);
     // 配置混合算法
     GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
     //------------------------------
 }
 
@Override
 public void onSurfaceChanged(GL10 gl, int width, int height) {
     GLES20.glViewport(0, 0, width, height);
 }
 
@Override
 public void onDrawFrame(GL10 gl) {
     // 清屏,否则会有画面残留
     GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
     if (iDrawer != -1)
        drawTriangle(iDrawer);
 }
 
public void setDrawer(int iDrawer) {
        this.iDrawer = iDrawer;
 }
 
 public native int createTriangleDrawer();
 
 public native void drawTriangle(int drawer);
 
}
           

上面使我们的环境搭建,大家可以看到TriangleDrawer这个类,这个就是我们搭建好环境之后真正进入OpenGL中的敲门砖了。

关于GLSurfaceView 以及 GLSurfaceView.Renderer

的详情可参考官方文档Android OpenGL ES官方文档,入门必看。

不过我开始看官方文档的时候也是一头雾水,所以写了博客,一是加深自己的理解,二是希望能帮助到大家。

简单三角形的绘制

我们前面也说过在OpenGL中只有3种基本图形,点,线,三角形,其他的几何图形都是有这几种图形表示

所以我们先来牛刀小试,来实现一个三角形增加一下信心:

由于OpenGL是跨平台的,所以我下面的代码核心部分是用C/C++写的,可能要求对于NDK有一定熟悉度,Android本身也提供了一些API,不过我们之所以绕一圈用C/C++是因为Android中的Java API的部分底层也是调用OpenGL C/C++的SO库。

首先定义一个纯虚类(用作接口)

base_drawer.h

#ifndef BLOGDEMO_BASE_DRAWER_H
#define BLOGDEMO_BASE_DRAWER_H
class BaseDrawer {
    virtual void Draw() = 0;
    virtual void Release() = 0;
};
#endif //BLOGDEMO_BASE_DRAWER_H
           

接着定义TriangleDrawer来实现绘制三角形

triangle_drawer.h部分代码

#ifndef BLOGDEMO_TRIANGLE_DRAWER_H
#define BLOGDEMO_TRIANGLE_DRAWER_H
#include "base_drawer.h"
#include <GLES2/gl2.h>
class TriangleDrawer : public BaseDrawer {
private:
     const char *TAG = "Drawer";
     const GLfloat m_vertex_coors[9] = {
        -0.5f, -0.5f, 0.0f,//左下
         0.5f, -0.5f, 0.0f,//右下
         0.0f, 0.5f, 0.0f//上
};
     GLuint m_program_id = 0;
     GLint m_vertex_pos_handler = -1;
     void CreateProgram();
     GLuint LoadShader(GLenum type, const GLchar *shader_code);
public:
     TriangleDrawer();
     ~TriangleDrawer();
     void Draw() override;
     void Release() override;
     void DoDraw();
     const GLchar *GetVertexShader();
     const GLchar *GetFragmentShader();
};
#endif //BLOGDEMO_TRIANGLE_DRAWER_H
           

base_drawer.cpp部分代码

void TriangleDrawer::Draw() {
    CreateProgram();
    DoDraw();
}



void TriangleDrawer::CreateProgram() {
    if (m_program_id == 0) {
        //创建一个空的OpenGLES程序,注意:需要在OpenGL渲染线程中创建,否则无法渲染
         m_program_id = glCreateProgram();
         LOGI(TAG, "create gl program : %d, %x", m_program_id, glGetError());
         if (glGetError() != GL_NO_ERROR) {
                    return;
         }
        GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, GetVertexShader());
         GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, GetFragmentShader());
         //将顶点着色器加入到程序
         glAttachShader(m_program_id, vertexShader);
         //将片元着色器加入到程序中
         glAttachShader(m_program_id, fragmentShader);
         //连接到着色器程序
         glLinkProgram(m_program_id);
         m_vertex_pos_handler = glGetAttribLocation(m_program_id, "aPosition");
         glDeleteShader(vertexShader);
         glDeleteShader(fragmentShader);
     }
    //使用OpenGL程序
     if (m_program_id != 0) {
            glUseProgram(m_program_id);
     }
}

GLuint TriangleDrawer::LoadShader(GLenum type, const GLchar *shader_code) {
    LOGI(TAG, "Load shader:n %s", shader_code)
    //根据type创建顶点着色器或者片元着色器
     GLuint shader = glCreateShader(type);
     //将资源加入到着色器中,并编译
     glShaderSource(shader, 1, &shader_code, NULL);
     glCompileShader(shader);
     GLint compiled;
     // 检查编译状态
     glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
     if (!compiled) {
         GLint infoLen = 0;
         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
         if (infoLen > 1) {
             GLchar *infoLog = (GLchar *) malloc(sizeof(GLchar) * infoLen);
             glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
             LOGI(TAG, "Error compiling shader:n%sn", infoLog);
             free(infoLog);
         }
        glDeleteShader(shader);
        return 0;
   }
   return shader;
}

void TriangleDrawer::DoDraw() {
    //启用顶点的句柄
     glEnableVertexAttribArray(m_vertex_pos_handler);
     glVertexAttribPointer(m_vertex_pos_handler, 3, GL_FLOAT, GL_FALSE, 0, m_vertex_coors);
     //开始绘制
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
}
           

上来就是一对代码是不是一脸懵,我刚开始学习的时候也是这样,上面的官方文档中也没给出什么能让人容易理解的文字描述。所以这也是这篇博客的意义。

我们先来看2个字符串

//顶点着色器(Vertex Shader)

const char *TriangleDrawer::GetVertexShader() {
    return "attribute vec4 aPosition; \n"
     "void main() \n"
     "{ \n"
     "   gl_Position = aPosition; \n"
     "} \n";
}

           

//片段着色器(Fragment Shader)

const char *TriangleDrawer::GetFragmentShader() {
    return "precision mediump float; \n"
     "void main() \n"
     "{ \n"
     "   gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n"
     "} \n";
}
           

这2个字符串就是我们所谓的OpenGL Shader代码,它是用一种被称作GLSL(GL Shader Language)的类似C语言的语言所写的,是不是很拗口,其实把GLSL当做是一种开发语言就行了,在开发语言层面上跟java啦,C啊差不多,只不过它的写法与C十分类似。既然是一种开发语言,数据结构、关键字、语法这些东西总是绕不过的,不过GLSL虽然比较简单,但是完整的介绍其语法也不是一两句能说清楚的,我们后面会说,这不是本章的重点,本章的重点是我们已经写好了上面的Vertex Shader和Fragment Shader,但是OpenGL如何使用呢,下面看图

Android音视频 - OpenGL ES 环境搭建与简单三角形的绘制OpenGL ES Android 环境搭建简单三角形的绘制总结

我上面的代码也是根据这个流程写的,这个是个通用的流程,大家可以封装一下,避免重复劳动

总结

本篇介绍了OpenGL ES在Android上的环境,并且用GLSL写了一个三角形增加信心,经过这篇文章之后我们应该对GLSL的使用流程有了初步的理解。

Github源码

继续阅读