OpenGL ES 3.0 学习——2D
着色器语言基础知识绘制纯色背景
JAVA版本C++版本 绘制圆点、直线、三角形
JAVA版本C++版本 绘制彩色三角形
JAVA版本C++版本 绘制纯色正方形
JAVA版本C++版本 绘制纯色圆形
JAVA版本C++版本
波纹扩散demo总结绘制整体流程
不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!
颜色的简单搭配:
不红+不绿+不蓝 = 黑
红+绿+蓝 = 白
红+绿 = 黄
红+蓝 = 紫
绿+蓝 = 青蓝
着色器语言基础知识这位博主是根据书本《OpenGL ES 3.0编程指南》第五章进行书写的。
参考博客:android平台下OpenGL ES 3.0着色语言基础知识(上)
参考博客:android平台下OpenGL ES 3.0着色语言基础知识(下)
绘制纯色背景 JAVA版本MainActivity
package com.example.openglndk;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
//JNI类
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
setContentView(mGLSurfaceView);
//设置版本ES 3.0
mGLSurfaceView.setEGLContextClientVersion(3);
//设置渲染器
GLSurfaceView.Renderer renderer = new MyRenderer(Color.RED);
mGLSurfaceView.setRenderer(renderer);
}
}
activity_main.xml
Renderer类(核心)
package com.example.openglndk;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyRenderer implements GLSurfaceView.Renderer {
//声明成员变量
private int color;
//构造方法初始化
public MyRenderer(int color){
this.color = color;
}
//实现接口方法
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
//分离RGBA的百分比
float redF = ((color >> 16) & 0xFF) * 1.0f / 255;
float greenF = ((color >> 8) & 0xFF) * 1.0f / 255;
float blueF = (color & 0xFF) * 1.0f / 255;
float alphaF = ((color >> 24) & 0xFF) * 1.0f / 255;
GLES30.glClearColor(redF, greenF, blueF, alphaF);
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
//设置视口
GLES30.glViewport(0, 0, width, height);
}
@Override
public void onDrawframe(GL10 gl10) {
//把颜色缓冲区设置为我们预设的颜色
//清楚颜色缓存
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
}
}
CMakeLists.txt和.cpp文件不需要。 C++版本
MainActivity
package com.example.openglndk;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
//JNI类
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
setContentView(mGLSurfaceView);
//设置版本ES 3.0
mGLSurfaceView.setEGLContextClientVersion(3);
//设置渲染器
// GLSurfaceView.Renderer renderer = new MyRenderer(Color.RED);
GLSurfaceView.Renderer renderer = new WaveRenderer(Color.GREEN);
mGLSurfaceView.setRenderer(renderer);
}
}
activity_main.xml不变。自定义渲染器(JNI类)Renderer类(改变)
package com.example.openglndk;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class WaveRenderer implements GLSurfaceView.Renderer {
//链接到库文件
static{
System.loadLibrary("native-wave");
}
//声明本地方法
public native void init();
public native void surfaceCreated(int color);
public native void surfaceChanged(int width,int height);
public native void drawframe();
//声明成员变量
private int color;
//构造方法初始化
public WaveRenderer(int color){
this.color = color;
}
//实现接口方法
//用三个本地方法复写三个接口方法
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
surfaceCreated(color);
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
surfaceChanged(width,height);
}
@Override
public void onDrawframe(GL10 gl10) {
drawframe();
}
}
CMakeLists.txt——后续不变。
# Sets the minimum version of CMake required to build the native library.
#cmake_minimum_required(VERSION 3.10.2)
cmake_minimum_required(VERSION 3.4.1)
##官方标准配置
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -Wall")
##ANDROID_PLATFORM_LEVEL=18
add_definitions("-DDYNAMIC_ES3")
set(OPENGL_LIB GLESv3)
# Declares and names the project.
project("openglndk")
add_library( # Sets the name of the library.
native-wave
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-wave.cpp)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
target_link_libraries(native-wave
${OPENGL_LIB}
android
EGL
log
m)
.cpp文件
// // Created by lvjunkai on 2022/2/16. // #include绘制圆点、直线、三角形#include #include //书写本地方法的具体逻辑 JNIEXPORT void JNICALL init(JNIEnv *env, jobject obj) { } JNIEXPORT void JNICALL surfaceCreated(JNIEnv *env, jobject obj, jint color) { //分离RGBA的百分比 GLfloat redF = ((color >> 16) & 0xFF) * 1.0f / 255; GLfloat greenF = ((color >> 8) & 0xFF) * 1.0f / 255; GLfloat blueF = (color & 0xFF) * 1.0f / 255; GLfloat alphaF = ((color >> 24) & 0xFF) * 1.0f / 255; glClearColor(redF, greenF, blueF, alphaF); } JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) { //设置视口 glViewport(0, 0, width, height); } JNIEXPORT void JNICALL drawframe(JNIEnv *env, jobject obj) { //把颜色缓冲区设置为我们预设的颜色 //清楚颜色缓存 glClear(GL_COLOR_BUFFER_BIT); } //本地方法声明和具体逻辑连接 JNINativeMethod methods[] = { //双引号中的方法是Renderer类中声明的本地方法名 //后面的方法是cpp文件中具体实现方法名 {"init", "()V", (void *) init}, {"surfaceCreated", "(I)V", (void *) surfaceCreated}, {"surfaceChanged", "(II)V", (void *) surfaceChanged}, {"drawframe", "()V", (void *) drawframe} }; 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; } jint JNI_onLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1; } //注册方法 if (registerNativeMethod(env) != JNI_OK) { return -1; } return JNI_VERSION_1_6; }
顶点着色器(GLSL语言)(一种仿C的语言)
attribute vec4 vPosition;
uniform mat4 vMatrix;
void main() {
gl_Position = vMatrix*vPosition;
gl_PointSize = 10.0;
}
片元着色器
precision mediump float;
uniform vec4 vColor;
void main() {
gl_FragColor = vColor;
}
圆点
glDrawArrays(GL_POINTS,0,3);
直线
glLineWidth(10.0f); glDrawArrays(GL_LINE_STRIP,0,2);
三角形
glDrawArrays(GL_TRIANGLES,0,3);
以下示例皆以三角形为例子,需要变换形状,只需修改绘制上述代码。
JAVA版本MainActivity——后续不改变。
package com.example.openglndk;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
//JNI类
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
setContentView(mGLSurfaceView);
//设置版本ES 3.0
mGLSurfaceView.setEGLContextClientVersion(3);
//设置渲染器 JAVA版本
GLSurfaceView.Renderer renderer = new MyRenderer();
//设置渲染器 C++版本
// GLSurfaceView.Renderer renderer = new WaveRenderer();
mGLSurfaceView.setRenderer(renderer);
}
}
activity_main.xml一直不变,后续不再赘述。核心类——Renderer类
package com.example.openglndk;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyRenderer implements GLSurfaceView.Renderer {
//坐标系xyz
private final static int COORDS_PER_VERTEX = 3;
//顶点之间的偏移量
private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
//顶点内存
private FloatBuffer vertexBuffer;
//着色器和管理程序
private int vertexShader;
private int fragmentShader;
private int mProgram;
//设置颜色,依次为红绿蓝和透明通道
private float color[] = {1.0f, 0.0f, 1.0f, 0.0f};
//定义点坐标
private float vertexPoints[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
//点数量
private int vertexCount = vertexPoints.length / COORDS_PER_VERTEX;
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"void main() {" +
" gl_Position = vMatrix*vPosition;" +
" gl_PointSize = 10.0;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
public MyRenderer() {
//坐标内存
//分配内存空间,每个浮点型占4字节空间
//ByteBuffer相当于使用gpu的桥梁
//gpu内存排列顺序,使用默认
//转化为管道
vertexBuffer = ByteBuffer.allocateDirect(
vertexPoints.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//传入指定的坐标数据
//将语法推送给GPU
vertexBuffer.put(vertexPoints);
vertexBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
//设置背景为白色
GLES30.glClearColor(1, 1, 1, 1);
vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER,
vertexShaderCode);
fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = linkProgram(vertexShader, fragmentShader);
}
private float mProjectionMatrix[] = new float[16];
private float mViewMatrix[] = new float[16];
private float mMVPMatrix[] = new float[16];
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES30.glViewport(0, 0, width, height);
//宽高比
float ratio = (float) width / height;
//正交投影矩阵
Matrix.orthoM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 0, 5);
//设置透视投影矩阵
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
//设置相机角度(核心)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//矩阵合并
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
}
//每20ms刷新
@Override
public void onDrawframe(GL10 gl10) {
//清除颜色缓冲区
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//将程序加入
GLES30.glUseProgram(mProgram);
//获取变换矩阵vMatrix成员句柄
//图形参数
int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
//指定vMatrix的值
GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
//获取顶点着色器的vPosition成员句柄
int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
//启用顶点的句柄
GLES30.glEnableVertexAttribArray(mPositionHandle);
//准备坐标数据
GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES30.GL_FLOAT, false,
vertexStride, vertexBuffer);
//获取片元着色器的vColor成员的句柄
int mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
//设置图形的颜色
GLES30.glUniform4fv(mColorHandle, 1, color, 0);
//绘制图形
GLES30.glLineWidth(10.0f);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(mPositionHandle);
}
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;
}
}
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
if (programId != 0) {
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序中
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.gllinkProgram(programId);
final int[] linkStatus = new int[1];
GLES30.glGetProgramiv(programId, GLES30.GL_link_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
String logInfo = GLES30.glGetProgramInfoLog(programId);
System.err.println(logInfo);
GLES30.glDeleteProgram(programId);
return 0;
}
return programId;
} else {
//创建失败
return 0;
}
}
}
C++版本
Renderer类——后续不改变。
package com.example.openglndk;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class WaveRenderer implements GLSurfaceView.Renderer {
//链接到库文件
static{
System.loadLibrary("native-wave");
}
//声明本地方法
public native void init();
public native void surfaceCreated();
public native void surfaceChanged(int width,int height);
public native void drawframe();
//构造函数
public WaveRenderer(){
init();
}
//实现接口方法
//用三个本地方法复写三个接口方法
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
surfaceCreated();
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
surfaceChanged(width,height);
}
@Override
public void onDrawframe(GL10 gl10) {
drawframe();
}
}
.cpp文件(核心)
// // Created by lvjunkai on 2022/2/21. // #include绘制彩色三角形#include #include #include #include #include //声明自定义函数 //创建编译加载 GLuint LoadShader(int type, char *shaderCode); //链接统一程序 GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader); //模板技术定义的函数 //用于求数组长度 template GLsizei getArrayLen(T &array) { return (sizeof(array) / sizeof(array[0])); } //坐标个数(xyz) const GLint COORDS_PER_VERTEX = 3; //顶点之间的偏移量 const int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节 //声明顶点着色器 GLuint vertexShader; //声明片元着色器 GLuint fragmentShader; //声明着色器管理程序 GLuint mProgram; //定义点坐标 float vertexPoints[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f }; //设置颜色,依次为红绿蓝和透明通道 float color[] = {1.0f, 1.0f, 0.0f, 0.0f}; const int vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX; //顶点着色器代码 char vertexShaderCode[] = "attribute vec4 vPosition; n" "uniform mat4 vMatrix;n" "void main() {n" " gl_Position = vMatrix*vPosition;n" " gl_PointSize = 10.0; n" "}"; //片段着色器代码 char fragmentShaderCode[] = "precision mediump float;" "uniform vec4 vColor;" "void main() {" " gl_FragColor = vColor;" "}"; //书写本地方法的具体逻辑 void JNICALL init(JNIEnv *env, jobject obj) { //初始化 } 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); } glm::mat4 mProjectionMatrix; glm::mat4 mViewMatrix; glm::mat4 mMVPMatrix; JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) { //设置视口 glViewport(0, 0, width, height); //宽高比 float ratio = (float) width / height; //二维图形设置正交投影矩阵即可 mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f); //ratio 一般表示视口的宽高比,width/height //相机位置 mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置 glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标 glm::vec3(0, 1, 0)); //矩阵合并 mMVPMatrix = mProjectionMatrix * mViewMatrix; } JNIEXPORT void JNICALL drawframe(JNIEnv *env, jobject obj) { //清除颜色缓冲区 glClear(GL_COLOR_BUFFER_BIT); //使用程序 glUseProgram(mProgram); GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix"); //矩阵转换成数组 float mMVPMatrixArray[16]; int m = 0; for(int i=0;i<4;i++) for(int j=0;j<4;j++){ mMVPMatrixArray[m] = mMVPMatrix[i][j]; m++; } //指定vMatrix的值 glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray); //获取顶点着色器的vPosition成员句柄 GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition"); //启用图形顶点的句柄 glEnableVertexAttribArray(mPositionHandle); //准备图形的坐标数据 glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride, vertexPoints); //获取片元着色器的vColor成员的句柄 GLint mColorHandle = glGetUniformLocation(mProgram, "vColor"); //设置绘制图形的颜色 glUniform4fv(mColorHandle, 1, color); //顶点法绘制图形 glLineWidth(10.0f); glDrawArrays(GL_TRIANGLES,0,vertexCount); //禁止顶点数组的句柄 glDisableVertexAttribArray(mPositionHandle); } JNIEXPORT GLuint LoadShader(int type, char *shaderCode) { //创建一个着色器 GLuint shader = glCreateShader(type); if (shader != 0) { //加载到着色器 glShaderSource(shader, 1, &shaderCode, NULL); //编译着色器 glCompileShader(shader); //检测状态 GLint compileStatus; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); if (compileStatus == 0) { //声明log长度变量 GLint infoLen = 0; //获取长度并赋值 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { //创建成功小于等于1 char *infoLog = static_cast (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 (malloc(sizeof(char) * infoLen)); glGetProgramInfoLog(program, infoLen, NULL, infoLog); printf("PROGRAM ERROR!"); free(infoLog); } glDeleteProgram(program); return 0; } return program; } else { //创建失败 return 0; } } //本地方法声明和具体逻辑连接 JNINativeMethod methods[] = { {"init", "()V", (void *) init}, {"surfaceCreated", "()V", (void *) surfaceCreated}, {"surfaceChanged", "(II)V", (void *) surfaceChanged}, {"drawframe", "()V", (void *) drawframe} }; 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; } jint JNI_onLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1; } //注册方法 if (registerNativeMethod(env) != JNI_OK) { return -1; } return JNI_VERSION_1_6; }
顶点着色器
#version 300 es
in vec4 vPosition;
in vec4 vColor;
uniform mat4 vMatrix;
out vec4 aColor;
void main() {
gl_Position = vMatrix*vPosition;
gl_PointSize = 10.0;
aColor = vColor;
}
片元着色器
#version 300 es
precision mediump float;
in vec4 aColor;
out vec4 fragColor;
void main() {
fragColor = aColor;
}
JAVA版本
Renderer类
package com.example.openglndk;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
//渲染器,把3D物体绘制到屏幕上
public class MyRenderer implements GLSurfaceView.Renderer {
//坐标系xyz
private final static int COORDS_PER_VERTEX = 3;
//颜色通道(RGBA)
private final static int COORDS_PER_COLOR = 4;
//顶点之间的偏移量
private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
//颜色之间的偏移量
private final int colorStride = COORDS_PER_COLOR * 4; // 每个颜色四个字节
//声明内存分配的数据类型
private FloatBuffer vertexBuffer, colorBuffer;
//着色器和管理程序
private int vertexShader;
private int fragmentShader;
private int mProgram;
//彩色数据
private float colors[] = {
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f
};
//定义点坐标
private float[] vertexPoints = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
//点数量
private int vertexCount = vertexPoints.length / COORDS_PER_VERTEX;
//顶点着色器代码
private String vertexShaderCode = "#version 300 esn" +
"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 esn" +
"precision mediump float;n" +
"in vec4 aColor;n" +
"out vec4 fragColor;n" +
"void main() {n" +
" fragColor = aColor;n" +
"}n";
//构造函数
public MyRenderer() {
//分配顶点内存
vertexBuffer = ByteBuffer.allocateDirect(vertexPoints.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//传入指定的坐标数据
//将语法推送给GPU
vertexBuffer.put(vertexPoints);
vertexBuffer.position(0);
//分配颜色内存
colorBuffer = ByteBuffer.allocateDirect(colors.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//传入指定的数据
colorBuffer.put(colors);
colorBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
//设置背景颜色为白色
GLES30.glClearColor(1, 1, 1, 1);
//创建顶点着色器
vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode);
//创建片元着色器
fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode);
//将顶点着色器和片元着色器交给统一程序管理
mProgram = linkProgram(vertexShader, fragmentShader);
}
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);
}
@Override
public void onDrawframe(GL10 gl10) {
//清除颜色缓冲区
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//使用统一的管理程序
GLES30.glUseProgram(mProgram);
//获取变换矩阵vMatrix成员句柄
//图形参数
int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
//指定vMatrix的值
GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
//获取顶点着色器的vPosition成员句柄
int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
//启用顶点的句柄
GLES30.glEnableVertexAttribArray(mPositionHandle);
//准备坐标数据
GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES30.GL_FLOAT, false,
vertexStride, vertexBuffer);
//获取片元着色器的vColor成员的句柄
//因为是彩色,数据类型和顶点类似,所以必须是glGetAttribLocation方法
//glGetUniformLocation方法画不全颜色,会变成黑色
int mColorHandle = GLES30.glGetAttribLocation(mProgram, "vColor");
//启动颜色的句柄
GLES30.glEnableVertexAttribArray(mColorHandle);
//设置图形的颜色
//准备颜色数据
GLES30.glVertexAttribPointer(mColorHandle, COORDS_PER_COLOR,
GLES30.GL_FLOAT, false,
colorStride, colorBuffer);
// GLES30.glUniform4fv(mColorHandle, 1, colors, 0);
//绘制图形
GLES30.glLineWidth(10.0f);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);
//禁止颜色数组的句柄
GLES30.glDisableVertexAttribArray(mColorHandle);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(mPositionHandle);
}
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;
}
}
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绘制纯色正方形 JAVA版本#include #include #include #include #include //声明自定义函数 //创建编译加载 GLuint LoadShader(int type, char *shaderCode); //链接统一程序 GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader); //模板技术定义的函数 //用于求数组长度 template GLsizei getArrayLen(T &array) { return (sizeof(array) / sizeof(array[0])); } //坐标个数(xyz) const GLint COORDS_PER_VERTEX = 3; //颜色通道(RGBA) const GLint COORDS_PER_COLOR = 4; //顶点之间的偏移量 const GLsizei vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节 //颜色之间的偏移量 const GLsizei colorStride = COORDS_PER_COLOR * 4; // 每个颜色四个字节 //声明顶点着色器 GLuint vertexShader; //声明片元着色器 GLuint fragmentShader; //声明着色器管理程序 GLuint mProgram; //定义圆点坐标 float vertexPoints[] = { 0.0f, 0.5f, 0.0f, //上侧点 -0.5f, -0.5f, 0.0f, //左侧点 0.5f, -0.5f, 0.0f //右侧点 }; //定义颜色数据 float colors[] = { 1.0f, 0.5f, 1.0f, 1.0f, //顶部 1.0f, 1.0f, 0.5f, 1.0f, //左侧 0.5f, 1.0f, 1.0f, 1.0f //右侧 }; //点数量 int vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX; //顶点着色器代码 char vertexShaderCode[] = "#version 300 es n" "in vec4 vPosition;n" "in vec4 vColor;n" "uniform mat4 vMatrix;n" "out vec4 aColor;n" "void main() {n" " gl_Position = vMatrix*vPosition;n" " gl_PointSize = 10.0;n" " aColor = vColor;n" "}"; //片段着色器代码 char fragmentShaderCode[] = "#version 300 esn" "precision mediump float;n" "in vec4 aColor;n" "out vec4 fragColor;n" "void main() {n" " fragColor = aColor;n" "}"; //书写本地方法的具体逻辑 void JNICALL init(JNIEnv *env, jobject obj) { //初始化 } 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); } glm::mat4 mProjectionMatrix; glm::mat4 mViewMatrix; glm::mat4 mMVPMatrix; JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) { //设置视口 glViewport(0, 0, width, height); //宽高比 float ratio = (float) width / height; //二维图形设置正交投影矩阵即可 mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f); //ratio 一般表示视口的宽高比,width/height //相机位置 mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置 glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标 glm::vec3(0, 1, 0)); //矩阵合并 mMVPMatrix = mProjectionMatrix * mViewMatrix; } JNIEXPORT void JNICALL drawframe(JNIEnv *env, jobject obj) { //清除颜色缓冲区 glClear(GL_COLOR_BUFFER_BIT); //使用统一的管理程序 glUseProgram(mProgram); //获取变换矩阵vMatrix成员句柄 //图形参数 GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix"); //矩阵转换成数组 float mMVPMatrixArray[16]; int m = 0; for(int i=0;i<4;i++) for(int j=0;j<4;j++){ mMVPMatrixArray[m] = mMVPMatrix[i][j]; m++; } //指定vMatrix的值 glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray); //获取顶点着色器的vPosition成员句柄 GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition"); //启用顶点的句柄 glEnableVertexAttribArray(mPositionHandle); //准备坐标数据 glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, false, vertexStride, vertexPoints); //获取片元着色器的vColor成员的句柄 GLint mColorHandle = glGetAttribLocation(mProgram, "vColor"); //启动颜色的句柄 glEnableVertexAttribArray(mColorHandle); //准备颜色数据 glVertexAttribPointer(mColorHandle, COORDS_PER_COLOR, GL_FLOAT, false, colorStride, colors); //绘制图形 glLineWidth(10.0f); glDrawArrays(GL_TRIANGLES, 0, vertexCount); //禁止颜色数组的句柄 glDisableVertexAttribArray(mColorHandle); //禁止顶点数组的句柄 glDisableVertexAttribArray(mPositionHandle); } JNIEXPORT GLuint LoadShader(int type, char *shaderCode) { //创建一个着色器 GLuint shader = glCreateShader(type); if (shader != 0) { //加载到着色器 glShaderSource(shader, 1, &shaderCode, NULL); //编译着色器 glCompileShader(shader); //检测状态 GLint compileStatus; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); if (compileStatus == 0) { //声明log长度变量 GLint infoLen = 0; //获取长度并赋值 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { //创建成功小于等于1 char *infoLog = static_cast (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 (malloc(sizeof(char) * infoLen)); glGetProgramInfoLog(program, infoLen, NULL, infoLog); printf("PROGRAM ERROR!"); free(infoLog); } glDeleteProgram(program); return 0; } return program; } else { //创建失败 return 0; } } //本地方法声明和具体逻辑连接 JNINativeMethod methods[] = { {"init", "()V", (void *) init}, {"surfaceCreated", "()V", (void *) surfaceCreated}, {"surfaceChanged", "(II)V", (void *) surfaceChanged}, {"drawframe", "()V", (void *) drawframe} }; 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; } jint JNI_onLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1; } //注册方法 if (registerNativeMethod(env) != JNI_OK) { return -1; } return JNI_VERSION_1_6; }
Renderer类
package com.example.openglndk;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyRenderer implements GLSurfaceView.Renderer {
private final static int COORDS_PER_VERTEX = 3;
//顶点之间的偏移量
private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
private FloatBuffer vertexBuffer;
private ShortBuffer indexBuffer;
private int vertexShader;
private int fragmentShader;
private int mProgram;
//物体空间点坐标
private float vertexPoints[] = {
-0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f // top right
};
private int vertexCount = vertexPoints.length / COORDS_PER_VERTEX;
//设置颜色,依次为红绿蓝和透明通道
//青蓝色
private float color[] = {0.1f, 0.7f, 1.0f, 0.0f};
//索引
private short index[] = {
0, 1, 2, 0, 2, 3
};
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"void main() {" +
" gl_Position = vMatrix*vPosition;" + //乘以投影矩阵
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
public MyRenderer() {
//坐标
vertexBuffer = ByteBuffer.allocateDirect(
vertexPoints.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexBuffer.put(vertexPoints);
vertexBuffer.position(0);
//索引
indexBuffer = ByteBuffer.allocateDirect(index.length * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer();
indexBuffer.put(index);
indexBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES30.glClearColor(1, 1, 1, 1);
vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER,
vertexShaderCode);
fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = linkProgram(vertexShader, fragmentShader);
}
private float mProjectionMatrix[] = new float[16];
private float mViewMatrix[] = new float[16];
private float mMVPMatrix[] = new float[16];
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES30.glViewport(0, 0, width, height);
//宽高比
//(float) (width / height)错误,两个整数相除,得到整数再变成float,有误差
//必须先转换,再计算
float ratio = width > height ? (float) width / height : (float) height / width;
if(width > height){
//设置正交投影矩阵,将物体空间的坐标映射
//orthoM函数产生的矩阵会把所有的左右之间、上下之间,远近之间的点映射到归一化设备坐标中
//横屏
Matrix.orthoM(mProjectionMatrix,0,-ratio,ratio,-1,1,0,5);
}else{
//竖屏
Matrix.orthoM(mProjectionMatrix,0,-1,1,-ratio,ratio,0,5);
}
//设置透视投影矩阵
// Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
//设置相机角度
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//矩阵合并
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
}
@Override
public void onDrawframe(GL10 gl10) {
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//将程序加入
GLES30.glUseProgram(mProgram);
//图形参数
int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
//指定vMatrix的值
GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
//获取顶点着色器的vPosition成员句柄
int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
//启用顶点的句柄
GLES30.glEnableVertexAttribArray(mPositionHandle);
//准备坐标数据
GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES30.GL_FLOAT, false,
vertexStride, vertexBuffer);
//获取片元着色器的vColor成员的句柄
int mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
//设置绘制三角形的颜色
GLES30.glUniform4fv(mColorHandle, 1, color, 0);
//顶点法绘制正方形
GLES30.glLineWidth(10.0f);
GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, vertexCount);
//索引法绘制正方形
// GLES30.glDrawElements(GLES30.GL_TRIANGLES, index.length, GLES30.GL_UNSIGNED_SHORT, indexBuffer);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(mPositionHandle);
}
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;
}
}
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绘制纯色圆形 JAVA版本#include #include #include #include #include //声明自定义函数 //创建编译加载 GLuint LoadShader(int type, char *shaderCode); //链接统一程序 GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader); //模板技术定义的函数 //用于求数组长度 template GLsizei getArrayLen(T &array) { return (sizeof(array) / sizeof(array[0])); } //坐标个数(xyz) const GLint COORDS_PER_VERTEX = 3; //顶点之间的偏移量 const int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节 //声明顶点着色器 GLuint vertexShader; //声明片元着色器 GLuint fragmentShader; //声明着色器管理程序 GLuint mProgram; //定义点坐标 float vertexPoints[] = { -0.5f, 0.5f, 0.0f, //左上角 -0.5f, -0.5f, 0.0f, //左下角 0.5f, -0.5f, 0.0f, //右下角 0.5f, 0.5f, 0.0f //右上角 }; //设置颜色,依次为红绿蓝和透明通道 float color[] = {1.0f, 1.0f, 0.0f, 0.0f}; //索引 short index[] = { 0, 1, 2, 0, 2, 3 }; const int vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX; //顶点着色器代码 char vertexShaderCode[] = "attribute vec4 vPosition; n" "uniform mat4 vMatrix;n" "void main() {n" " gl_Position = vMatrix*vPosition;n" " gl_PointSize = 10.0; n" "}"; //片段着色器代码 char fragmentShaderCode[] = "precision mediump float;" "uniform vec4 vColor;" "void main() {" " gl_FragColor = vColor;" "}"; //书写本地方法的具体逻辑 void JNICALL init(JNIEnv *env, jobject obj) { //初始化 } 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); } glm::mat4 mProjectionMatrix; glm::mat4 mViewMatrix; glm::mat4 mMVPMatrix; JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) { //设置视口 glViewport(0, 0, width, height); //宽高比 float ratio = (float) width / height; //二维图形设置正交投影矩阵即可 mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f); //ratio 一般表示视口的宽高比,width/height //相机位置 mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置 glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标 glm::vec3(0, 1, 0)); //矩阵合并 mMVPMatrix = mProjectionMatrix * mViewMatrix; } JNIEXPORT void JNICALL drawframe(JNIEnv *env, jobject obj) { //清除颜色缓冲区 glClear(GL_COLOR_BUFFER_BIT); //使用程序 glUseProgram(mProgram); //获取顶点着色器的vMatrix成员句柄 GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix"); //矩阵转换成数组 float mMVPMatrixArray[16]; int m = 0; for(int i=0;i<4;i++) for(int j=0;j<4;j++){ mMVPMatrixArray[m] = mMVPMatrix[i][j]; m++; } //指定vMatrix的值 glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray); //获取顶点着色器的vPosition成员句柄 GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition"); //启用图形顶点的句柄 glEnableVertexAttribArray(mPositionHandle); //准备图形的坐标数据 glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride, vertexPoints); //获取片元着色器的vColor成员的句柄 GLint mColorHandle = glGetUniformLocation(mProgram, "vColor"); //设置绘制图形的颜色 glUniform4fv(mColorHandle, 1, color); //顶点法绘制图形 glLineWidth(10.0f); // glDrawArrays(GL_LINE_LOOP,0,vertexCount); //索引法绘制图形 glDrawElements(GL_TRIANGLES,getArrayLen(index),GL_UNSIGNED_SHORT,index); //禁止顶点数组的句柄 glDisableVertexAttribArray(mPositionHandle); } JNIEXPORT GLuint LoadShader(int type, char *shaderCode) { //创建一个着色器 GLuint shader = glCreateShader(type); if (shader != 0) { //加载到着色器 glShaderSource(shader, 1, &shaderCode, NULL); //编译着色器 glCompileShader(shader); //检测状态 GLint compileStatus; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); if (compileStatus == 0) { //声明log长度变量 GLint infoLen = 0; //获取长度并赋值 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { //创建成功小于等于1 char *infoLog = static_cast (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 (malloc(sizeof(char) * infoLen)); glGetProgramInfoLog(program, infoLen, NULL, infoLog); printf("PROGRAM ERROR!"); free(infoLog); } glDeleteProgram(program); return 0; } return program; } else { //创建失败 return 0; } } //本地方法声明和具体逻辑连接 JNINativeMethod methods[] = { {"init", "()V", (void *) init}, {"surfaceCreated", "()V", (void *) surfaceCreated}, {"surfaceChanged", "(II)V", (void *) surfaceChanged}, {"drawframe", "()V", (void *) drawframe} }; 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; } jint JNI_onLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1; } //注册方法 if (registerNativeMethod(env) != JNI_OK) { return -1; } return JNI_VERSION_1_6; }
Renderer类
package com.example.openglndk;
import android.opengl.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 {
private final static int COORDS_PER_VERTEX = 3;
private int n = 360; //切割份数
private float radius = 0.3f; //半径
//顶点之间的偏移量
private final int vertexStride = COORDS_PER_VERTEX * 4; // 每个顶点四个字节
private FloatBuffer vertexBuffer;
private int vertexShader;
private int fragmentShader;
private int mProgram;
private int vertexCount;
//顶点
//定义正方形四点坐标
private float vertexPoints[];
private float height = 0.0f;
//设置颜色,依次为红绿蓝和透明通道
//粉色
private float color[] = {1.0f, 0.5f, 0.7f, 0.0f};
//创建顶点坐标
private float[] createPositions() {
ArrayList 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(height);
}
float[] f = new float[data.size()];
for (int i = 0; i < f.length; i++) {
f[i] = data.get(i);
}
return f;
}
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"uniform mat4 vMatrix;" +
"void main() {" +
" gl_Position = vMatrix*vPosition;" +
" gl_PointSize = 10.0;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
public MyRenderer() {
vertexPoints = createPositions();
vertexCount = vertexPoints.length / COORDS_PER_VERTEX;
//坐标
vertexBuffer = ByteBuffer.allocateDirect(
vertexPoints.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexBuffer.put(vertexPoints);
vertexBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES30.glClearColor(1, 1, 1, 1);
vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER,
vertexShaderCode);
fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = linkProgram(vertexShader, fragmentShader);
}
private float mProjectionMatrix[] = new float[16];
private float mViewMatrix[] = new float[16];
private float mMVPMatrix[] = new float[16];
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES30.glViewport(0, 0, width, height);
//宽高比
float ratio = (float) width / height;
//设置投影矩阵
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
//设置相机角度
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//矩阵合并
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
}
//每20ms刷新
@Override
public void onDrawframe(GL10 gl10) {
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//将程序加入
GLES30.glUseProgram(mProgram);
//获取变换矩阵vMatrix成员句柄
//图形参数
int mMatrixHandler = GLES30.glGetUniformLocation(mProgram, "vMatrix");
//指定vMatrix的值
GLES30.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
//获取顶点着色器的vPosition成员句柄
int mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");
//启用顶点的句柄
GLES30.glEnableVertexAttribArray(mPositionHandle);
//准备坐标数据
GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES30.GL_FLOAT, false,
vertexStride, vertexBuffer);
//获取片元着色器的vColor成员的句柄
int mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
//设置绘制图形的颜色
GLES30.glUniform4fv(mColorHandle, 1, color, 0);
//顶点法绘制图形
GLES30.glLineWidth(10.0f);
GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, vertexCount);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(mPositionHandle);
}
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;
}
}
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
if (programId != 0) {
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序中
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.gllinkProgram(programId);
final int[] linkStatus = new int[1];
GLES30.glGetProgramiv(programId, GLES30.GL_link_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
String logInfo = GLES30.glGetProgramInfoLog(programId);
System.err.println(logInfo);
GLES30.glDeleteProgram(programId);
return 0;
}
return programId;
} else {
//创建失败
return 0;
}
}
}
C++版本
我弄了好久,一天多,从JAVA版本到C++版本。总结起来还是因为C++的基础不够牢固。C++中求数组长度没有函数,没有ArrayList数据结构,创建数组等都不太熟悉。然后就找不到错误在哪里,最后我一点一点从断点调试,才发现问题,没有拿到Vector中的数据,C++函数无法返回数组,只能返回指向数组的指针。也拿不到准确的建造数组的长度,Vector中取数据,按照数组格式,可能取不到数据,要按照迭代器进行遍历取数据。总之因为一堆原因,C++和JAVA的各种语法和数据结构的不同,导致创建不了点的坐标,拿不到那种float数据类型的数组,就没有点,绘制不了。最后我不在函数中拿到数组了,我直接返回Vector数据结构,然后在初始化中,使用迭代器遍历Vector,将其中数据直接放入到数组中,终于拿到了数据,完成了绘制。
.cpp文件
// // Created by lvjunkai on 2022/2/21. // #include#include #include #include #include #include #include #include //命名空间 using namespace std; //常数Π const double PI = acos(-1.0); //声明自定义函数 //创建编译加载 GLuint LoadShader(int type, char *shaderCode); //链接统一程序 GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader); //模板技术定义的函数 //用于求数组长度 template 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 vertexVector; //存放顶点数据的数据结构 float vertexPoints[1083]; //顶点数组 float radius = 0.3f; //半径 int n = 360; //切割份数 //顶点个数 GLsizei vertexCount; //设置颜色,依次为红绿蓝和透明通道 float color[] = {1.0f, 1.0f, 0.0f, 0.0f}; //顶点着色器代码 char vertexShaderCode[] = "attribute vec4 vPosition;" "uniform mat4 vMatrix;" "void main() {" " gl_Position = vMatrix*vPosition;" " gl_PointSize = 10.0;" "}"; //片段着色器代码 char fragmentShaderCode[] = "precision mediump float;" "uniform vec4 vColor;" "void main() {" " gl_FragColor = vColor;" "}"; //创建顶点坐标放入vector数据结构中 vector createPositions() { vector data; //圆上点 float angDegSpan = 360.0f / n; for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) { data.push_back((float) (radius * sin(i * PI / 180.0f))); data.push_back((float) (radius * cos(i * PI / 180.0f))); data.push_back(0.0f); } return data; } JNIEXPORT void JNICALL init(JNIEnv *env, jobject obj) { //拿到顶点数据 vertexVector = createPositions(); //创建迭代器 vector ::iterator t; int i = 0; //迭代器遍历vector中的数据 //将其中数据放入数组中,形成顶点坐标 for (t = vertexVector.begin(); t != vertexVector.end(); t++) { vertexPoints[i] = *t; i++; } vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX; } //书写本地方法的具体逻辑 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); } glm::mat4 mProjectionMatrix; glm::mat4 mViewMatrix; glm::mat4 mMVPMatrix; JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) { //设置视口 glViewport(0, 0, width, height); //宽高比 float ratio = (float) width / height; //正交投影矩阵 mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f); //ratio 一般表示视口的宽高比,width/height //透视投影矩阵 // glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f); //ratio 一般表示视口的宽高比,width/height //相机位置 mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置 glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标 glm::vec3(0, 1, 0)); //矩阵合并 mMVPMatrix = mProjectionMatrix * mViewMatrix; } JNIEXPORT void JNICALL drawframe(JNIEnv *env, jobject obj) { //清除颜色缓冲区 glClear(GL_COLOR_BUFFER_BIT); //使用程序 glUseProgram(mProgram); //获取变换矩阵vMatrix成员句柄 GLint mMatrixHandler = glGetUniformLocation(mProgram, "vMatrix"); //矩阵转换成数组 float mMVPMatrixArray[16]; int m = 0; for(int i=0;i<4;i++) for(int j=0;j<4;j++){ mMVPMatrixArray[m] = mMVPMatrix[i][j]; m++; } //指定vMatrix的值 glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrixArray); //获取顶点着色器的vPosition成员句柄 GLint mPositionHandle = glGetAttribLocation(mProgram, "vPosition"); //启用图形顶点的句柄 glEnableVertexAttribArray(mPositionHandle); //准备图形的坐标数据 glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GL_FLOAT, GL_FALSE, vertexStride, vertexPoints); //获取片元着色器的vColor成员的句柄 GLint mColorHandle = glGetUniformLocation(mProgram, "vColor"); //设置绘制图形的颜色 glUniform4fv(mColorHandle, 1, color); //绘制图形 glLineWidth(10.0f); glDrawArrays(GL_LINE_LOOP, 0, vertexCount); //禁止顶点数组的句柄 glDisableVertexAttribArray(mPositionHandle); } JNIEXPORT GLuint LoadShader(int type, char *shaderCode) { //创建一个着色器 GLuint shader = glCreateShader(type); if (shader != 0) { //加载到着色器 glShaderSource(shader, 1, &shaderCode, NULL); //编译着色器 glCompileShader(shader); //检测状态 GLint compileStatus; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); if (compileStatus == 0) { //声明log长度变量 GLint infoLen = 0; //获取长度并赋值 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { //创建成功小于等于1 char *infoLog = static_cast (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 (malloc(sizeof(char) * infoLen)); glGetProgramInfoLog(program, infoLen, NULL, infoLog); printf("PROGRAM ERROR!"); free(infoLog); } glDeleteProgram(program); return 0; } return program; } else { //创建失败 return 0; } } //本地方法声明和具体逻辑连接 JNINativeMethod methods[] = { {"init", "()V", (void *) init}, {"surfaceCreated", "()V", (void *) surfaceCreated}, {"surfaceChanged", "(II)V", (void *) surfaceChanged}, {"drawframe", "()V", (void *) drawframe} }; 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; } jint JNI_onLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1; } //注册方法 if (registerNativeMethod(env) != JNI_OK) { return -1; } return JNI_VERSION_1_6; }
Renderer类
package com.example.openglndk;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class WaveRenderer implements GLSurfaceView.Renderer {
//链接到库
static{
System.loadLibrary("native-wave");
}
//声明本地方法
public native void init();
public native void surfaceCreated();
public native void surfaceChanged(int width, int height);
public native void drawframe();
//构造函数
public WaveRenderer(){
init();
}
//接口方法实现
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
surfaceCreated();
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
surfaceChanged(width,height);
}
@Override
public void onDrawframe(GL10 gl10) {
drawframe();
}
}
波纹扩散demo
.cpp文件
// // Created by lvjunkai on 2022/2/21. // #include#include #include #include #include #include #include //命名空间 using namespace std; //常数Π const double PI = acos(-1.0); //声明自定义函数 //创建编译加载 GLuint LoadShader(int type, char *shaderCode); //链接统一程序 GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader); //模板技术定义的函数 //用于求数组长度 template 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 vertexVector; //存放顶点数据的数据结构 float vertexPoints[1083]; //顶点数组 //float radius; //半径 int n = 360; //切割份数 //顶点个数 GLsizei vertexCount; //设置颜色,依次为红绿蓝和透明通道 float color[] = {1.0f, 1.0f, 0.0f, 0.0f}; //顶点着色器代码 char vertexShaderCode[] = "attribute vec4 vPosition;" "uniform mat4 vMatrix;" "void main() {" " gl_Position = vMatrix*vPosition;" " gl_PointSize = 10.0;" "}"; //片段着色器代码 char fragmentShaderCode[] = "precision mediump float;" "uniform vec4 vColor;" "void main() {" " gl_FragColor = vColor;" "}"; //创建顶点坐标放入vector数据结构中 vector createPositions(float radius) { vector data; //圆上点 float angDegSpan = 360.0f / n; for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) { data.push_back((float) (radius * sin(i * PI / 180.0f))); data.push_back((float) (radius * cos(i * PI / 180.0f))); data.push_back(0.0f); } return data; } JNIEXPORT void JNICALL init(JNIEnv *env, jobject obj,float radius) { //拿到顶点数据 vertexVector = createPositions(radius); //创建迭代器 vector ::iterator t; int i = 0; //迭代器遍历vector中的数据 //将其中数据放入数组中,形成顶点坐标 for (t = vertexVector.begin(); t != vertexVector.end(); t++) { vertexPoints[i] = *t; i++; } vertexCount = getArrayLen(vertexPoints) / COORDS_PER_VERTEX; } //书写本地方法的具体逻辑 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); } glm::mat4 mProjectionMatrix; glm::mat4 mViewMatrix; glm::mat4 mMVPMatrix; float mMVPMatrixArray[16]; JNIEXPORT void JNICALL surfaceChanged(JNIEnv *env, jobject obj, jint width, jint height) { //设置视口 glViewport(0, 0, width, height); //宽高比 float ratio = (float) width / height; //正交投影矩阵 mProjectionMatrix = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f); //ratio 一般表示视口的宽高比,width/height //透视投影矩阵 // glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f); //ratio 一般表示视口的宽高比,width/height //相机位置 mViewMatrix = glm::lookAt(glm::vec3(0, 0, -3), // Camera is at (0,0,1), in World Space 相机位置 glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标 glm::vec3(0, 1, 0)); //矩阵合并 mMVPMatrix = mProjectionMatrix * mViewMatrix; //矩阵转换成数组 int m = 0; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { mMVPMatrixArray[m] = mMVPMatrix[i][j]; m++; } } 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 (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 (malloc(sizeof(char) * infoLen)); glGetProgramInfoLog(program, infoLen, NULL, infoLog); printf("PROGRAM ERROR!"); free(infoLog); } glDeleteProgram(program); return 0; } return program; } else { //创建失败 return 0; } } //本地方法声明和具体逻辑连接 JNINativeMethod methods[] = { {"init", "(F)V", (void *) init}, {"surfaceCreated", "()V", (void *) surfaceCreated}, {"surfaceChanged", "(II)V", (void *) surfaceChanged}, {"drawframe", "()V", (void *) drawframe} }; 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; } jint JNI_onLoad(JavaVM *vm, void *reserved) { JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1; } //注册方法 if (registerNativeMethod(env) != JNI_OK) { return -1; } return JNI_VERSION_1_6; }
Renderer类
package com.example.openglndk;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class WaveRenderer implements GLSurfaceView.Renderer {
//链接到库文件
static{
System.loadLibrary("native-wave");
}
//声明本地方法
public native void init(float radius);
public native void surfaceCreated();
public native void surfaceChanged(int width,int height);
public native void drawframe();
//构造函数
public WaveRenderer(float radius){
init(radius);
}
//实现接口方法
//用三个本地方法复写三个接口方法
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
surfaceCreated();
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
surfaceChanged(width,height);
}
@Override
public void onDrawframe(GL10 gl10) {
drawframe();
}
}
MainActivity——用于扩散操作,有bug
package com.example.openglndk;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
//JNI类
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLSurfaceView;
private float radius = 0.1f;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
setContentView(mGLSurfaceView);
//设置版本ES 3.0
mGLSurfaceView.setEGLContextClientVersion(3);
//设置渲染器 JAVA版本
// GLSurfaceView.Renderer renderer = new MyRenderer();
//设置渲染器 C++版本
@SuppressLint("HandlerLeak") Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
GLSurfaceView.Renderer renderer = new WaveRenderer((Float) msg.obj);
mGLSurfaceView.setRenderer(renderer);
}
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
message.obj = radius;
while(radius <= 0.5f){
handler.sendMessage(message);
radius += 0.1f;
}
}
};
handler.post(runnable);
}
}
总结绘制整体流程
- 创建一个Activity作为窗口,布局只有一个GLSurfaceView控件用来显示绘制的内容。新建一个Renderer渲染器(核心),将3D物体绘制到屏幕上。
新建渲染器,实现GLSurfaceView.Renderer接口。声明Native方法,将其变成JNI类。用声明的Native方法复写接口的三个方法:onSurfaceCreated,onSurfaceChanged和onDrawframe方法。链接到声明方法具体实现库的文件名。 新建声明方法具体实现库。
定义圆点坐标定义颜色数据书写着色器代码创建和编译着色器将着色器交给统一的程序管理设置视口清除颜色缓冲区绘制图形 在库编译文件CMakeLists.txt中,加入该库,使其可以进行编译执行。



