【OpenGL ES】三维图形绘制


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

OpenGL ES 学习——3D

颜色的简单搭配:

不红+不绿+不蓝 = 黑

红+绿+蓝 = 白

红+绿 = 黄

红+蓝 = 紫

绿+蓝 = 青蓝

投影矩阵

投影主要分为正交投影透视投影两种。

正交投影

没有近大远小的效果,是平行投影,投影显示的大小就是物体的大小。

Matrix.orthoM方法

  • 这是在Java中设置正交投影的函数。
Matrix.orthoM(mProjectMatrix, 0, left, right, bottom, top, near, far);

在这里插入图片描述

glm::ortho方法

  • C++语言中,没有专门的数学库来处理矩阵,所以我们需要自己引入第三方库GLM
  • 在官网下载GLM库,然后解压,将glm文件夹加入到C++下面的include文件夹中,然后在文件中书写头文件即可使用。
template<typename T>
	GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top, T zNear, T zFar)
	{
		if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO)
			return orthoLH_ZO(left, right, bottom, top, zNear, zFar);
		else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO)
			return orthoLH_NO(left, right, bottom, top, zNear, zFar);
		else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO)
			return orthoRH_ZO(left, right, bottom, top, zNear, zFar);
		else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO)
			return orthoRH_NO(left, right, bottom, top, zNear, zFar);
	}
  • 还是和Java中方法相似,就是无偏移量参数,数据保存在矩阵中了,无需再一个内存来保存数据了。

透视投影

特点是近大远小,模拟人的眼球,近的物体显示大,远的物体显示小,发散光。

相机位置

相机位置,就是所谓的人所在位置,即观察者所在的位置。

Javac++中都有函数来设置相机位置,来观察物体。通过

改变相机位置,可以使我们观察到物体的每一个位置。

Matrix.setLookAtM方法

  • 这是在Java中设置相机位置的函数。
Matrix.setLookAtM(
    mVMatrix,0,  
    cx,cy,cz,   //相机位置
    tx,ty,tz,   //目标点坐标
    upx,upy,upz);  //视点位置,即眼睛位置
  • 第二个参数是偏移量,大多数情况都默认为0
  • 最后将这些数据都存入mVMatrix中。

glm::lookAt方法

template<typename T, qualifier Q>
	GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAt(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up)
	{
		GLM_IF_CONSTEXPR(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT)
			return lookAtLH(eye, center, up);
		else
			return lookAtRH(eye, center, up);
	}
  • 返回一个mat4的矩阵。
  • 参数就是三个vec3的向量:第一个就是相机位置,第二个就是目标点坐标,第三个是视点位置。

变换

我们熟知的变换方式有平移旋转缩放

平移

旋转

缩放

多个彩色三角形利用相机和矩阵形成立体效果

绘制立方体

对于三维图形来说,相机的朝向和角度是关键,看的视角很关键,不然你绘制是个立体图形,但是只能看到一个平面。

  • 顶点着色器
#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 java.nio.ShortBuffer;

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 ShortBuffer indexBuffer;
    //着色器和管理程序
    private int vertexShader;
    private int fragmentShader;
    private int mProgram;
    //彩色数据
    private float colors[] = {
    
    
            0f,1f,1f,1f,
            1f,1f,0f,1f,
            1f,0f,1f,1f,
            0f,1f,0f,1f,
            1f,0f,0f,1f,
            0f,0f,1f,1f,
            1f,1f,1f,1f,
            0f,0f,0f,1f,
    };

    //定义点坐标
    private float vertexPoints[] = {
    
    
            0.5f, 0.5f, 0.5f,  //右面左上0
            0.5f, 0.5f, -0.5f, //右面左下1
            -0.5f, 0.5f, 0.5f, //右面右上2
            -0.5f, 0.5f, -0.5f, //右面右下3

            0.5f, -0.5f, 0.5f,  //左面右上4
            0.5f, -0.5f, -0.5f, //左面右下5
            -0.5f, -0.5f, 0.5f, //左面左上6
            -0.5f, -0.5f, -0.5f, //左面左下7
    };

    //索引
    private short index[] = {
    
    
            //上
            0, 1, 2,
            1, 2, 3,
            //下
            4, 5, 6,
            5, 6, 7,
            //左
            2, 3, 6,
            3, 6, 7,
            //右
            0, 1, 4,
            1, 4, 5,
            //前
            0, 2, 4,
            2, 4, 6,
            //后
            1, 3, 7,
            1, 7, 5

    };

    //点数量
    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);
        //索引
        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);
    }

    /**
     * 图形尺寸
     *
     * @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, 3, 3, 3, 0f, 0f, 0f, 1f, 0f, 1.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.GL_DEPTH_BUFFER_BIT);
        //开启深度测试
        GLES30.glEnable(GLES30.GL_DEPTH_TEST);
        //使用统一的管理程序
        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_LINE_LOOP, 0, vertexCount);
        //索引法绘制图形,索引绘制三角形,无需在用扇形,直接绘制即可
        GLES30.glDrawElements(GLES30.GL_TRIANGLES, index.length,
                GLES30.GL_UNSIGNED_SHORT, indexBuffer);
        //禁止颜色数组的句柄
        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;

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 colors[] = {
        0.0f,1.0f,1.0f,1.0f,
        1.0f,1.0f,0.0f,1.0f,
        1.0f,0.0f,1.0f,1.0f,
        0.0f,1.0f,0.0f,1.0f,
        1.0f,0.0f,0.0f,1.0f,
        0.0f,0.0f,1.0f,1.0f,
        1.0f,1.0f,1.0f,1.0f,
        0.0f,0.0f,0.0f,1.0f,
};


//定义点坐标
float vertexPoints[] = {
        0.5f, 0.5f, 0.5f,  //右面左上0
        0.5f, 0.5f, -0.5f, //右面左下1
        -0.5f, 0.5f, 0.5f, //右面右上2
        -0.5f, 0.5f, -0.5f, //右面右下3

        0.5f, -0.5f, 0.5f,  //左面右上4
        0.5f, -0.5f, -0.5f, //左面右下5
        -0.5f, -0.5f, 0.5f, //左面左上6
        -0.5f, -0.5f, -0.5f, //左面左下7
};

//索引
short index[] = {
        //上
        0, 1, 2,
        1, 2, 3,
        //下
        4, 5, 6,
        5, 6, 7,
        //左
        2, 3, 6,
        3, 6, 7,
        //右
        0, 1, 4,
        1, 4, 5,
        //前
        0, 2, 4,
        2, 4, 6,
        //后
        1, 3, 7,
        1, 7, 5

};


//顶点个数
GLsizei 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"
                            "}";


JNIEXPORT void JNICALL init(JNIEnv *env, jobject obj) {

}

//书写本地方法的具体逻辑
/**初始化
 *
 * @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::frustum(-ratio, ratio, -1.0f, 1.0f, 3.0f,
                                   7.0f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(3, 3, 3), // 相机位置
                              glm::vec3(0, 0, 0), // 观察点坐标
                              glm::vec3(1, 0, 1));
    //矩阵合并
    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 | GL_DEPTH_BUFFER_BIT);
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    //使用统一的管理程序
    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, false,
            vertexStride, vertexPoints);
    //获取片元着色器的vColor成员的句柄
    //因为是彩色,数据类型和顶点类似,所以必须是glGetAttribLocation方法
    //glGetUniformLocation方法画不全颜色,会变成黑色
    GLint mColorHandle = glGetAttribLocation(mProgram, "vColor");
    //启动颜色的句柄
    glEnableVertexAttribArray(mColorHandle);
    //设置图形的颜色
    //准备颜色数据
    glVertexAttribPointer(mColorHandle, COORDS_PER_COLOR,
                                 GL_FLOAT, false,
            colorStride, colors);
    //绘制图形
    glLineWidth(10.0f);
    //索引法绘制图形,索引绘制三角形,无需在用扇形,直接绘制即可
    glDrawElements(GL_TRIANGLES, getArrayLen(index),
            GL_UNSIGNED_SHORT, index);
    //禁止颜色数组的句柄
    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.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 final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

    private float radius = 0.5f; //半径

    private FloatBuffer vertexBuffer;

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

    private int vertexCount;

    //定义点坐标
    private float vertexPoints[];

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

    //根据经纬度创建球体顶点坐标
    private float[] createPositions() {
    
    
        //球以(0,0,0)为中心,以R为半径,则球上任意一点的坐标为
        // 其中,a为圆心到点的线段与xz平面的夹角,b为圆心到点的线段在xz平面的投影与z轴的夹角
        ArrayList<Float> data = new ArrayList<>();
        float r1, r2;
        float h1, h2;
        float sin, cos;
        float step = 1.0f;
        // 遍历纬度
        for (float i = -90; i < 90 + step; i += step) {
    
    
            //Math.sin(x)  x 的正玄值。返回值在 -1.0 到 1.0 之间;X 都是指的“弧度”而非“角度”,弧度的计算公式为: 2*PI/360*角度
            //计算当前点与圆心连线,与Y轴夹角θ的正余弦值(此坐标系为openGL坐标系)
            /**
             * 以下坐标系为openGl坐标系
             * 此处计算当前纬度与Z轴夹角的正余弦值,由于根据公式,应该计算与Y轴的正余弦,但因为互余夹角sin与cos相等,所以按此计算没有问题
             * 后续的X的坐标就变成了x=R*cos*cos,所有关于θ的角的sin值都变换为cos计算
             */
            r1 = (float) (radius * Math.cos(i * Math.PI / 180.0));
            r2 = (float) (radius * Math.cos((i + step) * Math.PI / 180.0));
            h1 = (float) (radius * Math.sin(i * Math.PI / 180.0));
            h2 = (float) (radius * Math.sin((i + step) * Math.PI / 180.0));
            // 固定纬度, 360 度旋转遍历一条纬线
            float step2 = step * 2;
            for (float j = 0.0f; j < 360.0f + step; j += step2) {
    
    
                // 计算当前点在XZ平面投影,与Z轴的夹角φ的正余弦值(此坐标系为openGL坐标系)
                cos = (float) Math.cos(j * Math.PI / 180.0);
                sin = (float) Math.sin(j * Math.PI / 180.0);

                data.add(r2 * cos);
                data.add(h2);
                data.add(r2 * sin);
                data.add(r1 * cos);
                data.add(h1);
                data.add(r1 * sin);
            }
        }
        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++版本

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

//定义宏
//#define PI 3.1415926;
//常数Π
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[196566];  //顶点数组
float radius = 0.5f; //半径

//顶点个数
GLsizei vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;


//设置颜色,依次为红绿蓝和透明通道
float color[] = {0.1f, 0.9f, 1.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() {
    //球以(0,0,0)为中心,以R为半径,则球上任意一点的坐标为
    // 其中,a为圆心到点的线段与xz平面的夹角,b为圆心到点的线段在xz平面的投影与z轴的夹角
    vector<float> data;
    float r1, r2;
    float h1, h2;
    float sin1, cos1;
    float step = 1.0f;
    // 遍历纬度
    for (float i = -90; i < 90 + step; i += step) {
        //Math.sin(x)  x 的正玄值。返回值在 -1.0 到 1.0 之间;X 都是指的“弧度”而非“角度”,弧度的计算公式为: 2*PI/360*角度
        //计算当前点与圆心连线,与Y轴夹角θ的正余弦值(此坐标系为openGL坐标系)
        /**
         * 以下坐标系为openGl坐标系
         * 此处计算当前纬度与Z轴夹角的正余弦值,由于根据公式,应该计算与Y轴的正余弦,但因为互余夹角sin与cos相等,所以按此计算没有问题
         * 后续的X的坐标就变成了x=R*cos*cos,所有关于θ的角的sin值都变换为cos计算
         */
        r1 = (float) (radius * cos(i * PI / 180.0));
        r2 = (float) (radius * cos((i + step) * PI / 180.0));
        h1 = (float) (radius * sin(i * PI / 180.0));
        h2 = (float) (radius * sin((i + step) * PI / 180.0));
        // 固定纬度, 360 度旋转遍历一条纬线
        float step2 = step * 2;
        for (float j = 0.0f; j < 360.0f + step; j += step2) {
            // 计算当前点在XZ平面投影,与Z轴的夹角φ的正余弦值(此坐标系为openGL坐标系)
            cos1 = (float) cos(j * PI / 180.0);
            sin1 = (float) sin(j * PI / 180.0);

            data.push_back(r2 * cos1);
            data.push_back(h2);
            data.push_back(r2 * sin1);
            data.push_back(r1 * cos1);
            data.push_back(h1);
            data.push_back(r1 * sin1);
        }
    }
    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++;
    }
}

//书写本地方法的具体逻辑
/**初始化
 *
 * @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;
    //二维图形设置正交投影矩阵即可
    //frustum投影看不见图像,ortho投影才看的到球
    mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f,
                                     100.0f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), //  相机位置
                              glm::vec3(0, 0, 0), // 观察点坐标
                              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(0.5f);
    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;
}

绘制圆环

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 java.util.List;
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 vertexPoints[];
    //点的数量
    private int vertexCount;

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

    //创建所有点
    private float[] createPositions() {
    
    
        List<Float> coords = new ArrayList<Float>();
        float Rinner = 0.2f;//内圆半径
        float Rring = 0.3f;//环半径
        int count = 20;//循环次数
        float alpha = 0;//外圆角度
        float x0, x1, y0, y1, z0, z1;
        float alphaStep = (float) (2 * Math.PI / count);//alpha角的步长(外部圆环)
        float betaStep = (float) (2 * Math.PI / count);//beta角的步长(每个切片)
        float beta = 0;//切片循环角度
        int count0 = 20;//循环次数
        /***************************外层循环主要负责把圆环切成几个部分*******************************/
        for (int i = 0; i < count; i++) {
    
    
            alpha = i * alphaStep;
            /***************************内层循环主要负责把每个部分画出来*******************************/

            for (int j = 0; j <= count0; j++) {
    
    
                beta = j * betaStep;
                x0 = (float) (Math.cos(alpha) * (Rring + Rinner + Rinner * Math.cos(beta)));
                y0 = (float) (Math.sin(alpha) * (Rring + Rinner + Rinner * Math.cos(beta)));
                z0 = (float) -(Rring * Math.sin(beta));

                x1 = (float) (Math.cos(alpha + alphaStep) * (Rring + Rinner + Rinner * Math.cos(beta)));
                y1 = (float) (Math.sin(alpha + alphaStep) * (Rring + Rinner + Rinner * Math.cos(beta)));
                z1 = (float) (-Rring * Math.sin(beta));

                coords.add(x0);
                coords.add(y0);
                coords.add(z0);
                coords.add(x1);
                coords.add(y1);
                coords.add(z1);
            }
        }
        float[] f = new float[coords.size()];    //所有的顶点
        for (int i = 0; i < f.length; i++) {
    
    
            f[i] = coords.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, 5, 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.glDrawArrays(GLES30.GL_LINE_STRIP, 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++版本

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

//定义宏
//#define PI 3.1415926;
//常数Π
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[196566];  //顶点数组

//顶点个数
GLsizei vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;


//设置颜色,依次为红绿蓝和透明通道
float color[] = {0.1f, 0.9f, 1.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> coords;
    float Rinner = 0.2f;//环半径
    float Rring = 0.2f;//内圆半径
    int count = 20;//循环次数
    float alpha = 0;//外圆角度
    float x0, x1, y0, y1, z0, z1;
    float alphaStep = (float) (2 * PI / count);//alpha角的步长(外部圆环)
    float betaStep = (float) (2 * PI / count);//beta角的步长(每个切片)
    float beta = 0;//切片循环角度
    int count0 = 20;//循环次数
    /***************************外层循环主要负责把圆环切成几个部分*******************************/
    for (int i = 0; i < count; i++) {
        alpha = i * alphaStep;
        /***************************内层循环主要负责把每个部分画出来*******************************/

        for (int j = 0; j <= count0; j++) {
            beta = j * betaStep;
            x0 = (float) (cos(alpha) * (Rring + Rinner + Rinner * cos(beta)));
            y0 = (float) (sin(alpha) * (Rring + Rinner + Rinner * cos(beta)));
            z0 = (float) -(Rring * sin(beta));

            x1 = (float) (cos(alpha + alphaStep) * (Rring + Rinner + Rinner * cos(beta)));
            y1 = (float) (sin(alpha + alphaStep) * (Rring + Rinner + Rinner * cos(beta)));
            z1 = (float) (-Rring * sin(beta));

            coords.push_back(x0);
            coords.push_back(y0);
            coords.push_back(z0);
            coords.push_back(x1);
            coords.push_back(y1);
            coords.push_back(z1);
        }
    }
    return coords;
}


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++;
    }
}

//书写本地方法的具体逻辑
/**初始化
 *
 * @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;
    //二维图形设置正交投影矩阵即可
    //frustum投影看不见图像,ortho投影才看的到球
    mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f,
                                     100.0f); //ratio 一般表示视口的宽高比,width/height
    //相机位置
    mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), //  相机位置
                              glm::vec3(0, 0, 0), // 观察点坐标
                              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);
    //绘制图形
    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;
}

绘制圆柱体

圆柱和圆锥比,只修改了点的数量和坐标,其他都没变。

//创建圆柱的所有点
    private float[] createPositions() {
    
    
        ArrayList<Float> data = new ArrayList<>();
        //两个圆上点
        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(0.0f);
            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;
    }

绘制圆锥体

根据我近几日所学,查找资料,找不到为什么我的总是一个圆,而不是圆锥。然后我回复了一下所学东西,回想每一个函数意义,回想学过的立体几何的数学知识,最后发现,是相机的朝向角度问题,因为是从Z轴往下看的,所以就相当于从圆锥顶部看下去,就是一个圆了。改变相机角度,让其从y轴,即侧面看去,就可以看出其圆锥的近似模样。点是没有错的,就一个锥点和底部圆面点。

C++版本

  • 还差相机设置不会,投影也不会。

  • .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[1086];  //顶点数组
float radius = 0.5f; //半径
float height = 1.0f; //高度
int n = 360; //切割份数

//顶点个数
GLsizei vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX;

//设置颜色,依次为红绿蓝和透明通道
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;
    //锥点
    data.push_back(0.0f);
    data.push_back(0.0f);
    data.push_back(height);
    //圆上点
    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++;
    }
}

//书写本地方法的具体逻辑
/**初始化
 *
 * @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(1, 1, 0.7), // 相机位置
                              glm::vec3(0, 0, 0), // 观察点坐标
                              glm::vec3(0, 0, 1));
    //矩阵合并
    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", "()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 final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节

    private float radius = 0.5f; //半径
    private float height = 1.0f; //圆锥高度
    private int n = 360; //切割份数

    private FloatBuffer vertexBuffer;

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

    //声明点坐标
    private float vertexPoints[];
    //点的数量
    private int vertexCount;

    //设置颜色,依次为红绿蓝和透明通道
    //黑色
    private float color[] = {
    
    0.0f, 0.0f, 0.0f, 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(0.0f);
        }
        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;" +
                    "}";

    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, 1, -3, -1, 0f, 0f, 0f, 0f, 0.0f, 1.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.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 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;
        }
    }
}

猜你喜欢

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