NDK开发OpenGL ES 3.0(五)——一个立方体
标签(空格分隔): OpenGL-ES 0
版本:2
作者:陈小默
声明:禁止商用,禁止转载
文章仅发布于 作业部落、、开源中国
上一篇:NDK开发OpenGL ES 3.0(四)——旋转的彩色方块
本章参考书:
[1]Dan Ginsburg,Budirjanto Purnomo.OpenGL ES 3.0 编程指南 第二版(姚军 译).北京:机械工业出版社
本篇主要介绍OpenGL ES中的图元装配和一些光栅化过程。学完本篇内容之后我们将会建造一个简单的旋转的立方体,就像下图所示。
[toc]
九、图元装配和光栅化
本章主要内容是介绍基本图元,以及图元的装配和最终显示之前的光栅化。
9.1 基本图元
图元是由一组顶点描述的几何形状对象。在OpenGL中,任何图元都可以被绘制,但是在OpenGL ES中,由于其性能被精简(毕竟移动端CPU和GPU不比桌面设备),所以只能绘制三种最基本的图元,但所有的图形都可以使用这三个基本图元来表示:
- 点精灵
- 直线
- 三角形
9.1.1 点精灵
在OpenGL中,点精灵是指定位置和半径的屏幕对其的正方形,常用作粒子效果。
图元枚举:GL_POINTS
注意:默认情况下,点精灵的坐标原点是屏幕左上角。当我们决定绘制点的时候,需要在着色器中设置点的尺寸,否则,可能会造成绘图错误。在着色器中表示点精灵尺寸的变量是gl_PointSize。
9.1.2 直线
图元枚举:GL_LINES、GL_LINE_STRIP、GL_LINE_LOOP
说明:假设现在有[0,1,2,3,4]共5个点组成的数组,我们使用下列类型绘制的结果:
- GL_LINES:绘制出[0,1]和[2,3]两条线,第五个点由于没有匹配点而被忽略。
- GL_LINE_STRIP:绘制出[0,1],[1,2],[2,3],[3,4]两两相连的四条线组成的折现。
- GL_LINE_LOOP:绘制出[0,1],[1,2],[2,3],[3,4],[4,1]首尾相连的封闭图形。
其他函数:
void glLineWidth(GLfloat width);
- width:指定线宽,默认为1.0
9.1.3 三角形
将一个图形分解为若干三角形是几何渲染最常用的方式
图元枚举:GL_TRIANGLES, GL_TRIANGLE_STRIP ,GL_TRIANGLE_FAN
说明:假设有[0,1,2,3,4,5,6]共7个点组成的数组,我们使用下列类型的绘制结果:
- GL_TRIANGLES:绘制出[0,1,2]和[3,4,5]两个三角形,第7个点由于没有匹配点而被忽略。
- GL_TRIANGLE_STRIP:绘制出[0,1,2],[2,1,3],[2,3,4],[4,3,5],[4,5,6]共5个三角形(绘制出偶数下标[n,n+1,n+2]和奇数下标[n+1,n,n+2]共n-2个三角形)
- GL_TRIANGLE_FAN:绘制出[0,1,2],[0,2,3],[0,3,4],[0,4,5],[0,5,6]等共享顶点的5个三角形(绘制出[0,n+1,n+2]共n-2个三角形)
9.2 绘制图元
OpenGL ES提供了五种绘制图元的函数,分别是glDrawArrays、glDrawElements、glDrawRangeElement、glDrawArraysInstanced和glDrawElementsInstanced。接下来分别介绍这几种方法。
9.2.1 glDrawArrays
void glDrawArrays (GLenum mode, GLint first, GLsizei count);
- mode:指定图元枚举,9.1节中的任意一个。
- first:指定从顶点数组中的何处开始绘制。
- count:需要绘制的顶点数量。
示例代码:
#define VERTEX_IND 0
GLfloat VERTICES[]={...};
glEnableVertexAttribArray(VERTEX_IND);
glVertexAttribPointer(VERTEX_IND,3,GL_FLOAT,GL_FALSE,0,VERTICES);
glDrawArrays(GL_TRIANGLES,0,3);
9.2.2 glDrawElements
该函数用来绘制图元,使用前需要创建图元索引列表。
void glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
- mode:指定图元枚举,9.1节中的任意一个。
- count:指定要绘制的索引数量。
- type:指定索引类型。
- indices:索引数组。
示例代码:
#define VERTEX_IND 0
GLfloat VERTICES[]={
0.1f,0.3f,0.4f,//0
0.2f,0.5f,0.7f,//1
0.9f,-0.3f,0.8f,//2
0.4f,0.5f,-0.2f//3
};
GLubyte INDICES[]={
0,1,2,
3,2,1
};
glEnableVertexAttribArray(VERTEX_IND);
glVertexAttribPointer(VERTEX_IND,3,GL_FLOAT,GL_FALSE,0,VERTICES);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_BYTE,INDICES);
9.2.3 glDrawRangeElement
void glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);
- mode:指定图元枚举,9.1节中的任意一个。
- start:指定图元索引列表中最小值(从顶点数组何处开始)。
- end:指定图元索引列表中的最大值(从顶点数组何处结束)。
- count:指定要绘制的索引数量。
- type:指定索引类型。
- indices:索引数组。
示例代码(本例限定了仅顶点坐标中1-6有效,那么此次就只会绘制[3,4,5]这个三角形):
#define VERTEX_IND 0
GLfloat VERTICES[] = {};//10个顶点
GLubyte INDICES[] = {
0, 1, 2,
0, 2, 3,
3, 4, 5,
7, 8, 9
};
glEnableVertexAttribArray(VERTEX_IND);
glVertexAttribPointer(VERTEX_IND, 3, GL_FLOAT, GL_FALSE, 0, VERTICES);
glDrawRangeElements(GL_TRIANGLES, 1, 6, 12, GL_UNSIGNED_BYTE, INDICES);
}
9.3 图元装配光栅化过程
在图形经过顶点着色器处理之后,便进入了图元装配和光栅化的过程。其流程如下:
st=>start: 顶点着色器输出
cut=>inputoutput: 裁剪
split=>inputoutput: 透视分割
tran=>inputoutput: 窗口变换
culling=>inputoutput: 剔除
e=>end: 片元着色器输入
st->cut->split->tran->culling->e
9.3.1 裁剪
在某些时候,我们的图元肯能超出了屏幕能够显示的范围。这时候,就需要对这些图元进行裁剪。
- 点精灵裁剪:如果整个点都位于视图之外,则放弃这个点,否则保留。
- 直线裁剪:如果直线完全位于视图之外,则放弃全部。如果直线完全位于视图内,则全部保留。如果直线部分位置视图内,则重新计算并生成新的顶点。
- 三角形裁剪:如果三角形全部位于视图之外,放弃全部顶点。如果三角形完全位于视图内,则保留全部顶点。如果三角形部分位于视图内,则重新计算并产生若干新的三角形顶点
9.3.2 透视分割
透视分割取得裁剪坐标指定的点,并将其投影到屏幕或者是视图上。在过程中坐标将被转换为规范化坐标,即[-1.0..1.0]区间。
9.3.3 视口变换
视口是一个二维矩形窗口区域,是所有OpenGL ES渲染操作最终显示的地方。
void glViewport(GLint x,GLint y,GLsizei w,GLsizei h);
这个常用的方法指定了一个视口的区域。
9.4 剔除
在图形被光栅化之前,我们需要确定它是正面或者反面,这样可以剔除掉不需要显示的图形,仅保留可见部分。在剔除之前我们需要告诉OpenGL正反面的标准是什么。对于三角形图元,我们仅仅需要注明当前已逆时针为正还是逆时针。
void glFrontFace (GLenum mode);
- mode:旋转方向GL_CW顺时针,GL_CCW逆时针
当我们指定了区分正反的方法之后,接下来可以调用下列方法剔除特定的面。
void glCullFace (GLenum mode);
- mode:指定我们需要剔除的方向GL_FRONT剔除正面、GL_BACK剔除背面、GL_FRONT_AND_BACK剔除正面和背面
9.5 光栅化
在进行完以上操作之后,光栅化管线取得了单独的图元,并为该图元生成相应的片元(具体的像素点)。然后每个像素点都会经过片元着色器处理。
10、绘制一个旋转立方体
本章内容为绘制一个旋转的立方体的教程。本章只展示JNI部分的编写(暂不详解),其他部分和之前相同,请自行实现。示例改编自OpenGL ES 3.0 编程指南 第七章示例代码目录结构如下:
#define LOG_TAG "cube-lib"
#include "esUtil.h"
#include
#include
#define PI 3.14
#define POSITION_IND 0
#define COLOR_IND 1
#define MVP_IND 2
#define VERTEX_NUM 108
#define INDICES_NUM 36
#define MATRIX_COLUMN 4
#define MATRIX_ROW 4
GLuint program;
GLuint positionVBO;
GLuint colorVBO;
GLuint mvpVBO;
GLuint indicesIBO;
GLfloat aspect;
uint64_t lastFrameNs;
GLfloat angle;
const char VERTEX_SHADER[] =
"#version 300 es\n"
"layout(location = " STRV(POSITION_IND) ") in vec4 a_position;\n"
"layout(location = " STRV(COLOR_IND) ") in vec4 a_color;\n"
"layout(location = " STRV(MVP_IND) ") in mat4 a_mvpMatrix;\n"
"out vec4 v_color;\n"
"void main()\n"
"{\n"
" v_color = a_color;\n"
" gl_Position = a_mvpMatrix * a_position;\n"
"}\n";
const char FRAGMENT_SHADER[] =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 v_color;\n"
"layout(location = 0) out vec4 outColor;\n"
"void main()\n"
"{\n"
" outColor = v_color;\n"
"}\n";
GLfloat VERTEX_POS[VERTEX_NUM] =
{
-0.5f, -0.5f, -0.5f,//0
0.5f, -0.5f, 0.5f,//2
-0.5f, -0.5f, 0.5f,//1
-0.5f, -0.5f, -0.5f,//0
0.5f, -0.5f, -0.5f,//3
0.5f, -0.5f, 0.5f,//2
-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,//4
0.5f, 0.5f, 0.5f,//6
0.5f, 0.5f, -0.5f,//7
-0.5f, -0.5f, -0.5f,//0
-0.5f, 0.5f, -0.5f,//4
0.5f, 0.5f, -0.5f,//7
-0.5f, -0.5f, -0.5f,//0
0.5f, 0.5f, -0.5f,//7
0.5f, -0.5f, -0.5f,//3
-0.5f, -0.5f, 0.5f,//1
0.5f, -0.5f, 0.5f,//2
0.5f, 0.5f, 0.5f,//6
-0.5f, -0.5f, 0.5f,//1
0.5f, 0.5f, 0.5f,//6
-0.5f, 0.5f, 0.5f,//5
-0.5f, -0.5f, -0.5f,//0
-0.5f, -0.5f, 0.5f,//1
-0.5f, 0.5f, 0.5f,//5
-0.5f, -0.5f, -0.5f,//0
-0.5f, 0.5f, 0.5f,//5
-0.5f, 0.5f, -0.5f,//4
0.5f, -0.5f, -0.5f,//3
0.5f, 0.5f, -0.5f,//7
0.5f, 0.5f, 0.5f,//6
0.5f, -0.5f, -0.5f,//3
0.5f, 0.5f, 0.5f,//6
0.5f, -0.5f, 0.5f//2
};
const GLfloat cubeColor[] =
{
1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
0.0f, 1.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f
};
typedef struct {
GLfloat m[MATRIX_ROW][MATRIX_COLUMN];
} Matrix;
bool
init() {
program = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
if (!program)
return false;
glGenBuffers(1, &positionVBO);
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glBufferData(GL_ARRAY_BUFFER, VERTEX_NUM * sizeof(GLfloat), VERTEX_POS, GL_STATIC_DRAW);
glGenBuffers(1, &colorVBO);
glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
glBufferData(GL_ARRAY_BUFFER, INDICES_NUM * 4 * sizeof(GLfloat), cubeColor, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
glVertexAttribPointer(COLOR_IND, 4, GL_FLOAT,
GL_FALSE, 0, (const void *) NULL);
glEnableVertexAttribArray(COLOR_IND);
angle = float(drand48() * 360.0f);
glGenBuffers(1, &mvpVBO);
glBindBuffer(GL_ARRAY_BUFFER, mvpVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Matrix), NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glVertexAttribPointer(POSITION_IND, 3, GL_FLOAT,
GL_FALSE, 3 * sizeof(GLfloat), (const void *) NULL);
glEnableVertexAttribArray(POSITION_IND);
glBindBuffer(GL_ARRAY_BUFFER, mvpVBO);
glVertexAttribPointer(MVP_IND + 0, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix),
(const void *) NULL);
glVertexAttribPointer(MVP_IND + 1, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix),
(const void *) (sizeof(GLfloat) * 4));
glVertexAttribPointer(MVP_IND + 2, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix),
(const void *) (sizeof(GLfloat) * 8));
glVertexAttribPointer(MVP_IND + 3, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix),
(const void *) (sizeof(GLfloat) * 12));
glEnableVertexAttribArray(MVP_IND + 0);
glEnableVertexAttribArray(MVP_IND + 1);
glEnableVertexAttribArray(MVP_IND + 2);
glEnableVertexAttribArray(MVP_IND + 3);
glVertexAttribDivisor(MVP_IND + 0, 1);
glVertexAttribDivisor(MVP_IND + 1, 1);
glVertexAttribDivisor(MVP_IND + 2, 1);
glVertexAttribDivisor(MVP_IND + 3, 1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesIBO);
glUseProgram(program);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
return true;
}
void
matrixLoadIdentity(Matrix *result) {
memset(result, 0, sizeof(Matrix));
result->m[0][0] = 1.0f;
result->m[1][1] = 1.0f;
result->m[2][2] = 1.0f;
result->m[3][3] = 1.0f;
}
void
matrixMultiply(Matrix *result, Matrix *srcA, Matrix *srcB) {
Matrix tmp;
int i;
for (i = 0; i < 4; i++) {
tmp.m[i][0] = (srcA->m[i][0] * srcB->m[0][0]) +
(srcA->m[i][1] * srcB->m[1][0]) +
(srcA->m[i][2] * srcB->m[2][0]) +
(srcA->m[i][3] * srcB->m[3][0]);
tmp.m[i][1] = (srcA->m[i][0] * srcB->m[0][1]) +
(srcA->m[i][1] * srcB->m[1][1]) +
(srcA->m[i][2] * srcB->m[2][1]) +
(srcA->m[i][3] * srcB->m[3][1]);
tmp.m[i][2] = (srcA->m[i][0] * srcB->m[0][2]) +
(srcA->m[i][1] * srcB->m[1][2]) +
(srcA->m[i][2] * srcB->m[2][2]) +
(srcA->m[i][3] * srcB->m[3][2]);
tmp.m[i][3] = (srcA->m[i][0] * srcB->m[0][3]) +
(srcA->m[i][1] * srcB->m[1][3]) +
(srcA->m[i][2] * srcB->m[2][3]) +
(srcA->m[i][3] * srcB->m[3][3]);
}
memcpy(result, &tmp, sizeof(Matrix));
}
void
frustum(Matrix *result, float w, float h, float nearZ,
float farZ) {
float left = -w;
float right = w;
float bottom = -h;
float top = h;
float deltaX = right - left;
float deltaY = top - bottom;
float deltaZ = farZ - nearZ;
Matrix frust;
if ((nearZ <= 0.0f) || (farZ <= 0.0f) ||
(deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f)) {
return;
}
frust.m[0][0] = 2.0f * nearZ / deltaX;
frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
frust.m[1][1] = 2.0f * nearZ / deltaY;
frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
frust.m[2][0] = (right + left) / deltaX;
frust.m[2][1] = (top + bottom) / deltaY;
frust.m[2][2] = -(nearZ + farZ) / deltaZ;
frust.m[2][3] = -1.0f;
frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
matrixMultiply(result, &frust, result);
}
void
translate(Matrix *result, GLfloat tx, GLfloat ty, GLfloat tz) {
result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz);
result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz);
result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz);
result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz);
}
void
rotate(Matrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
GLfloat sinAngle, cosAngle;
GLfloat mag = sqrtf(x * x + y * y + z * z);
sinAngle = sinf(float(angle * PI / 180.0f));
cosAngle = cosf(float(angle * PI / 180.0f));
if (mag > 0.0f) {
GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
GLfloat oneMinusCos;
Matrix rotMat;
x /= mag;
y /= mag;
z /= mag;
xx = x * x;
yy = y * y;
zz = z * z;
xy = x * y;
yz = y * z;
zx = z * x;
xs = x * sinAngle;
ys = y * sinAngle;
zs = z * sinAngle;
oneMinusCos = 1.0f - cosAngle;
rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
rotMat.m[0][1] = (oneMinusCos * xy) - zs;
rotMat.m[0][2] = (oneMinusCos * zx) + ys;
rotMat.m[0][3] = 0.0F;
rotMat.m[1][0] = (oneMinusCos * xy) + zs;
rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
rotMat.m[1][2] = (oneMinusCos * yz) - xs;
rotMat.m[1][3] = 0.0F;
rotMat.m[2][0] = (oneMinusCos * zx) - ys;
rotMat.m[2][1] = (oneMinusCos * yz) + xs;
rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
rotMat.m[2][3] = 0.0F;
rotMat.m[3][0] = 0.0F;
rotMat.m[3][1] = 0.0F;
rotMat.m[3][2] = 0.0F;
rotMat.m[3][3] = 1.0F;
matrixMultiply(result, &rotMat, result);
}
}
void
update(float deltaTime) {
Matrix *matrixBuf;
Matrix perspective;
matrixLoadIdentity(&perspective);
GLfloat frustumW, frustumH;
frustumH = tanf(float(60.0f / 360.0f * PI)) * 1.5f;
frustumW = frustumH * aspect;
frustum(&perspective, frustumW, frustumH, 1.0f, 20.0f);
glBindBuffer(GL_ARRAY_BUFFER, mvpVBO);
matrixBuf = (Matrix *) glMapBufferRange(GL_ARRAY_BUFFER, 0, sizeof(Matrix),
GL_MAP_WRITE_BIT);
Matrix modelview;
matrixLoadIdentity(&modelview);
translate(&modelview, 0, 0, -2.0f);
angle += (deltaTime * 40.0f);
if (angle >= 360.0f) {
angle -= 360.0f;
}
rotate(&modelview, angle, 1.0, 0.0, 1.0);
matrixMultiply(&matrixBuf[0], &modelview, &perspective);
glUnmapBuffer(GL_ARRAY_BUFFER);
}
extern "C" {
JNIEXPORT jboolean JNICALL Java_com_github_cccxm_gles_model_CubeLib_init(JNIEnv *env, jclass type);
JNIEXPORT void JNICALL Java_com_github_cccxm_gles_model_CubeLib_resize(JNIEnv *env, jclass type,
jint width, jint height);
JNIEXPORT void JNICALL Java_com_github_cccxm_gles_model_CubeLib_render(JNIEnv *env, jclass type);
JNIEXPORT void JNICALL Java_com_github_cccxm_gles_model_CubeLib_destroy(JNIEnv *env, jclass type);
}
jboolean
Java_com_github_cccxm_gles_model_CubeLib_init(JNIEnv *env, jclass type) {
if (init())return JNI_TRUE;
return JNI_FALSE;
}
void
Java_com_github_cccxm_gles_model_CubeLib_resize(JNIEnv *env, jclass type,
jint width, jint height) {
aspect = (GLfloat) width / (GLfloat) height;
glViewport(0, 0, width, height);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void
Java_com_github_cccxm_gles_model_CubeLib_render(JNIEnv *env, jclass type) {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
auto nowNs = now.tv_sec * 1000000000ull + now.tv_nsec;
if (lastFrameNs > 0) {
float dt = float(nowNs - lastFrameNs) * 0.000000001f;
update(dt);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
lastFrameNs = nowNs;
}
void
Java_com_github_cccxm_gles_model_CubeLib_destroy(JNIEnv *env, jclass type) {
glDeleteBuffers(1, &positionVBO);
glDeleteBuffers(1, &colorVBO);
glDeleteBuffers(1, &mvpVBO);
glDeleteBuffers(1, &indicesIBO);
glDeleteProgram(program);
}
#undef LOG_TAG
下一篇:NDK开发OpenGL ES 3.0(六)——2D纹理贴图