NDK-第一个OpenGL示例

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NDK-第一个OpenGL示例相关的知识,希望对你有一定的参考价值。

代码结构如下:

一、Java层

LammyGLSurfaceView.java(用于展示渲染的OpenGL模型)

package com.example.lammy.openglstudy;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;

public class LammyGLSurfaceView extends GLSurfaceView {
    private LammyRenderer lammyRenderer;
    public LammyGLSurfaceView(Context context) {
        super(context);
        init();
    }

    public LammyGLSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void  init(){
        lammyRenderer = new LammyRenderer();
        Native.InitAssetManager(getContext().getAssets());
        // 这里申请 2 版本环境,若想3 则需要在 jni 层面去做
        setEGLContextClientVersion(2);
        setRenderer(lammyRenderer);// 设置OpenGL渲染器
    }
}

LammyRender.java(定义OpenGL渲染器)

package com.example.lammy.openglstudy;

import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class LammyRenderer implements GLSurfaceView.Renderer {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // gl.glClearColor(0.1f , 0.4f,0.6f , 1f);
       Log.loge("onSurfaceCreated ...........");
       Native.InitOpenGL();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // gl.glViewport(0,0,width ,height);
        Log.loge("onSurfaceChanged ...........");
        Native.OnViewportChanged((float)width ,(float) height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // gl.glClear(gl.GL_COLOR_BUFFER_BIT);
        // Log.loge("onDrawFrame ...........");
        Native.RenderOneFrame();
    }
}

Log.java(打印日志)

package com.example.lammy.openglstudy;

public class  Log{
    public static  void loge(String s){
        android.util.Log.e("lammy-java-log:" , s );
    }
}

MainActivity.java(主入口程序)

package com.example.lammy.openglstudy;

import android.os.Bundle;
import android.os.Environment;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private LammyGLSurfaceView lammyGLSurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        lammyGLSurfaceView = new LammyGLSurfaceView(this);
        setContentView(lammyGLSurfaceView);
        Log.loge(Environment.getExternalStorageState());
    }
}

Native.java(加载本地库并声明要调用的C函数)

package com.example.lammy.openglstudy;

import android.content.res.AssetManager;

public class Native {
    static {
        System.loadLibrary("native-lib");// 注意名称与CMakeLists.txt中一致
    }

    public static native void InitAssetManager(AssetManager am);
    public static native void InitOpenGL();
    public static native void OnViewportChanged(float width, float height);
    public static native void RenderOneFrame();
}
二、C++层

导入GLM库(glm是数学库,用于处理矩阵):https://sourceforge.net/projects/ogl-math/
导入时候只要将整个文件拷贝至cpp文件。要注意在CMakeLists.txt文件中配置头文件路径。

ggl.h(打印日志)

#ifndef OPENGLSTUDY_GGL_H
    #define OPENGLSTUDY_GGL_H
#endif

#include <jni.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <android/asset_manager_jni.h>
#include <android/asset_manager.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <string>
#include <sstream>
#include <vector>
#include <functional>
#include <map>
#include "glm/glm/glm.hpp"

#define __DEBUG__ANDROID__ON
//write debug images
#ifdef  __DEBUG__ANDROID__ON
    #include <android/log.h>
    // Define the LOGI and others for print debug infomation like the log.i in java
    #define LOG_TAG    "lammy-jni-log:"
    #define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
    #define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
    #define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)
    #define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
#endif

opengl.cpp(通过AAssetManager指针从JNI层读取文件)

#include "ggl.h"
#include "scene.h"

AAssetManager *aAssetManager = nullptr;

unsigned char *LoadFileContent(const char *path, int &filesSize) {
    unsigned char *fileContent = nullptr;
    filesSize = 0;
    AAsset *asset = AAssetManager_open(aAssetManager, path, AASSET_MODE_UNKNOWN);
    if (asset == nullptr) {
        LOGE("LoadFileContent asset is null, load shader error ");
        return nullptr;
    }
    filesSize = AAsset_getLength(asset);
    fileContent = new unsigned char[filesSize];
    AAsset_read(asset, fileContent, filesSize);
    fileContent[filesSize] = '\\0';
    AAsset_close(asset);
    LOGE("LoadFileContent success ...%s", path);
    return fileContent;
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_lammy_openglstudy_Native_InitAssetManager(JNIEnv *env, jclass type, jobject am) {
    aAssetManager = AAssetManager_fromJava(env, am);
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_lammy_openglstudy_Native_InitOpenGL(JNIEnv *env, jclass type) {
    // glClearColor(0.1 , 0.4,0.6 , 1);
    Init();
    // InitModel(aAssetManager , "model/Cube.obj" );
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_lammy_openglstudy_Native_OnViewportChanged(JNIEnv *env, jclass type, jfloat width, jfloat height) {
    // glViewport(0,0,width ,height);
    SetViewPortSize(width, height);
}

float GetFrameTime() {
    static unsigned long long lastTime = 0, currentTime = 0;
    timeval current;
    gettimeofday(&current, nullptr);
    // 将时间转化为毫秒
    currentTime = current.tv_sec * 1000 + current.tv_usec / 1000;
    unsigned long long frameTime = lastTime == 0 ? 0 : currentTime - lastTime;
    lastTime = currentTime;
    return float(frameTime) / 1000.0f;
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_lammy_openglstudy_Native_RenderOneFrame(JNIEnv *env, jclass type) {
    // glClear(GL_COLOR_BUFFER_BIT);
    Draw();
}

scene.h(绘制场景头文件)

#ifndef OPENGLSTUDY_SCENE_H
    #define OPENGLSTUDY_SCENE_H
#endif

#include "ggl.h"

void Init();// 初始化OpenGL数据
void SetViewPortSize(float width , float height);// 设置视口并更新透视矩阵
void Draw();// 完成绘制

scene.cpp(实现真正的场景绘制)

#include "scene.h"
#include "ggl.h"
#include "utils.h"
#include "glm/glm/gtc/matrix_transform.hpp"
#include "glm/glm/ext.hpp"
#include "glm/glm/detail/_noise.hpp"

// 将数据从 cpu 放到 gpu
GLuint vbo, ebo;
GLuint program;
GLint positionLocation, modelMatrixLocation, viewMatrixLocation, projectMatrixLocation, colorLoction;
GLint texcoordLocation, textureLocation;
GLint texture;
// 不初始化就是单位矩阵
glm::mat4 modelMatrix, viewMatrix, projectMatrix;

void Init() {
    float data[] = {
            -0.2f, -0.2f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
            0.0f, 0.2f, -0.2f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
            1.0f, 0.0f, 0.0f, 0.2f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
            1.0f, 0.5f, 1.0f
    };

    glGenBuffers(1, &vbo); // 1 表示需要一个vbo , 后面的vbo 指向显存块
    glBindBuffer(GL_ARRAY_BUFFER, vbo);//绑定显存地址
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 30, data, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);// 设置当前buffer为0 即解绑

    // 利用element绘制 ebo 来控制绘制点的顺序
    unsigned short indexes[] = {0, 1, 2};
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * 3, indexes, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    int fileSize = 0;
    // 从assets目录加载顶点着色器代码
    unsigned char *shaderCode = LoadFileContent("test.vs", fileSize);
    GLuint vsShader = CompileShader(GL_VERTEX_SHADER, (char *) shaderCode);
    delete shaderCode;
    // 从assets目录加载片段着色器代码
    shaderCode = LoadFileContent("test.fs.glsl", fileSize);
    GLint fsShader = CompileShader(GL_FRAGMENT_SHADER, (char *) shaderCode);
    program = CreateProgram(vsShader, fsShader);
    glDeleteShader(vsShader);
    glDeleteShader(fsShader);

    // attribute 的插槽 和 uniform 插槽不一样,并且都是从 0 开始的
    positionLocation = glGetAttribLocation(program, "position");
    colorLoction = glGetAttribLocation(program, "color");
    modelMatrixLocation = glGetUniformLocation(program, "ModelMatrix");
    viewMatrixLocation = glGetUniformLocation(program, "ViewMatrix");
    projectMatrixLocation = glGetUniformLocation(program, "ProjectionMatrix");
    textureLocation = glGetUniformLocation(program, "U_texture");
    texcoordLocation = glGetAttribLocation(program, "texcoord");

    glm::mat4 model;
    modelMatrix = glm::translate(model, glm::vec3(0.0f, 0.0f, -0.6f));
    texture = CreateTextureFromBMP("test2.bmp");
}

void SetViewPortSize(float width, float height) {
    glViewport(0, 0, width, height);
    projectMatrix = glm::perspective(45.0f, width / height, 0.1f, 1000.0f);
}

void Draw() {
    glClearColor(0.1f, 0.4f, 0.6f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(program);

    glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, glm::value_ptr(modelMatrix));
    glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, glm::value_ptr(viewMatrix));
    glUniformMatrix4fv(projectMatrixLocation, 1, GL_FALSE, glm::value_ptr(projectMatrix));

    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(textureLocation, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glEnableVertexAttribArray(positionLocation);
    glEnableVertexAttribArray(texcoordLocation);
    glEnableVertexAttribArray(colorLoction);
    // 参数说明: GL_FALSE 表示是否需要将数据映射到0-1,这里本来是浮点,不需要;
    // 0 表示 vbo 中数据的起始位置
    glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 10, 0);
    glVertexAttribPointer(colorLoction, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 10,
                          (void *) (sizeof(float) * 4));
    glVertexAttribPointer(texcoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 10,
                          (void *) (sizeof(float) * 8));

    // glDrawArrays(GL_TRIANGLES, 0 , 3);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glUseProgram(0);
}

utils.h(完成创建纹理,程序,连接程序工具类)

#ifndef OPENGLSTUDY_UTILS_H
    #define OPENGLSTUDY_UTILS_H
#endif

#include "ggl.h"

unsigned char * LoadFileContent(const char *path , int &fileSize);
GLuint CompileShader(GLenum shaderType , const char * shaderCode)我的OpenGL学习进阶之旅NDK开发中find_library查找的系统动态库在哪里?

OpenGL——第一个 OpenGL 程序

OpenGL ES3 非常好的系列文章

NDK OpenGL 未定义对 glVertexPointer 的引用

Android 逆向Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | Visual Studio 中 SDK 和 NDK 安装位置 )(代码片段

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情