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是一个接口,用来给用户自己处理回调
下面给出核心代码,完整代码链接放在文末
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
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如何使用呢,下面看图
我上面的代码也是根据这个流程写的,这个是个通用的流程,大家可以封装一下,避免重复劳动
总结
本篇介绍了OpenGL ES在Android上的环境,并且用GLSL写了一个三角形增加信心,经过这篇文章之后我们应该对GLSL的使用流程有了初步的理解。