【OpenGL ES】二维图形绘制


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

OpenGL ES 学习——2D

颜色的简单搭配:

不红+不绿+不蓝 = 黑

红+绿+蓝 = 白

红+绿 = 黄

红+蓝 = 紫

绿+蓝 = 青蓝

着色器语言基础知识

这位博主是根据书本《OpenGL ES 3.0编程指南》第五章进行书写的。

参考博客:android平台下OpenGL ES 3.0着色语言基础知识(上)

参考博客:android平台下OpenGL ES 3.0着色语言基础知识(下)

绘制纯色背景

JAVA版本

  • MainActivity
package com.example.openglndk;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
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);
        //设置渲染器
        GLSurfaceView.Renderer renderer = new MyRenderer(Color.RED);
        mGLSurfaceView.setRenderer(renderer);
    }
}
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.opengl.GLSurfaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • Renderer类(核心)
package com.example.openglndk;

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

public class MyRenderer implements GLSurfaceView.Renderer {
    
    
    //声明成员变量
    private int color;

    //构造方法初始化
    public MyRenderer(int color){
    
    
        this.color = color;
    }

    //实现接口方法
    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        //分离RGBA的百分比
        float redF = ((color >> 16) & 0xFF) * 1.0f / 255;
        float greenF = ((color >> 8) & 0xFF) * 1.0f / 255;
        float blueF = (color & 0xFF) * 1.0f / 255;
        float alphaF = ((color >> 24) & 0xFF) * 1.0f / 255;
        GLES30.glClearColor(redF, greenF, blueF, alphaF);
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        //设置视口
        GLES30.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        //把颜色缓冲区设置为我们预设的颜色
        //清楚颜色缓存
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
    }
}
  • CMakeLists.txt.cpp文件不需要。

C++版本

  • MainActivity
package com.example.openglndk;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
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);
        //设置渲染器
//        GLSurfaceView.Renderer renderer = new MyRenderer(Color.RED);
        GLSurfaceView.Renderer renderer = new WaveRenderer(Color.GREEN);
        mGLSurfaceView.setRenderer(renderer);
    }
}
  • activity_main.xml不变。
  • 自定义渲染器(JNI类)
  • Renderer类(改变)
package com.example.openglndk;

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


public class WaveRenderer implements GLSurfaceView.Renderer {
    
    
    //链接到库文件
    static{
    
    
        System.loadLibrary("native-wave");
    }

    //声明本地方法
    public native void init();

    public native void surfaceCreated(int color);

    public native void surfaceChanged(int width,int height);

    public native void drawFrame();

    //声明成员变量
    private int color;

    //构造方法初始化
    public WaveRenderer(int color){
    
    
        this.color = color;
    }

    //实现接口方法
    //用三个本地方法复写三个接口方法
    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        surfaceCreated(color);
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        surfaceChanged(width,height);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        drawFrame();
    }
}
  • CMakeLists.txt——后续不变。
# Sets the minimum version of CMake required to build the native library.
#cmake_minimum_required(VERSION 3.10.2)
cmake_minimum_required(VERSION 3.4.1)

##官方标准配置
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -Wall")

##ANDROID_PLATFORM_LEVEL=18
add_definitions("-DDYNAMIC_ES3")
set(OPENGL_LIB GLESv3)

# Declares and names the project.
project("openglndk")


add_library( # Sets the name of the library.
        native-wave

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-wave.cpp)


find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)


target_link_libraries(native-wave
        ${
    
    OPENGL_LIB}
        android
        EGL
        log
        m)
  • .cpp文件
//
// Created by lvjunkai on 2022/2/16.
//
#include <jni.h>
#include <EGL/egl.h>
#include <GLES3/gl3.h>

//书写本地方法的具体逻辑
JNIEXPORT void JNICALL init(JNIEnv *env, jobject obj) {

}

JNIEXPORT void JNICALL surfaceCreated(JNIEnv *env, jobject obj, jint color) {
//分离RGBA的百分比
    GLfloat redF = ((color >> 16) & 0xFF) * 1.0f / 255;
    GLfloat greenF = ((color >> 8) & 0xFF) * 1.0f / 255;
    GLfloat blueF = (color & 0xFF) * 1.0f / 255;
    GLfloat alphaF = ((color >> 24) & 0xFF) * 1.0f / 255;
    glClearColor(redF, greenF, blueF, alphaF);
}

JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) {
    //设置视口
    glViewport(0, 0, width, height);
}

JNIEXPORT void JNICALL drawFrame(JNIEnv *env, jobject obj) {
    //把颜色缓冲区设置为我们预设的颜色
    //清楚颜色缓存
    glClear(GL_COLOR_BUFFER_BIT);
}

/**
 * 动态注册
 */
//本地方法声明和具体逻辑连接
JNINativeMethod methods[] = {
        //双引号中的方法是Renderer类中声明的本地方法名
        //后面的方法是cpp文件中具体实现方法名
        {"init", "()V", (void *) init},
        {"surfaceCreated", "(I)V",  (void *) surfaceCreated},
        {"surfaceChanged", "(II)V", (void *) surfaceChanged},
        {"drawFrame",      "()V",   (void *) drawFrame}
};


/**
 * 动态注册
 * @param env
 * @return
 */
jint registerNativeMethod(JNIEnv *env) {
    //到本地方法存在的类找出其方法声明
    jclass cl = env->FindClass("com/example/openglndk/WaveRenderer");
    if ((env->RegisterNatives(cl, methods, sizeof(methods) / sizeof(methods[0]))) < 0) {
        return -1;
    }
    return 0;
}

/**
 * 加载默认回调
 * @param vm
 * @param reserved
 * @return
 */
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    //注册方法
    if (registerNativeMethod(env) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}

绘制圆点、直线、三角形

  • 顶点着色器(GLSL语言)(一种仿C的语言)
attribute vec4 vPosition; 
uniform mat4 vMatrix;
void main() {
    
    
  gl_Position = vMatrix*vPosition;
  gl_PointSize = 10.0;  
}
  • 片元着色器
precision mediump float;
uniform vec4 vColor;
void main() {
    
    
  gl_FragColor = vColor;
}
  • 圆点
glDrawArrays(GL_POINTS,0,3);
  • 直线
glLineWidth(10.0f);
glDrawArrays(GL_LINE_STRIP,0,2);
  • 三角形
glDrawArrays(GL_TRIANGLES,0,3);

以下示例皆以三角形为例子,需要变换形状,只需修改绘制上述代码。

JAVA版本

  • MainActivity——后续不改变。
package com.example.openglndk;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
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();
        //设置渲染器  C++版本
//        GLSurfaceView.Renderer renderer = new WaveRenderer();
        mGLSurfaceView.setRenderer(renderer);
    }
}
  • activity_main.xml一直不变,后续不再赘述。
  • 核心类——Renderer类
package com.example.openglndk;

import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyRenderer implements GLSurfaceView.Renderer {
    
    
    //坐标系xyz
    private final static int COORDS_PER_VERTEX = 3;
    //顶点之间的偏移量
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
    //顶点内存
    private FloatBuffer vertexBuffer;
    //着色器和管理程序
    private int vertexShader;
    private int fragmentShader;
    private int mProgram;
    //设置颜色,依次为红绿蓝和透明通道
    private float color[] = {
    
    1.0f, 0.0f, 1.0f, 0.0f};

    //定义点坐标
    private float vertexPoints[] = {
    
    
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
    };

    //点数量
    private int vertexCount = vertexPoints.length / COORDS_PER_VERTEX;

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 vMatrix;" +
                    "void main() {" +
                    "  gl_Position = vMatrix*vPosition;" +
                    "  gl_PointSize = 10.0;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

    public MyRenderer() {
    
    
        //坐标内存
        //分配内存空间,每个浮点型占4字节空间
        //ByteBuffer相当于使用gpu的桥梁
        //gpu内存排列顺序,使用默认
        //转化为管道
        vertexBuffer = ByteBuffer.allocateDirect(
                vertexPoints.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        //传入指定的坐标数据
        //将语法推送给GPU
        vertexBuffer.put(vertexPoints);
        vertexBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        //设置背景为白色
        GLES30.glClearColor(1, 1, 1, 1);
        vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER,
                vertexShaderCode);
        fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER,
                fragmentShaderCode);
        mProgram = linkProgram(vertexShader, fragmentShader);
    }


    private float mProjectionMatrix[] = new float[16];
    private float mViewMatrix[] = new float[16];
    private float mMVPMatrix[] = new float[16];

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        GLES30.glViewport(0, 0, width, height);
        //宽高比
        float ratio = (float) width / height;
        //正交投影矩阵
        Matrix.orthoM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 0, 5);
        //设置透视投影矩阵
        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        //设置相机角度(核心)
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //矩阵合并
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
    }

    //每20ms刷新
    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        //清除颜色缓冲区
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
        //将程序加入
        GLES30.glUseProgram(mProgram);
        //获取变换矩阵vMatrix成员句柄
        //图形参数
        int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
        //指定vMatrix的值
        GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
        //获取顶点着色器的vPosition成员句柄
        int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
        //启用顶点的句柄
        GLES30.glEnableVertexAttribArray(mPositionHandle);
        //准备坐标数据
        GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES30.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        //获取片元着色器的vColor成员的句柄
        int mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
        //设置图形的颜色
        GLES30.glUniform4fv(mColorHandle, 1, color, 0);
        //绘制图形
        GLES30.glLineWidth(10.0f);
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);
        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(mPositionHandle);
    }

    /**
     * 编译
     *
     * @param type       顶点着色器:GLES30.GL_VERTEX_SHADER
     *                   片段着色器:GLES30.GL_FRAGMENT_SHADER
     * @param shaderCode
     * @return
     */
    private 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;
        }
    }

    /**
     * 链接小程序
     *
     * @param vertexShaderId   顶点着色器
     * @param fragmentShaderId 片段着色器
     * @return
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
    
    
        final int programId = GLES30.glCreateProgram();
        if (programId != 0) {
    
    
            //将顶点着色器加入到程序
            GLES30.glAttachShader(programId, vertexShaderId);
            //将片元着色器加入到程序中
            GLES30.glAttachShader(programId, fragmentShaderId);
            //链接着色器程序
            GLES30.glLinkProgram(programId);
            final int[] linkStatus = new int[1];
            GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] == 0) {
    
    
                String logInfo = GLES30.glGetProgramInfoLog(programId);
                System.err.println(logInfo);
                GLES30.glDeleteProgram(programId);
                return 0;
            }
            return programId;
        } else {
    
    
            //创建失败
            return 0;
        }
    }
}

C++版本

  • Renderer类——后续不改变。
package com.example.openglndk;

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


public class WaveRenderer implements GLSurfaceView.Renderer {
    
    
    //链接到库文件
    static{
    
    
        System.loadLibrary("native-wave");
    }

    //声明本地方法
    public native void init();

    public native void surfaceCreated();

    public native void surfaceChanged(int width,int height);

    public native void drawFrame();
    
    //构造函数
    public WaveRenderer(){
    
    
        init();
    }

    //实现接口方法
    //用三个本地方法复写三个接口方法
    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        surfaceCreated();
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        surfaceChanged(width,height);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        drawFrame();
    }
}
  • .cpp文件(核心)
//
// Created by lvjunkai on 2022/2/21.
//
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <jni.h>
#include <malloc.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>

//声明自定义函数
//创建编译加载
GLuint LoadShader(int type, char *shaderCode);

//链接统一程序
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);

//模板技术定义的函数
//用于求数组长度
template<class T>
GLsizei getArrayLen(T &array) {
    return (sizeof(array) / sizeof(array[0]));
}

//坐标个数(xyz)
const GLint COORDS_PER_VERTEX = 3;

//顶点之间的偏移量
const int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

//声明顶点着色器
GLuint vertexShader;
//声明片元着色器
GLuint fragmentShader;
//声明着色器管理程序
GLuint mProgram;

//定义点坐标
float vertexPoints[] = {
        0.0f, 0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f
};

//设置颜色,依次为红绿蓝和透明通道
float color[] = {1.0f, 1.0f, 0.0f, 0.0f};

const int vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;


//顶点着色器代码
char vertexShaderCode[] = "attribute vec4 vPosition; \n"
                          "uniform mat4 vMatrix;\n"
                          "void main() {\n"
                          "  gl_Position = vMatrix*vPosition;\n"
                          "  gl_PointSize = 10.0;  \n"
                          "}";


//片段着色器代码
char fragmentShaderCode[] = "precision mediump float;"
                            "uniform vec4 vColor;"
                            "void main() {"
                            "  gl_FragColor = vColor;"
                            "}";

//书写本地方法的具体逻辑
void JNICALL init(JNIEnv *env, jobject obj) {
    //初始化
}

/**初始化
 *
 * @param env
 * @param obj
*/
void JNICALL surfaceCreated(JNIEnv *env, jobject obj) {
    //设置背景颜色为黑色
    glClearColor(0, 0, 0, 0);
    //创建顶点着色器
    vertexShader = LoadShader(GL_VERTEX_SHADER, vertexShaderCode);
    //创建片元着色器
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentShaderCode);
    //将顶点着色器和片元着色器交给统一程序管理
    mProgram = linkProgram(vertexShader, fragmentShader);
}

/**图形尺寸
 *
 * @param env
 * @param obj
 * @param width
 * @param height
*/
glm::mat4 mProjectionMatrix;
glm::mat4 mViewMatrix;
glm::mat4 mMVPMatrix;

JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) {
    //设置视口
    glViewport(0, 0, width, height);
    //宽高比
    float ratio = (float) width / height;
    //二维图形设置正交投影矩阵即可
    mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f,
                                   100.0f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置
                              glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标
                              glm::vec3(0, 1, 0));
    //矩阵合并
    mMVPMatrix = mProjectionMatrix * mViewMatrix;
}


/**渲染绘制
 *
 * @param env
 * @param obj
*/
JNIEXPORT void JNICALL drawFrame(JNIEnv *env, jobject obj) {
    //清除颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    //使用程序
    glUseProgram(mProgram);
    GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix");
    //矩阵转换成数组
    float mMVPMatrixArray[16];
    int m = 0;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++){
            mMVPMatrixArray[m] = mMVPMatrix[i][j];
            m++;
        }
    //指定vMatrix的值
    glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray);
    //获取顶点着色器的vPosition成员句柄
    GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition");
    //启用图形顶点的句柄
    glEnableVertexAttribArray(mPositionHandle);
    //准备图形的坐标数据
    glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride, vertexPoints);
    //获取片元着色器的vColor成员的句柄
    GLint mColorHandle = glGetUniformLocation(mProgram, "vColor");
    //设置绘制图形的颜色
    glUniform4fv(mColorHandle, 1, color);
    //顶点法绘制图形
    glLineWidth(10.0f);
    glDrawArrays(GL_TRIANGLES,0,vertexCount);
    //禁止顶点数组的句柄
    glDisableVertexAttribArray(mPositionHandle);
}


/**加载着色器
 *
 */
JNIEXPORT 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);
                printf("SHADER ERROR!");
                free(infoLog);
            }
            //创建失败
            glDeleteShader(shader);
            return 0;
        }
        return shader;
    } else {
        //创建失败
        return 0;
    }
}

/**链接着色器
 *
 */
JNIEXPORT 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);
                printf("PROGRAM ERROR!");
                free(infoLog);
            }
            glDeleteProgram(program);
            return 0;
        }
        return program;
    } else {
        //创建失败
        return 0;
    }
}

/** 动态注册
* @param env
 * @return
*/
//本地方法声明和具体逻辑连接
JNINativeMethod methods[] = {
        {"init", "()V", (void *) init},
        {"surfaceCreated", "()V",   (void *) surfaceCreated},
        {"surfaceChanged", "(II)V", (void *) surfaceChanged},
        {"drawFrame",      "()V",   (void *) drawFrame}
};

/** 动态注册
* @param env
* @return
*/
jint registerNativeMethod(JNIEnv *env) {
    //到本地方法存在的类找出其方法声明
    jclass cl = env->FindClass("com/example/openglndk/WaveRenderer");
    if ((env->RegisterNatives(cl, methods, sizeof(methods) / sizeof(methods[0]))) < 0) {
        return -1;
    }
    return 0;
}

/** 加载默认回调
* @param vm
* @param reserved
* @return
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    //注册方法
    if (registerNativeMethod(env) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}

绘制彩色三角形

  • 顶点着色器
#version 300 es 
in vec4 vPosition;
in vec4 vColor;
uniform mat4 vMatrix;
out vec4 aColor;
void main() {
    
    
     gl_Position  = vMatrix*vPosition;
     gl_PointSize = 10.0;
     aColor = vColor;
}
  • 片元着色器
#version 300 es
precision mediump float;
in vec4 aColor;
out vec4 fragColor;
void main() {
    
    
     fragColor = aColor;
}

JAVA版本

  • Renderer类
package com.example.openglndk;

import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

//渲染器,把3D物体绘制到屏幕上
public class MyRenderer implements GLSurfaceView.Renderer {
    
    
    //坐标系xyz
    private final static int COORDS_PER_VERTEX = 3;
    //颜色通道(RGBA)
    private final static int COORDS_PER_COLOR = 4;
    //顶点之间的偏移量
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
    //颜色之间的偏移量
    private final int colorStride = COORDS_PER_COLOR * 4; // 每个颜色四个字节
    //声明内存分配的数据类型
    private FloatBuffer vertexBuffer, colorBuffer;
    //着色器和管理程序
    private int vertexShader;
    private int fragmentShader;
    private int mProgram;
    //彩色数据
    private float colors[] = {
    
    
            0.0f, 1.0f, 0.0f, 1.0f,
            1.0f, 0.0f, 0.0f, 1.0f,
            0.0f, 0.0f, 1.0f, 1.0f
    };

    //定义点坐标
    private float[] vertexPoints = {
    
    
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
    };

    //点数量
    private int vertexCount = vertexPoints.length / COORDS_PER_VERTEX;

    //顶点着色器代码
    private String vertexShaderCode = "#version 300 es\n" +
            "in vec4 vPosition;\n" +
            "in vec4 vColor;\n" +
            "uniform mat4 vMatrix;\n" +
            "out vec4 aColor;\n" +
            "void main() {\n" +
            "     gl_Position  = vMatrix*vPosition;\n" +
            "     gl_PointSize = 10.0;\n" +
            "     aColor = vColor;\n" +
            "}\n";

    //片元着色器代码
    private String fragmentShaderCode = "#version 300 es\n" +
            "precision mediump float;\n" +
            "in vec4 aColor;\n" +
            "out vec4 fragColor;\n" +
            "void main() {\n" +
            "     fragColor = aColor;\n" +
            "}\n";


    //构造函数
    public MyRenderer() {
    
    
        //分配顶点内存
        vertexBuffer = ByteBuffer.allocateDirect(vertexPoints.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        //传入指定的坐标数据
        //将语法推送给GPU
        vertexBuffer.put(vertexPoints);
        vertexBuffer.position(0);
        //分配颜色内存
        colorBuffer = ByteBuffer.allocateDirect(colors.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        //传入指定的数据
        colorBuffer.put(colors);
        colorBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        //设置背景颜色为白色
        GLES30.glClearColor(1, 1, 1, 1);
        //创建顶点着色器
        vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode);
        //创建片元着色器
        fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode);
        //将顶点着色器和片元着色器交给统一程序管理
        mProgram = linkProgram(vertexShader, fragmentShader);
    }

    /**
     * 图形尺寸
     *
     * @param gl10
     * @param width
     * @param height
     */
    private float mProjectionMatrix[] = new float[16];
    private float mViewMatrix[] = new float[16];
    private float mMVPMatrix[] = new float[16];

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        //设置视口
        GLES30.glViewport(0, 0, width, height);
        //宽高比
        float ratio = (float) width / height;
        //设置透视投影矩阵
        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        //设置相机角度(核心)
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //矩阵合并
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
    }

    /**
     * @param gl10 渲染
     */
    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        //清除颜色缓冲区
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
        //使用统一的管理程序
        GLES30.glUseProgram(mProgram);
        //获取变换矩阵vMatrix成员句柄
        //图形参数
        int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
        //指定vMatrix的值
        GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
        //获取顶点着色器的vPosition成员句柄
        int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
        //启用顶点的句柄
        GLES30.glEnableVertexAttribArray(mPositionHandle);
        //准备坐标数据
        GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES30.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        //获取片元着色器的vColor成员的句柄
        //因为是彩色,数据类型和顶点类似,所以必须是glGetAttribLocation方法
        //glGetUniformLocation方法画不全颜色,会变成黑色
        int mColorHandle = GLES30.glGetAttribLocation(mProgram, "vColor");
        //启动颜色的句柄
        GLES30.glEnableVertexAttribArray(mColorHandle);
        //设置图形的颜色
        //准备颜色数据
        GLES30.glVertexAttribPointer(mColorHandle, COORDS_PER_COLOR,
                GLES30.GL_FLOAT, false,
                colorStride, colorBuffer);
//        GLES30.glUniform4fv(mColorHandle, 1, colors, 0);
        //绘制图形
        GLES30.glLineWidth(10.0f);
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);
        //禁止颜色数组的句柄
        GLES30.glDisableVertexAttribArray(mColorHandle);
        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(mPositionHandle);
    }


    /**
     * 编译
     *
     * @param type       顶点着色器:GLES30.GL_VERTEX_SHADER
     *                   片段着色器:GLES30.GL_FRAGMENT_SHADER
     * @param shaderCode
     * @return
     */
    private 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;
        }
    }

    /**
     * 链接小程序
     *
     * @param vertexShaderId   顶点着色器
     * @param fragmentShaderId 片段着色器
     * @return
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
    
    
        final int programId = GLES30.glCreateProgram();
        if (programId != 0) {
    
    
            //将顶点着色器加入到程序
            GLES30.glAttachShader(programId, vertexShaderId);
            //将片元着色器加入到程序中
            GLES30.glAttachShader(programId, fragmentShaderId);
            //链接着色器程序
            GLES30.glLinkProgram(programId);
            final int[] linkStatus = new int[1];
            GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] == 0) {
    
    
                String logInfo = GLES30.glGetProgramInfoLog(programId);
                System.err.println(logInfo);
                GLES30.glDeleteProgram(programId);
                return 0;
            }
            return programId;
        } else {
    
    
            //创建失败
            return 0;
        }
    }
}

C++版本

  • .cpp文件
//
// Created by lvjunkai on 2022/2/21.
//
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <jni.h>
#include <malloc.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>

//声明自定义函数
//创建编译加载
GLuint LoadShader(int type, char *shaderCode);

//链接统一程序
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);

//模板技术定义的函数
//用于求数组长度
template<class T>
GLsizei getArrayLen(T &array) {
    return (sizeof(array) / sizeof(array[0]));
}

//坐标个数(xyz)
const GLint COORDS_PER_VERTEX = 3;
//颜色通道(RGBA)
const GLint COORDS_PER_COLOR = 4;

//顶点之间的偏移量
const GLsizei vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
//颜色之间的偏移量
const GLsizei colorStride = COORDS_PER_COLOR * 4; // 每个颜色四个字节

//声明顶点着色器
GLuint vertexShader;
//声明片元着色器
GLuint fragmentShader;
//声明着色器管理程序
GLuint mProgram;

//定义圆点坐标
float vertexPoints[] = {
        0.0f, 0.5f, 0.0f, //上侧点
        -0.5f, -0.5f, 0.0f, //左侧点
        0.5f, -0.5f, 0.0f  //右侧点
};

//定义颜色数据
float colors[] = {
        1.0f, 0.5f, 1.0f, 1.0f, //顶部
        1.0f, 1.0f, 0.5f, 1.0f, //左侧
        0.5f, 1.0f, 1.0f, 1.0f  //右侧
};

//点数量
int vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;

//顶点着色器代码
char vertexShaderCode[] = "#version 300 es \n"
                          "in vec4 vPosition;\n"
                          "in vec4 vColor;\n"
                          "uniform mat4 vMatrix;\n"
                          "out vec4 aColor;\n"
                          "void main() {\n"
                          "     gl_Position  = vMatrix*vPosition;\n"
                          "     gl_PointSize = 10.0;\n"
                          "     aColor = vColor;\n"
                          "}";

//片段着色器代码
char fragmentShaderCode[] = "#version 300 es\n"
                            "precision mediump float;\n"
                            "in vec4 aColor;\n"
                            "out vec4 fragColor;\n"
                            "void main() {\n"
                            "     fragColor = aColor;\n"
                            "}";


//书写本地方法的具体逻辑
void JNICALL init(JNIEnv *env, jobject obj) {
    //初始化
}

/**初始化
 *
 * @param env
 * @param obj
*/
void JNICALL surfaceCreated(JNIEnv *env, jobject obj) {
    //设置背景颜色为黑色
    glClearColor(0, 0, 0, 0);
    //创建顶点着色器
    vertexShader = LoadShader(GL_VERTEX_SHADER, vertexShaderCode);
    //创建片元着色器
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentShaderCode);
    //将顶点着色器和片元着色器交给统一程序管理
    mProgram = linkProgram(vertexShader, fragmentShader);
}

/**图形尺寸
 *
 * @param env
 * @param obj
 * @param width
 * @param height
*/
glm::mat4 mProjectionMatrix;
glm::mat4 mViewMatrix;
glm::mat4 mMVPMatrix;

JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) {
    //设置视口
    glViewport(0, 0, width, height);
    //宽高比
    float ratio = (float) width / height;
    //二维图形设置正交投影矩阵即可
    mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f,
                                   100.0f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置
                              glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标
                              glm::vec3(0, 1, 0));
    //矩阵合并
    mMVPMatrix = mProjectionMatrix * mViewMatrix;
}


/**渲染绘制
 *
 * @param env
 * @param obj
*/
JNIEXPORT void JNICALL drawFrame(JNIEnv *env, jobject obj) {
    //清除颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    //使用统一的管理程序
    glUseProgram(mProgram);
    //获取变换矩阵vMatrix成员句柄
    //图形参数
    GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix");
    //矩阵转换成数组
    float mMVPMatrixArray[16];
    int m = 0;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++){
            mMVPMatrixArray[m] = mMVPMatrix[i][j];
            m++;
        }
    //指定vMatrix的值
    glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray);
    //获取顶点着色器的vPosition成员句柄
    GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition");
    //启用顶点的句柄
    glEnableVertexAttribArray(mPositionHandle);
    //准备坐标数据
    glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                          GL_FLOAT, false,
                          vertexStride, vertexPoints);
    //获取片元着色器的vColor成员的句柄
    GLint mColorHandle = glGetAttribLocation(mProgram, "vColor");
    //启动颜色的句柄
    glEnableVertexAttribArray(mColorHandle);
    //准备颜色数据
    glVertexAttribPointer(mColorHandle, COORDS_PER_COLOR,
                          GL_FLOAT, false,
                          colorStride, colors);
    //绘制图形
    glLineWidth(10.0f);
    glDrawArrays(GL_TRIANGLES, 0, vertexCount);
    //禁止颜色数组的句柄
    glDisableVertexAttribArray(mColorHandle);
    //禁止顶点数组的句柄
    glDisableVertexAttribArray(mPositionHandle);
}


/**加载着色器
 *
 */
JNIEXPORT 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);
                printf("SHADER ERROR!");
                free(infoLog);
            }
            //创建失败
            glDeleteShader(shader);
            return 0;
        }
        return shader;
    } else {
        //创建失败
        return 0;
    }
}

/**链接着色器
 *
 */
JNIEXPORT 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);
                printf("PROGRAM ERROR!");
                free(infoLog);
            }
            glDeleteProgram(program);
            return 0;
        }
        return program;
    } else {
        //创建失败
        return 0;
    }
}

/** 动态注册
* @param env
 * @return
*/
//本地方法声明和具体逻辑连接
JNINativeMethod methods[] = {
        {"init", "()V", (void *) init},
        {"surfaceCreated", "()V", (void *) surfaceCreated},
        {"surfaceChanged", "(II)V", (void *) surfaceChanged},
        {"drawFrame", "()V", (void *) drawFrame}
};

/** 动态注册
* @param env
* @return
*/
jint registerNativeMethod(JNIEnv *env) {
    //到本地方法存在的类找出其方法声明
    jclass cl = env->FindClass("com/example/openglndk/WaveRenderer");
    if ((env->RegisterNatives(cl, methods, sizeof(methods) / sizeof(methods[0]))) < 0) {
        return -1;
    }
    return 0;
}

/** 加载默认回调
* @param vm
* @param reserved
* @return
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    //注册方法
    if (registerNativeMethod(env) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}

绘制纯色正方形

JAVA版本

  • Renderer类
package com.example.openglndk;

import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyRenderer implements GLSurfaceView.Renderer {
    
    
    /**
     * 坐标个数(xyz)
     */
    private final static int COORDS_PER_VERTEX = 3;

    //顶点之间的偏移量
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

    private FloatBuffer vertexBuffer;
    private ShortBuffer indexBuffer;

    private int vertexShader;
    private int fragmentShader;
    private int mProgram;

    //物体空间点坐标
    private float vertexPoints[] = {
    
    
            -0.5f, 0.5f, 0.0f, // top left
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f, // bottom right
            0.5f, 0.5f, 0.0f  // top right
    };

    private int vertexCount = vertexPoints.length / COORDS_PER_VERTEX;

    //设置颜色,依次为红绿蓝和透明通道
    //青蓝色
    private float color[] = {
    
    0.1f, 0.7f, 1.0f, 0.0f};

    //索引
    private short index[] = {
    
    
            0, 1, 2, 0, 2, 3
    };

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 vMatrix;" +
                    "void main() {" +
                    "  gl_Position = vMatrix*vPosition;" + //乘以投影矩阵
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";


    public MyRenderer() {
    
    
        //坐标
        vertexBuffer = ByteBuffer.allocateDirect(
                vertexPoints.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        vertexBuffer.put(vertexPoints);
        vertexBuffer.position(0);
        //索引
        indexBuffer = ByteBuffer.allocateDirect(index.length * 2)
                .order(ByteOrder.nativeOrder())
                .asShortBuffer();
        indexBuffer.put(index);
        indexBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        GLES30.glClearColor(1, 1, 1, 1);
        vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER,
                vertexShaderCode);
        fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER,
                fragmentShaderCode);
        mProgram = linkProgram(vertexShader, fragmentShader);
    }

    private float mProjectionMatrix[] = new float[16];
    private float mViewMatrix[] = new float[16];
    private float mMVPMatrix[] = new float[16];

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        GLES30.glViewport(0, 0, width, height);
        //宽高比
        //(float) (width / height)错误,两个整数相除,得到整数再变成float,有误差
        //必须先转换,再计算
        float ratio = width > height ? (float) width / height : (float) height / width;
        if(width > height){
    
    
            //设置正交投影矩阵,将物体空间的坐标映射
            //orthoM函数产生的矩阵会把所有的左右之间、上下之间,远近之间的点映射到归一化设备坐标中
            //横屏
            Matrix.orthoM(mProjectionMatrix,0,-ratio,ratio,-1,1,0,5);
        }else{
    
    
            //竖屏
            Matrix.orthoM(mProjectionMatrix,0,-1,1,-ratio,ratio,0,5);
        }
        //设置透视投影矩阵
//        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        //设置相机角度
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //矩阵合并
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
        //将程序加入
        GLES30.glUseProgram(mProgram);
        //图形参数
        int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
        //指定vMatrix的值
        GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
        //获取顶点着色器的vPosition成员句柄
        int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
        //启用顶点的句柄
        GLES30.glEnableVertexAttribArray(mPositionHandle);
        //准备坐标数据
        GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES30.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        //获取片元着色器的vColor成员的句柄
        int mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
        //设置绘制三角形的颜色
        GLES30.glUniform4fv(mColorHandle, 1, color, 0);
        //顶点法绘制正方形
        GLES30.glLineWidth(10.0f);
        GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, vertexCount);
        //索引法绘制正方形
//        GLES30.glDrawElements(GLES30.GL_TRIANGLES, index.length, GLES30.GL_UNSIGNED_SHORT, indexBuffer);
        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(mPositionHandle);
    }

    /**
     * 编译
     *
     * @param type       顶点着色器:GLES30.GL_VERTEX_SHADER
     *                   片段着色器:GLES30.GL_FRAGMENT_SHADER
     * @param shaderCode
     * @return
     */
    private 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;
        }
    }

    /**
     * 链接小程序
     *
     * @param vertexShaderId   顶点着色器
     * @param fragmentShaderId 片段着色器
     * @return
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
    
    
        final int programId = GLES30.glCreateProgram();
        if (programId != 0) {
    
    
            //将顶点着色器加入到程序
            GLES30.glAttachShader(programId, vertexShaderId);
            //将片元着色器加入到程序中
            GLES30.glAttachShader(programId, fragmentShaderId);
            //链接着色器程序
            GLES30.glLinkProgram(programId);
            final int[] linkStatus = new int[1];
            GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] == 0) {
    
    
                String logInfo = GLES30.glGetProgramInfoLog(programId);
                System.err.println(logInfo);
                GLES30.glDeleteProgram(programId);
                return 0;
            }
            return programId;
        } else {
    
    
            //创建失败
            return 0;
        }
    }
}

C++版本

  • .cpp文件
//
// Created by lvjunkai on 2022/2/21.
//
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <jni.h>
#include <malloc.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>

//声明自定义函数
//创建编译加载
GLuint LoadShader(int type, char *shaderCode);

//链接统一程序
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);

//模板技术定义的函数
//用于求数组长度
template<class T>
GLsizei getArrayLen(T &array) {
    return (sizeof(array) / sizeof(array[0]));
}

//坐标个数(xyz)
const GLint COORDS_PER_VERTEX = 3;

//顶点之间的偏移量
const int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

//声明顶点着色器
GLuint vertexShader;
//声明片元着色器
GLuint fragmentShader;
//声明着色器管理程序
GLuint mProgram;

//定义点坐标
float vertexPoints[] = {
        -0.5f, 0.5f, 0.0f, //左上角
        -0.5f, -0.5f, 0.0f, //左下角
        0.5f, -0.5f, 0.0f,  //右下角
        0.5f, 0.5f, 0.0f  //右上角
};

//设置颜色,依次为红绿蓝和透明通道
float color[] = {1.0f, 1.0f, 0.0f, 0.0f};

//索引
short index[] = {
        0, 1, 2, 0, 2, 3
};

const int vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;


//顶点着色器代码
char vertexShaderCode[] = "attribute vec4 vPosition; \n"
                          "uniform mat4 vMatrix;\n"
                          "void main() {\n"
                          "  gl_Position = vMatrix*vPosition;\n"
                          "  gl_PointSize = 10.0;  \n"
                          "}";


//片段着色器代码
char fragmentShaderCode[] = "precision mediump float;"
                            "uniform vec4 vColor;"
                            "void main() {"
                            "  gl_FragColor = vColor;"
                            "}";

//书写本地方法的具体逻辑
void JNICALL init(JNIEnv *env, jobject obj) {
    //初始化
}

/**初始化
 *
 * @param env
 * @param obj
*/
void JNICALL surfaceCreated(JNIEnv *env, jobject obj) {
    //设置背景颜色为黑色
    glClearColor(0, 0, 0, 0);
    //创建顶点着色器
    vertexShader = LoadShader(GL_VERTEX_SHADER, vertexShaderCode);
    //创建片元着色器
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentShaderCode);
    //将顶点着色器和片元着色器交给统一程序管理
    mProgram = linkProgram(vertexShader, fragmentShader);
}

/**图形尺寸
 *
 * @param env
 * @param obj
 * @param width
 * @param height
*/
glm::mat4 mProjectionMatrix;
glm::mat4 mViewMatrix;
glm::mat4 mMVPMatrix;

JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) {
    //设置视口
    glViewport(0, 0, width, height);
    //宽高比
    float ratio = (float) width / height;
    //二维图形设置正交投影矩阵即可
    mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f,
                                   100.0f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置
                              glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标
                              glm::vec3(0, 1, 0));
    //矩阵合并
    mMVPMatrix = mProjectionMatrix * mViewMatrix;
}


/**渲染绘制
 *
 * @param env
 * @param obj
*/
JNIEXPORT void JNICALL drawFrame(JNIEnv *env, jobject obj) {
    //清除颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    //使用程序
    glUseProgram(mProgram);
    //获取顶点着色器的vMatrix成员句柄
    GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix");
    //矩阵转换成数组
    float mMVPMatrixArray[16];
    int m = 0;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++){
            mMVPMatrixArray[m] = mMVPMatrix[i][j];
            m++;
        }
    //指定vMatrix的值
    glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray);
    //获取顶点着色器的vPosition成员句柄
    GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition");
    //启用图形顶点的句柄
    glEnableVertexAttribArray(mPositionHandle);
    //准备图形的坐标数据
    glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride, vertexPoints);
    //获取片元着色器的vColor成员的句柄
    GLint mColorHandle = glGetUniformLocation(mProgram, "vColor");
    //设置绘制图形的颜色
    glUniform4fv(mColorHandle, 1, color);
    //顶点法绘制图形
    glLineWidth(10.0f);
//    glDrawArrays(GL_LINE_LOOP,0,vertexCount);
    //索引法绘制图形
    glDrawElements(GL_TRIANGLES,getArrayLen(index),GL_UNSIGNED_SHORT,index);
    //禁止顶点数组的句柄
    glDisableVertexAttribArray(mPositionHandle);
}


/**加载着色器
 *
 */
JNIEXPORT 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);
                printf("SHADER ERROR!");
                free(infoLog);
            }
            //创建失败
            glDeleteShader(shader);
            return 0;
        }
        return shader;
    } else {
        //创建失败
        return 0;
    }
}

/**链接着色器
 *
 */
JNIEXPORT 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);
                printf("PROGRAM ERROR!");
                free(infoLog);
            }
            glDeleteProgram(program);
            return 0;
        }
        return program;
    } else {
        //创建失败
        return 0;
    }
}

/** 动态注册
* @param env
 * @return
*/
//本地方法声明和具体逻辑连接
JNINativeMethod methods[] = {
        {"init", "()V", (void *) init},
        {"surfaceCreated", "()V",   (void *) surfaceCreated},
        {"surfaceChanged", "(II)V", (void *) surfaceChanged},
        {"drawFrame",      "()V",   (void *) drawFrame}
};

/** 动态注册
* @param env
* @return
*/
jint registerNativeMethod(JNIEnv *env) {
    //到本地方法存在的类找出其方法声明
    jclass cl = env->FindClass("com/example/openglndk/WaveRenderer");
    if ((env->RegisterNatives(cl, methods, sizeof(methods) / sizeof(methods[0]))) < 0) {
        return -1;
    }
    return 0;
}

/** 加载默认回调
* @param vm
* @param reserved
* @return
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    //注册方法
    if (registerNativeMethod(env) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}

绘制纯色圆形

JAVA版本

  • Renderer类
package com.example.openglndk;

import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyRenderer implements GLSurfaceView.Renderer {
    
    
    /**
     * 坐标个数(xyz)
     */
    private final static int COORDS_PER_VERTEX = 3;
    private int n = 360; //切割份数
    private float radius = 0.3f; //半径

    //顶点之间的偏移量
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

    private FloatBuffer vertexBuffer;

    private int vertexShader;
    private int fragmentShader;
    private int mProgram;

    private int vertexCount;

    //顶点
    //定义正方形四点坐标
    private float vertexPoints[];
    private float height = 0.0f;

    //设置颜色,依次为红绿蓝和透明通道
    //粉色
    private float color[] = {
    
    1.0f, 0.5f, 0.7f, 0.0f};

    //创建顶点坐标
    private float[] createPositions() {
    
    
        ArrayList<Float> data = new ArrayList<>();
        //设置圆心坐标
        /*data.add(0.0f);
        data.add(0.0f);
        data.add(height);*/
        float angDegSpan = 360f / n;
        for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) {
    
    
            data.add((float) (radius * Math.sin(i * Math.PI / 180f)));
            data.add((float) (radius * Math.cos(i * Math.PI / 180f)));
            data.add(height);
        }
        float[] f = new float[data.size()];
        for (int i = 0; i < f.length; i++) {
    
    
            f[i] = data.get(i);
        }
        return f;
    }

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 vMatrix;" +
                    "void main() {" +
                    "  gl_Position = vMatrix*vPosition;" +
                    "  gl_PointSize = 10.0;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";


    public MyRenderer() {
    
    
        vertexPoints = createPositions();
        vertexCount = vertexPoints.length / COORDS_PER_VERTEX;
        //坐标
        vertexBuffer = ByteBuffer.allocateDirect(
                vertexPoints.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        vertexBuffer.put(vertexPoints);
        vertexBuffer.position(0);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        GLES30.glClearColor(1, 1, 1, 1);
        vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER,
                vertexShaderCode);
        fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER,
                fragmentShaderCode);
        mProgram = linkProgram(vertexShader, fragmentShader);
    }


    private float mProjectionMatrix[] = new float[16];
    private float mViewMatrix[] = new float[16];
    private float mMVPMatrix[] = new float[16];

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        GLES30.glViewport(0, 0, width, height);
        //宽高比
        float ratio = (float) width / height;
        //设置投影矩阵
        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        //设置相机角度
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //矩阵合并
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
    }

    //每20ms刷新
    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
        //将程序加入
        GLES30.glUseProgram(mProgram);
        //获取变换矩阵vMatrix成员句柄
        //图形参数
        int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
        //指定vMatrix的值
        GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
        //获取顶点着色器的vPosition成员句柄
        int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
        //启用顶点的句柄
        GLES30.glEnableVertexAttribArray(mPositionHandle);
        //准备坐标数据
        GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES30.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        //获取片元着色器的vColor成员的句柄
        int mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
        //设置绘制图形的颜色
        GLES30.glUniform4fv(mColorHandle, 1, color, 0);
        //顶点法绘制图形
        GLES30.glLineWidth(10.0f);
        GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, vertexCount);
        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(mPositionHandle);
    }

    /**
     * 编译
     *
     * @param type       顶点着色器:GLES30.GL_VERTEX_SHADER
     *                   片段着色器:GLES30.GL_FRAGMENT_SHADER
     * @param shaderCode
     * @return
     */
    private 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;
        }
    }

    /**
     * 链接小程序
     *
     * @param vertexShaderId   顶点着色器
     * @param fragmentShaderId 片段着色器
     * @return
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
    
    
        final int programId = GLES30.glCreateProgram();
        if (programId != 0) {
    
    
            //将顶点着色器加入到程序
            GLES30.glAttachShader(programId, vertexShaderId);
            //将片元着色器加入到程序中
            GLES30.glAttachShader(programId, fragmentShaderId);
            //链接着色器程序
            GLES30.glLinkProgram(programId);
            final int[] linkStatus = new int[1];
            GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] == 0) {
    
    
                String logInfo = GLES30.glGetProgramInfoLog(programId);
                System.err.println(logInfo);
                GLES30.glDeleteProgram(programId);
                return 0;
            }
            return programId;
        } else {
    
    
            //创建失败
            return 0;
        }
    }
}

C++版本

我弄了好久,一天多,从JAVA版本到C++版本。总结起来还是因为C++的基础不够牢固。C++中求数组长度没有函数,没有ArrayList数据结构,创建数组等都不太熟悉。然后就找不到错误在哪里,最后我一点一点从断点调试,才发现问题,没有拿到Vector中的数据,C++函数无法返回数组,只能返回指向数组的指针。也拿不到准确的建造数组的长度,Vector中取数据,按照数组格式,可能取不到数据,要按照迭代器进行遍历取数据。总之因为一堆原因,C++和JAVA的各种语法和数据结构的不同,导致创建不了点的坐标,拿不到那种float数据类型的数组,就没有点,绘制不了。最后我不在函数中拿到数组了,我直接返回Vector数据结构,然后在初始化中,使用迭代器遍历Vector,将其中数据直接放入到数组中,终于拿到了数据,完成了绘制。

  • .cpp文件
//
// Created by lvjunkai on 2022/2/21.
//
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <jni.h>
#include <malloc.h>
#include <math.h>
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>

//命名空间
using namespace std;

//常数Π
const double PI = acos(-1.0);

//声明自定义函数
//创建编译加载
GLuint LoadShader(int type, char *shaderCode);

//链接统一程序
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);

//模板技术定义的函数
//用于求数组长度
template<class T>
GLsizei getArrayLen(T &array) {
    return (sizeof(array) / sizeof(array[0]));
}


//坐标个数(xyz)
const GLint COORDS_PER_VERTEX = 3;

//顶点之间的偏移量
const GLsizei vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

//声明顶点着色器
GLuint vertexShader;
//声明片元着色器
GLuint fragmentShader;
//声明着色器管理程序
GLuint mProgram;

//定义正方形四点坐标
vector<float> vertexVector;  //存放顶点数据的数据结构
float vertexPoints[1083];  //顶点数组
float radius = 0.3f; //半径
int n = 360; //切割份数

//顶点个数
GLsizei vertexCount;

//设置颜色,依次为红绿蓝和透明通道
float color[] = {1.0f, 1.0f, 0.0f, 0.0f};


//顶点着色器代码
char vertexShaderCode[] = "attribute vec4 vPosition;"
                          "uniform mat4 vMatrix;"
                          "void main() {"
                          "  gl_Position = vMatrix*vPosition;"
                          "  gl_PointSize = 10.0;"
                          "}";

//片段着色器代码
char fragmentShaderCode[] = "precision mediump float;"
                            "uniform vec4 vColor;"
                            "void main() {"
                            "  gl_FragColor = vColor;"
                            "}";

//创建顶点坐标放入vector数据结构中
vector<float> createPositions() {
    vector<float> data;
    //圆上点
    float angDegSpan = 360.0f / n;
    for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) {
        data.push_back((float) (radius * sin(i * PI / 180.0f)));
        data.push_back((float) (radius * cos(i * PI / 180.0f)));
        data.push_back(0.0f);
    }
    return data;
}


JNIEXPORT void JNICALL init(JNIEnv *env, jobject obj) {
    //拿到顶点数据
    vertexVector = createPositions();
    //创建迭代器
    vector<float>::iterator t;
    int i = 0;
    //迭代器遍历vector中的数据
    //将其中数据放入数组中,形成顶点坐标
    for (t = vertexVector.begin(); t != vertexVector.end(); t++) {
        vertexPoints[i] = *t;
        i++;
    }
    vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;
}

//书写本地方法的具体逻辑
/**初始化
 *
 * @param env
 * @param obj
*/
JNIEXPORT void JNICALL surfaceCreated(JNIEnv *env, jobject obj) {
    //设置背景颜色为黑色
    glClearColor(0, 0, 0, 0);
    //创建顶点着色器
    vertexShader = LoadShader(GL_VERTEX_SHADER, vertexShaderCode);
    //创建片元着色器
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentShaderCode);
    //将顶点着色器和片元着色器交给统一程序管理
    mProgram = linkProgram(vertexShader, fragmentShader);
}

/**图形尺寸
 *
 * @param env
 * @param obj
 * @param width
 * @param height
*/
glm::mat4 mProjectionMatrix;
glm::mat4 mViewMatrix;
glm::mat4 mMVPMatrix;

JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) {
    //设置视口
    glViewport(0, 0, width, height);
    //宽高比
    float ratio = (float) width / height;
    //正交投影矩阵
    mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f,
                                   100.0f); //ratio 一般表示视口的宽高比,width/height
    //透视投影矩阵
//    glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置
                              glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标
                              glm::vec3(0, 1, 0));
    //矩阵合并
    mMVPMatrix = mProjectionMatrix * mViewMatrix;
}


/**渲染绘制
 *
 * @param env
 * @param obj
*/
JNIEXPORT void JNICALL drawFrame(JNIEnv *env, jobject obj) {
    //清除颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    //使用程序
    glUseProgram(mProgram);
    //获取变换矩阵vMatrix成员句柄
    GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix");
    //矩阵转换成数组
    float mMVPMatrixArray[16];
    int m = 0;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++){
            mMVPMatrixArray[m] = mMVPMatrix[i][j];
            m++;
        }
    //指定vMatrix的值
    glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray);
    //获取顶点着色器的vPosition成员句柄
    GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition");
    //启用图形顶点的句柄
    glEnableVertexAttribArray(mPositionHandle);
    //准备图形的坐标数据
    glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride,
                          vertexPoints);
    //获取片元着色器的vColor成员的句柄
    GLint mColorHandle = glGetUniformLocation(mProgram, "vColor");
    //设置绘制图形的颜色
    glUniform4fv(mColorHandle, 1, color);
    //绘制图形
    glLineWidth(10.0f);
    glDrawArrays(GL_LINE_LOOP, 0, vertexCount);
    //禁止顶点数组的句柄
    glDisableVertexAttribArray(mPositionHandle);
}


/**加载着色器
 *
 */
JNIEXPORT 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);
                printf("SHADER ERROR!");
                free(infoLog);
            }
            //创建失败
            glDeleteShader(shader);
            return 0;
        }
        return shader;
    } else {
        //创建失败
        return 0;
    }
}

/**链接着色器
 *
 */
JNIEXPORT 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);
                printf("PROGRAM ERROR!");
                free(infoLog);
            }
            glDeleteProgram(program);
            return 0;
        }
        return program;
    } else {
        //创建失败
        return 0;
    }
}

/** 动态注册
* @param env
 * @return
*/
//本地方法声明和具体逻辑连接
JNINativeMethod methods[] = {
        {"init",           "()V",   (void *) init},
        {"surfaceCreated", "()V",   (void *) surfaceCreated},
        {"surfaceChanged", "(II)V", (void *) surfaceChanged},
        {"drawFrame",      "()V",   (void *) drawFrame}
};

/** 动态注册
* @param env
* @return
*/
jint registerNativeMethod(JNIEnv *env) {
    //到本地方法存在的类找出其方法声明
    jclass cl = env->FindClass("com/example/openglndk/WaveRenderer");
    if ((env->RegisterNatives(cl, methods, sizeof(methods) / sizeof(methods[0]))) < 0) {
        return -1;
    }
    return 0;
}

/** 加载默认回调
* @param vm
* @param reserved
* @return
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    //注册方法
    if (registerNativeMethod(env) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}
  • Renderer类
package com.example.openglndk;

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

public class WaveRenderer implements GLSurfaceView.Renderer {
    
    
    //链接到库
    static{
    
    
        System.loadLibrary("native-wave");
    }

    //声明本地方法
    public native void init();

    public native void surfaceCreated();

    public native void surfaceChanged(int width, int height);

    public native void drawFrame();

    //构造函数
    public WaveRenderer(){
    
    
        init();
    }

    //接口方法实现
    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        surfaceCreated();
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        surfaceChanged(width,height);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        drawFrame();
    }
}

波纹扩散demo

  • .cpp文件
//
// Created by lvjunkai on 2022/2/21.
//
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <jni.h>
#include <malloc.h>
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>

//命名空间
using namespace std;

//常数Π
const double PI = acos(-1.0);

//声明自定义函数
//创建编译加载
GLuint LoadShader(int type, char *shaderCode);

//链接统一程序
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);

//模板技术定义的函数
//用于求数组长度
template<class T>
GLsizei getArrayLen(T &array) {
    return (sizeof(array) / sizeof(array[0]));
}


//坐标个数(xyz)
const GLint COORDS_PER_VERTEX = 3;

//顶点之间的偏移量
const GLsizei vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

//声明顶点着色器
GLuint vertexShader;
//声明片元着色器
GLuint fragmentShader;
//声明着色器管理程序
GLuint mProgram;

//定义正方形四点坐标
vector<float> vertexVector;  //存放顶点数据的数据结构
float vertexPoints[1083];  //顶点数组
//float radius; //半径
int n = 360; //切割份数

//顶点个数
GLsizei vertexCount;

//设置颜色,依次为红绿蓝和透明通道
float color[] = {1.0f, 1.0f, 0.0f, 0.0f};


//顶点着色器代码
char vertexShaderCode[] = "attribute vec4 vPosition;"
                          "uniform mat4 vMatrix;"
                          "void main() {"
                          "  gl_Position = vMatrix*vPosition;"
                          "  gl_PointSize = 10.0;"
                          "}";

//片段着色器代码
char fragmentShaderCode[] = "precision mediump float;"
                            "uniform vec4 vColor;"
                            "void main() {"
                            "  gl_FragColor = vColor;"
                            "}";

//创建顶点坐标放入vector数据结构中
vector<float> createPositions(float radius) {
    vector<float> data;
    //圆上点
    float angDegSpan = 360.0f / n;
    for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) {
        data.push_back((float) (radius * sin(i * PI / 180.0f)));
        data.push_back((float) (radius * cos(i * PI / 180.0f)));
        data.push_back(0.0f);
    }
    return data;
}


JNIEXPORT void JNICALL init(JNIEnv *env, jobject obj,float radius) {
    //拿到顶点数据
    vertexVector = createPositions(radius);
    //创建迭代器
    vector<float>::iterator t;
    int i = 0;
    //迭代器遍历vector中的数据
    //将其中数据放入数组中,形成顶点坐标
    for (t = vertexVector.begin(); t != vertexVector.end(); t++) {
        vertexPoints[i] = *t;
        i++;
    }
    vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;
}

//书写本地方法的具体逻辑
/**初始化
 *
 * @param env
 * @param obj
*/
JNIEXPORT void JNICALL surfaceCreated(JNIEnv *env, jobject obj) {
    //设置背景颜色为黑色
    glClearColor(0, 0, 0, 0);
    //创建顶点着色器
    vertexShader = LoadShader(GL_VERTEX_SHADER, vertexShaderCode);
    //创建片元着色器
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentShaderCode);
    //将顶点着色器和片元着色器交给统一程序管理
    mProgram = linkProgram(vertexShader, fragmentShader);
}

/**图形尺寸
 *
 * @param env
 * @param obj
 * @param width
 * @param height
*/
glm::mat4 mProjectionMatrix;
glm::mat4 mViewMatrix;
glm::mat4 mMVPMatrix;
float mMVPMatrixArray[16];

JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) {
    //设置视口
    glViewport(0, 0, width, height);
    //宽高比
    float ratio = (float) width / height;
    //正交投影矩阵
    mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f,
                                   100.0f); //ratio 一般表示视口的宽高比,width/height
    //透视投影矩阵
//    glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置
                              glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标
                              glm::vec3(0, 1, 0));
    //矩阵合并
    mMVPMatrix = mProjectionMatrix * mViewMatrix;
    //矩阵转换成数组
    int m = 0;
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++) {
            mMVPMatrixArray[m] = mMVPMatrix[i][j];
            m++;
        }
}


/**渲染绘制
 *
 * @param env
 * @param obj
*/
JNIEXPORT void JNICALL drawFrame(JNIEnv *env, jobject obj) {
    //清除颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    //使用程序
    glUseProgram(mProgram);
    //获取变换矩阵vMatrix成员句柄
    GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix");
    //指定vMatrix的值
    glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray);
    //获取顶点着色器的vPosition成员句柄
    GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition");
    //启用图形顶点的句柄
    glEnableVertexAttribArray(mPositionHandle);
    //准备图形的坐标数据
    glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride,
                          vertexPoints);
    //获取片元着色器的vColor成员的句柄
    GLint mColorHandle = glGetUniformLocation(mProgram, "vColor");
    //设置绘制图形的颜色
    glUniform4fv(mColorHandle, 1, color);
    //绘制图形
    glLineWidth(3.0f);
    glDrawArrays(GL_LINE_LOOP, 0, vertexCount);
    //禁止顶点数组的句柄
    glDisableVertexAttribArray(mPositionHandle);
}


/**加载着色器
 *
 */
JNIEXPORT 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);
                printf("SHADER ERROR!");
                free(infoLog);
            }
            //创建失败
            glDeleteShader(shader);
            return 0;
        }
        return shader;
    } else {
        //创建失败
        return 0;
    }
}

/**链接着色器
 *
 */
JNIEXPORT 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);
                printf("PROGRAM ERROR!");
                free(infoLog);
            }
            glDeleteProgram(program);
            return 0;
        }
        return program;
    } else {
        //创建失败
        return 0;
    }
}

/** 动态注册
* @param env
 * @return
*/
//本地方法声明和具体逻辑连接
JNINativeMethod methods[] = {
        {"init", "(F)V",   (void *) init},
        {"surfaceCreated", "()V",   (void *) surfaceCreated},
        {"surfaceChanged", "(II)V", (void *) surfaceChanged},
        {"drawFrame",      "()V",   (void *) drawFrame}
};

/** 动态注册
* @param env
* @return
*/
jint registerNativeMethod(JNIEnv *env) {
    //到本地方法存在的类找出其方法声明
    jclass cl = env->FindClass("com/example/openglndk/WaveRenderer");
    if ((env->RegisterNatives(cl, methods, sizeof(methods) / sizeof(methods[0]))) < 0) {
        return -1;
    }
    return 0;
}

/** 加载默认回调
* @param vm
* @param reserved
* @return
*/
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    //注册方法
    if (registerNativeMethod(env) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}
  • Renderer类
package com.example.openglndk;

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


public class WaveRenderer implements GLSurfaceView.Renderer {
    
    
    //链接到库文件
    static{
    
    
        System.loadLibrary("native-wave");
    }

    //声明本地方法
    public native void init(float radius);

    public native void surfaceCreated();

    public native void surfaceChanged(int width,int height);

    public native void drawFrame();

    //构造函数
    public WaveRenderer(float radius){
    
    
        init(radius);
    }

    //实现接口方法
    //用三个本地方法复写三个接口方法
    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
    
    
        surfaceCreated();
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
    
    
        surfaceChanged(width,height);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
    
    
        drawFrame();
    }
}
  • MainActivity——用于扩散操作,有bug
package com.example.openglndk;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

//JNI类
public class MainActivity extends AppCompatActivity {
    
    
    private GLSurfaceView mGLSurfaceView;
    private float radius = 0.1f;

    @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();
        //设置渲染器  C++版本
        @SuppressLint("HandlerLeak") Handler handler = new Handler(){
    
    
            @Override
            public void handleMessage(@NonNull Message msg) {
    
    
                switch (msg.what){
    
    
                    case 1:
                        GLSurfaceView.Renderer renderer = new WaveRenderer((Float) msg.obj);
                        mGLSurfaceView.setRenderer(renderer);
                 }
            }
        };

        Runnable runnable = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                Message message = new Message();
                message.what = 1;
                message.obj = radius;
                while(radius <= 0.5f){
    
    
                    handler.sendMessage(message);
                    radius += 0.1f;
                }
            }
        };
        handler.post(runnable);
    }
}

总结绘制整体流程

  1. 创建一个Activity作为窗口,布局只有一个GLSurfaceView控件用来显示绘制的内容。
  2. 新建一个Renderer渲染器(核心),将3D物体绘制到屏幕上。
    • 新建渲染器,实现GLSurfaceView.Renderer接口。
    • 声明Native方法,将其变成JNI类。
    • 用声明的Native方法复写接口的三个方法:onSurfaceCreated,onSurfaceChangedonDrawFrame方法。
    • 链接到声明方法具体实现库的文件名。
  3. 新建声明方法具体实现库。
    • 定义圆点坐标
    • 定义颜色数据
    • 书写着色器代码
    • 创建和编译着色器
    • 将着色器交给统一的程序管理
    • 设置视口
    • 清除颜色缓冲区
    • 绘制图形
  4. 在库编译文件CMakeLists.txt中,加入该库,使其可以进行编译执行。

猜你喜欢

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