不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!
OpenGL ES 学习——3D
颜色的简单搭配:
不红+不绿+不蓝 = 黑
红+绿+蓝 = 白
红+绿 = 黄
红+蓝 = 紫
绿+蓝 = 青蓝
投影矩阵
投影主要分为正交投影
和透视投影
两种。
正交投影
没有近大远小的效果,是平行投影,投影显示的大小就是物体的大小。
Matrix.orthoM方法
- 这是在
Java
中设置正交投影的函数。
Matrix.orthoM(mProjectMatrix, 0, left, right, bottom, top, near, far);
glm::ortho方法
- 在
C++
语言中,没有专门的数学库来处理矩阵,所以我们需要自己引入第三方库GLM
。 - 在官网下载
GLM
库,然后解压,将glm
文件夹加入到C++
下面的include
文件夹中,然后在文件中书写头文件即可使用。
template<typename T>
GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top, T zNear, T zFar)
{
if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO)
return orthoLH_ZO(left, right, bottom, top, zNear, zFar);
else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO)
return orthoLH_NO(left, right, bottom, top, zNear, zFar);
else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO)
return orthoRH_ZO(left, right, bottom, top, zNear, zFar);
else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO)
return orthoRH_NO(left, right, bottom, top, zNear, zFar);
}
- 还是和
Java
中方法相似,就是无偏移量参数,数据保存在矩阵中了,无需再一个内存来保存数据了。
透视投影
特点是近大远小,模拟人的眼球,近的物体显示大,远的物体显示小,发散光。
相机位置
相机位置,就是所谓的人所在位置,即观察者所在的位置。
在
Java
和c++
中都有函数来设置相机位置,来观察物体。通过改变相机位置,可以使我们观察到物体的每一个位置。
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++版本
-
还差相机设置不会,投影也不会。文章来源:https://www.toymoban.com/news/detail-405204.html
-
.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模板网!