【OpenGL ES】构建可移植、易扩展的代码书写结构
JAVA语言
分离GLSL语言创建工具类传入环境 C++语言
分离GLSL语言创建工具类传入环境
总结
不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!
MainActivity
package com.example.openglndk;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
//JNI类
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
setContentView(mGLSurfaceView);
//设置版本ES 3.0
mGLSurfaceView.setEGLContextClientVersion(3);
//设置渲染器 JAVA版本
GLSurfaceView.Renderer renderer = new MyRenderer(this);
//设置渲染器 C++版本
// GLSurfaceView.Renderer renderer = new WaveRenderer(this);
mGLSurfaceView.setRenderer(renderer);
}
}
JAVA语言
分离GLSL语言
创建assets文件夹
在assets文件夹中创建glsl文件
工具类中有三个方法。
readFileFromAssets
public static String readFileFromAssets(Context context, String fileName) {
//初始化,关流判断
InputStream inputStream = null;
Reader reader = null;
BufferedReader bufferedReader = null;
//底层更快,性能更好
StringBuilder shaderCode = new StringBuilder();
try {
//得到资源中的asset数据流
inputStream = context.getResources().getAssets().open(fileName); //字节输入流
reader = new InputStreamReader(inputStream);// 字符流
bufferedReader = new BufferedReader(reader); //缓冲流
String temp;
//从第一行数据读取到最后一行为止
while ((temp = bufferedReader.readLine()) != null) {
//连起来,成为字符串
shaderCode.append(temp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//先打开的后关闭,后打开的先关闭
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return shaderCode.toString();
}
LoadShader
public static int LoadShader(int type, String shaderCode) {
//创建一个着色器
final int shaderId = GLES30.glCreateShader(type);
if (shaderId != 0) {
//加载到着色器
GLES30.glShaderSource(shaderId, shaderCode);
//编译着色器
GLES30.glCompileShader(shaderId);
//检测状态
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0) {
String logInfo = GLES30.glGetShaderInfoLog(shaderId);
System.err.println(logInfo);
//创建失败
GLES30.glDeleteShader(shaderId);
return 0;
}
return shaderId;
} else {
//创建失败
return 0;
}
}
linkProgram
public static int linkProgram(int vertexShader, int fragmentShader) {
final int program = GLES30.glCreateProgram();
if (program != 0) {
//将顶点着色器加入到程序
GLES30.glAttachShader(program, vertexShader);
//将片元着色器加入到程序中
GLES30.glAttachShader(program, fragmentShader);
//链接着色器程序
GLES30.gllinkProgram(program);
final int[] linkStatus = new int[1];
GLES30.glGetProgramiv(program, GLES30.GL_link_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
String logInfo = GLES30.glGetProgramInfoLog(program);
System.err.println(logInfo);
GLES30.glDeleteProgram(program);
return 0;
}
return program;
} else {
//创建失败
return 0;
}
}
传入环境
创建工具类和JAVA版本的一样。
在工具类中需加入下列头文件。
#include#include #include #include #include #include
工具类中还是三个方法。
readFileFromAssets
char *readFileFromAssets(JNIEnv *env, jobject assetManager, char *fileName) {
//创建资源
AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);
if (mgr == NULL) {
return nullptr;
}
//根据文件名打开资源文件
AAsset *asset = AAssetManager_open(mgr, fileName, AASSET_MODE_UNKNOWN);
if (NULL == asset) {
return nullptr;
}
//获取文件长度
long size = AAsset_getLength(asset);
//获取内存,存储数据
char *shaderCode = (char *) malloc(sizeof(char) * size + 1);
//结尾符号
shaderCode[size] = ' ';
//读取资源文件到buffer缓冲区中
AAsset_read(asset, shaderCode, size);
//不可释放内存,释放了数据就没了
// free(shaderCode);
//释放资源
AAsset_close(asset);
//返回数据
return shaderCode;
}
LoadShader
GLuint LoadShader(int type, char *shaderCode) {
//创建一个着色器
GLuint shader = glCreateShader(type);
if (shader != 0) {
//加载到着色器
glShaderSource(shader, 1, &shaderCode, NULL);
//编译着色器
glCompileShader(shader);
//检测状态
GLint compileStatus;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus == 0) {
//声明log长度变量
GLint infoLen = 0;
//获取长度并赋值
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
//创建成功小于等于1
char *infoLog = static_cast(malloc(sizeof(char) * infoLen));
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
cout << "SHADER ERROR!" << endl;
free(infoLog);
}
//创建失败
glDeleteShader(shader);
return 0;
}
return shader;
} else {
//创建失败
return 0;
}
}
linkProgram
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader) {
GLuint program = glCreateProgram();
if (program != 0) {
//将顶点着色器加入到程序
glAttachShader(program, vertexShader);
//将片元着色器加入到程序中
glAttachShader(program, fragmentShader);
//链接着色器程序
gllinkProgram(program);
//检测状态
GLint linkStatus;
glGetProgramiv(program, GL_link_STATUS, &linkStatus);
if (linkStatus == 0) {
GLint infoLen = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char *infoLog = static_cast(malloc(sizeof(char) * infoLen));
glGetProgramInfoLog(program, infoLen, NULL, infoLog);
cout << "PROGRAM ERROR!" << endl;
free(infoLog);
}
glDeleteProgram(program);
return 0;
}
return program;
} else {
//创建失败
return 0;
}
}
传入环境
根据上述划分,可以分为五部分:
- GLSL语句文件。工具类来实现读取文件、创建和编译着色器、链接统一管理程序等功能。在构造方法中,利用上下文环境来使用这些方法。在surfaceChanged中确定图形尺寸。在drawframe中进行图形绘制。
如果是GLSL语句发生变化,改变其文件即可;绘制图形变化,改变drawframe方法即可。所以这样只要哪一个部分改变,修改那个部分即可,实现了分离,利于维护和扩展代码。



