【OpenGL ES】三维图形绘制

这篇具有很好参考价值的文章主要介绍了【OpenGL ES】三维图形绘制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

OpenGL ES 学习——3D

颜色的简单搭配:

不红+不绿+不蓝 = 黑

红+绿+蓝 = 白

红+绿 = 黄

红+蓝 = 紫

绿+蓝 = 青蓝

投影矩阵

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

正交投影

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

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

【OpenGL ES】三维图形绘制

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文件文章来源地址https://www.toymoban.com/news/detail-405204.html

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

到了这里,关于【OpenGL ES】三维图形绘制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 积跬步至千里 || PyTorch 中 shape 和 size 属性的区别

    在深度学习中,张量 Tensor 和 向量 array 有相似之处,不同的在于 Tensor 兼具了求导的属性。张量和向量一样,可以表示多维矩阵,也同样都有 shape 和 size 属性。 PyTorch 中 shape 可以计算出各个维数的个数,即表示多维矩阵的形状; PyTorch 中 size() 可通过参数计算出特定维度上的

    2024年02月12日
    浏览(46)
  • 积跬步至千里 || PyTorch 中的“with torch no_grad” 语句

    “with ”torch.no_grad()的使用就像一个循环,其中循环内的每个张量都将requires_grad设置为False。这意味着当前与当前计算图相连的任何具有梯度的张量现在都与当前图分离。我们不再能够计算关于这个张量的梯度。 张量从当前图中分离,直到它在循环内。一旦它离开循环,如果

    2024年02月11日
    浏览(41)
  • MATLAB 之 绘制三维图形的基本函数、三维曲面和其他三维图形

    三维图形具有更强的数据表现能力,为此 MATLAB 提供了丰富的函数来绘制三维图形。绘制三维图形与绘制二维图形的方法十分类似,很多都是在二维绘图的基础上扩展而来。 基本的三维图形函数为 plot3 ,它是将二维绘图函数 plot 的有关功能扩展到三维空间,用来绘制三维曲线

    2024年02月06日
    浏览(41)
  • 计算机图形学实验四 OpenGL的鼠标交互绘制

    一、实验目的 1.掌握OpenGL的鼠标按钮响应函数。 2.掌握OpenGL的鼠标移动响应函数。 3.进一步巩固OpenGL的基本图元绘制基础。 二、实验内容 1.鼠标画草图——实现鼠标点到哪,线就画到哪。 思路: 1)在主程序注册鼠标响应和鼠标移动子函数: glutMouseFunc(mymouse); glutMotionFunc(mym

    2023年04月20日
    浏览(113)
  • 【小沐学GIS】基于C#绘制三维数字地球Earth(OpenGL)

    🍺三维数字地球系列相关文章如下🍺: 1 【小沐学GIS】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)第一期 2 【小沐学GIS】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)第二期 3 【小沐学GIS】基于OpenSceneGraph(OSG)绘制三维数字地球Earth 4 【小沐学GIS】基于C++绘制太阳系

    2024年01月22日
    浏览(37)
  • 【GIS开发】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)

    🍺三维数字地球系列相关文章如下🍺: 1 【小沐学GIS】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)第一期 2 【小沐学GIS】基于C++绘制三维数字地球Earth(OpenGL、glfw、glut)第二期 3 【小沐学GIS】基于OpenSceneGraph(OSG)绘制三维数字地球Earth 4 【小沐学GIS】基于C++绘制太阳系

    2023年04月17日
    浏览(63)
  • OpenGL ES 绘制一张图片

    GLSL 的修饰符与数据类型 GLSL 中变量的修饰符 const:修饰不可被外界改变的常量 attribute:修饰经常更改的变量,只可以在顶点着色器中使用 uniform:修饰不经常更改的变量,可用于顶点着色器和片段着色器 varying:修饰在顶点着色器计算,然后传递到片元着色器中使用的变量

    2024年02月06日
    浏览(39)
  • c++中用opengl的gl函数在三维空间中绘制圆形和画球体

    绘制圆形原理: 画圆形的原理如下图,画一个圆形就相当于切蛋糕一样,将一个圆形切成很多个扇形,而扇形可以用三角形类似表示,所以我们可以将绘制圆形转化为绘制许多个三角形。 绘制圆形的代码如下: 绘制球体原理: 数学中的简单原理,一个球体相当于一个圆形绕

    2024年02月11日
    浏览(38)
  • OpenGL ES入门教程(二)之绘制一个平面桌子

    上一篇文章OpenGL ES入门教程(一)编写第一个OpenGL程序,我们创建了自己的第一个OpenGL程序,实现了绘制红色背景的Activity页面,算是OpenGL ES的hello world程序吧。本篇文章基于上一篇文章基础上讲解如何使用OpenGL绘制一张平面桌子,桌子由一个长方形构成,且长方形中间绘制一

    2024年01月17日
    浏览(35)
  • LearnOpenGL - Android OpenGL ES 3.0 绘制三角形

    LearnOpenGL 笔记 - 入门 01 OpenGL LearnOpenGL 笔记 - 入门 02 创建窗口 LearnOpenGL 笔记 - 入门 03 你好,窗口 LearnOpenGL 笔记 - 入门 04 你好,三角形 OpenGL - 如何理解 VAO 与 VBO 之间的关系 经过一段时间 OpenGL 的学习,我们已经掌握了如何使用 glwf 在桌面端绘制简单图形。现在让我们把目光

    2024年02月12日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包