【OpenGL ES】构建可移植、易扩展的代码书写结构


不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!

【OpenGL ES】构建可移植、易扩展的代码书写结构

  • 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
    /**
         * 读取assets文件夹中的文件    提取着色器命令
         *
         * @param fileName 文件名
         * @param context   上下文环境
         * @return
         */
        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
    /**
         * 创建、编译着色器
         *
         * @param type       顶点着色器:GLES30.GL_VERTEX_SHADER
         *                   片段着色器:GLES30.GL_FRAGMENT_SHADER
         * @param shaderCode
         * @return
         */
        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
    /**
         * 链接统一管理程序
         *
         * @param vertexShader   顶点着色器
         * @param fragmentShader 片段着色器
         * @return
         */
        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;
            }
        }
    

传入环境

在这里插入图片描述
在这里插入图片描述

C++语言

分离GLSL语言

和JAVA版本的一样。

创建工具类

在工具类中需加入下列头文件。

#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <malloc.h>
#include <iostream>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
  • 工具类中还是三个方法。

    • readFileFromAssets
    /**
     *   读取文件
     * @param env    jni环境
     * @param assetManager  资源
     * @param fileName    文件名
     * @return   数据字符串
     */
    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] = '\0';
        //读取资源文件到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<char *>(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<char *>(malloc(sizeof(char) * infoLen));
                    glGetProgramInfoLog(program, infoLen, NULL, infoLog);
                    cout << "PROGRAM ERROR!" << endl;
                    free(infoLog);
                }
                glDeleteProgram(program);
                return 0;
            }
            return program;
        } else {
            //创建失败
            return 0;
        }
    }
    

传入环境

在这里插入图片描述
在这里插入图片描述

总结

根据上述划分,可以分为五部分:

  1. GLSL语句文件。
  2. 工具类来实现读取文件、创建和编译着色器、链接统一管理程序等功能。
  3. 在构造方法中,利用上下文环境来使用这些方法。
  4. surfaceChanged中确定图形尺寸。
  5. drawFrame中进行图形绘制。

如果是GLSL语句发生变化,改变其文件即可;绘制图形变化,改变drawFrame方法即可。所以这样只要哪一个部分改变,修改那个部分即可,实现了分离,利于维护和扩展代码。

猜你喜欢

转载自blog.csdn.net/qq_46546793/article/details/123471953