天天看點

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源碼

繼續閱讀