[OpenGL ES] Two-dimensional graphics drawing


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

OpenGL ES Learning - 2D

Simple collocation of colors:

Not red + not green + not blue = black

red + green + blue = white

red + green = yellow

red + blue = purple

Green + blue = blue

Shader Language Basics

This blogger is 《OpenGL ES 3.0编程指南》writing according to the fifth chapter of the book.

Reference blog: Basic knowledge of OpenGL ES 3.0 shading language under the android platform (Part 1)

Reference blog: Basic knowledge of OpenGL ES 3.0 shading language under the android platform (Part 2)

Draw a solid color background

JAVA version

  • 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类(core)
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.txtand .cpp文件don't need to.

C++ version

  • 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.xmlconstant.
  • custom renderer ( JNIclass)
  • Renderer类(Change)
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——The follow-up remains unchanged.
# 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;
}

Draw dots, lines, triangles

  • vertex shader ( GLSLlanguage) (an imitation Clanguage)
attribute vec4 vPosition; 
uniform mat4 vMatrix;
void main() {
    
    
  gl_Position = vMatrix*vPosition;
  gl_PointSize = 10.0;  
}
  • Fragment shader
precision mediump float;
uniform vec4 vColor;
void main() {
    
    
  gl_FragColor = vColor;
}
  • polka dot
glDrawArrays(GL_POINTS,0,3);
  • straight line
glLineWidth(10.0f);
glDrawArrays(GL_LINE_STRIP,0,2);
  • triangle
glDrawArrays(GL_TRIANGLES,0,3);

The following examples all use triangles as an example. If you need to change the shape, you only need to modify the above code for drawing.

JAVA version

  • MainActivity——No change in the future.
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.xmlIt has remained unchanged and will not be repeated in the future.
  • core class -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++ version

  • Renderer类——No change in the future.
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文件(core)
//
// 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;
}

draw colored triangles

  • vertex shader
#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;
}
  • Fragment shader
#version 300 es
precision mediump float;
in vec4 aColor;
out vec4 fragColor;
void main() {
    
    
     fragColor = aColor;
}

JAVA version

  • 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++ version

  • .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;
}

Draw a solid color square

JAVA version

  • 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++ version

  • .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;
}

Draw a solid color circle

JAVA version

  • 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++ version

I have been working on it for a long time, more than a day, from the JAVA version to the C++ version. To sum up, it is still because the foundation of C++ is not strong enough. There is no function to find the length of an array in C++, no ArrayList data structure, and I am not familiar with creating an array. Then I couldn't find where the error was. Finally, I debugged from the breakpoint bit by bit, and found the problem. I didn't get the data in the Vector. The C++ function could not return the array, but could only return the pointer to the array. It is also impossible to get the exact length of the constructed array. To fetch data in Vector, according to the array format, the data may not be fetched, and it is necessary to traverse and fetch data according to the iterator. In short, due to a bunch of reasons, the various grammars and data structures of C++ and JAVA are different, so that the coordinates of the points cannot be created, and the array of the float data type cannot be obtained, so there will be no points and cannot be drawn. Finally, instead of getting the array in the function, I directly return the Vector data structure, and then use the iterator to traverse the Vector during initialization, put the data directly into the array, and finally get the data and complete the drawing.

  • .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();
    }
}

Ripple diffusion 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——for diffusion operations, there is a 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);
    }
}

Summarize and draw the overall process

  1. Create one Activityas a window, and the layout has only one GLSurfaceViewcontrol to display the drawn content.
  2. Create a new Rendererrenderer (core) to 3Ddraw objects to the screen.
    • Create a new renderer and implement GLSurfaceView.Rendererthe interface.
    • Declare Nativethe method, turning it into JNIa class.
    • NativeOverride the three methods of the interface with the declared method: onSurfaceCreated, onSurfaceChangedand onDrawFramemethod.
    • Link to the filename of the library that declares the method's concrete implementation.
  3. New declaration method concrete implementation library.
    • define dot coordinates
    • define color data
    • Write shader code
    • Create and compile shaders
    • Hand over shaders to unified program management
    • set viewport
    • clear color buffer
    • draw graphics
  4. In the library compilation file CMakeLists.txt, add the library so that it can be compiled and executed.

Guess you like

Origin blog.csdn.net/qq_46546793/article/details/123331388