转载请以链接形式标明出处:
本文出自:103style的博客
OpenGL ES 3.0学习汇总
- OpenGL ES 3.0 学习记录汇总
指定顶点属性数据
所有OpenGL ES 3.0实现必须支持最少16个顶点属性。
以下代码实现了如何查询OpenGL ES 3.0实现真正支持的顶点属性数量。
GLint maxVertexAttribs;
glGetInterv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
常量顶点属性
常量顶点属性对于一个图元的所有顶点都相同,所以对一个图元的所有顶点只需指定一个值。
可以用以下函数指定:
void glVertexAttrib1f(GLint index, GLfloat x);
void glVertexAttrib2f(GLint index, GLfloat x, GLfloat y);
void glVertexAttrib3f(GLint index, GLfloat x, GLfloat y, GLfloat z);
void glVertexAttrib4f(GLint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
void glVertexAttrib1fv(GLint index, const GLfloat *values);
void glVertexAttrib2fv(GLint index, const GLfloat *values);
void glVertexAttrib3fv(GLint index, const GLfloat *values);
void glVertexAttrib4fv(GLint index, const GLfloat *values);
glVertexAttrib1f
和 glVertexAttrib1fv
在通用顶点属性中加载(x, 0.0, 0.0, 1.0);
glVertexAttrib2f
和 glVertexAttrib2fv
在通用顶点属性中加载(x, y, 0.0, 1.0);
glVertexAttrib4f
和 glVertexAttrib3fv
在通用顶点属性中加载(x, y, z, 1.0);
glVertexAttrib5f
和 glVertexAttrib4fv
在通用顶点属性中加载(x, y, z, w);
顶点数组
顶点数组指定每个顶点的属性 ,是保存在 应用程序 地址空间 (OpenGL ES 称为客户空间
) 的缓冲区。
用 glVertexAttribPointer
或 glVertexAttribIPointer
函数指定。
- 在 一个缓冲区 中存储所有顶点属性—— 结构数组
- 在 单独的缓冲区 中保存 每个顶点 的属性—— 数组结构
性能上, 结构数组 的分配方法 在OpenGL ES 3.0的硬件实现中更高效。
原因是,每个顶点的属性数据可以顺序方式读取,这最有可能造成高效的内存访问方式。
缺点 是在需要修改特定属性时,将 造成顶点缓冲区跨距更新。当顶点缓冲区以缓冲区对象提供时,需要 重新加载整个顶点属性缓冲区。可以通过 将动态的顶点属性保存在单独的缓冲区 来避免这种效率低下的情况。
结构数组
#define VERTEX_POS_SIZE 3
#define VERTEX_NORMAL_SIZE 3
#define VERTEX_TEXCOORD0_SIZE 2
#define VERTEX_TEXCOORD1_SIZE 2
#define VERTEX_POS_INDX 0
#define VERTEX_NORMAL_INDX 1
#define VERTEX_TEXCOORD0_INDX 2
#define VERTEX_TEXCOORD1_INDX 3
#define VERTEX_POS_OFFSET 0
#define VERTEX_NORMAL_OFFSET 3
#define VERTEX_TEXCOORD0_OFFSET 6
#define VERTEX_TEXCOORD1_OFFSET 8
#define VERTEX_ATTRIB_SIZE (VERTEX_POS_SIZE + VERTEX_NORMAL_SIZE +VERTEX_TEXCOORD0_SIZE +VERTEX_TEXCOORD1_SIZE)
float *p = (float*)malloc(numVertics * VERTEX_ATTRIB_SIZE * sizeof(float));
//顶点属性0:坐标
glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
p);
//顶点属性1 :法线
glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
(p + VERTEX_NORMAL_OFFSET));
//顶点属性2 : 纹理坐标1
glVertexAttribPointer(VERTEX_TEXCOORD0_INDX, VERTEX_TEXCOORD0_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
(p + VERTEX_TEXCOORD0_OFFSET));
//顶点属性3 : 纹理坐标2
glVertexAttribPointer(VERTEX_TEXCOORD1_INDX, VERTEX_TEXCOORD1_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
(p + VERTEX_TEXCOORD1_OFFSET));
数组结构
float *position = (float*)malloc(numVertices * VERTEX_POS_SIZE * sizeof(float));
float *normal = (float*)malloc(numVertices * VERTEX_NORMAL_SIZE * sizeof(float));
float *texcoord0 = (float*)malloc(numVertices * VERTEX_TEXCOORD0_SIZE * sizeof(float));
float *texcoord1 = (float*)malloc(numVertices * VERTEX_TEXCOORD1_SIZE * sizeof(float));
//顶点属性0:坐标
glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
position);
//顶点属性1 :法线
glVertexAttribPointer(VERTEX_NORMAL_INDX, VERTEX_NORMAL_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
normal);
//顶点属性2 : 纹理坐标1
glVertexAttribPointer(VERTEX_TEXCOORD0_INDX, VERTEX_TEXCOORD0_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
texcoord0);
//顶点属性3 : 纹理坐标2
glVertexAttribPointer(VERTEX_TEXCOORD1_INDX, VERTEX_TEXCOORD1_SIZE,
GL_FLOAT, GL_FALSE,
VERTEX_ATTRIB_SIZE * sizeof(float),
texcoord1);
顶点属性使用的数据格式
数据格式 空间占用越小,需要的内存带宽就越小
OpenGL ES 3.0 建议使用 GL_HALF_FLOAT
的16位浮点顶点格式。
纹理、坐标、法线、副法线、切向量
等都应该使用 GL_HALF_FLOAT
存储每个分量的候选。
颜色
可以存储为 GL_UNSIGNED_BYTE
,每个顶点颜色具有 4 个分量。
顶点位置
建议用 GL_HALF_FLOAT
,在不可行时,存储为 GL_FLOAT
。
顶点属性在内部保存为 单精度浮点数
, 如果数据类型不是,将被转换为 单精度浮点数
。
GLfloat f;
Glbyte b;
b = (GLfloat) b;
数据转换:
顶点数据格式 | 转换为浮点数 |
---|---|
GL_BYTE | max(c / (27 - 1),-1.0 ) |
GL_UNSIGNED_BYTE | c / (28 - 1) |
GL_SHORT | max(c / (216 - 1),-1.0 ) |
GL_UNSIGNED_SHORT | c / (216 - 1) |
GL_FIXED | c / 216 |
GL_FLOAT | c |
GL_HALF_FLOAT_OES | c |
在顶点着色器中, 也可能按照 整数 的形式访问整数型顶点数据属性,而不转换为浮点数,这种情况使用 glVertexAttribIPointer
函数。
在常量顶点属性和顶点数组之间选择
glEnableVertexAttribArray
和 glDisableVertexAttribArray
分别用于启用和禁用通用顶点属性数组。
glEnableVertexAttribArray(GLint index)
glDisableVertexAttribArray(GLint index)
index : 指定通用顶点属性索引, 范围[0, size - 1]
以下示例是 使用常量和顶点数组属性 绘制三角形
int Init(ESContext *esContext)
{
UserData *userData = (UserData*) esContext → userData;
const vShaderStr[] = "#version 300 es \n"
"layout(location = 0) in vec4 a_color; \n"
"layout(location = 1) in vec4 a_postion; \n"
"out vec4 v_color; \n"
"void main() \n"
"{ \n"
" v_color = a_color; \n"
" gl_Position = a_postion; \n"
"}";
const fShaderStr = "#version 300 es \n"
"precision mediump float; \n"
"in vec4 c_color; \n"
"out vec4 o_fragColor; \n"
"void main() \n"
"{ \n"
" o_fragColor = v_color; \n"
"}";
//创建着色器程序
GLint programObject;
programObject = esLoadProgram(vShaderStr, fShaderStr);
if(program == 0)
{
return GL_FALSE;
}
//保存着色器程序
userData->programObject = programObject;
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
return GL_TRUE;
}
void Draw(ESContect *esContext)
{
UserData *userData =(UserData*) esContext->userData;
//顶点属性常量 颜色
GLfloat color[4] = {1.0f, 0.0f, 0.0f, 1.0f};
//三个顶点坐标
GLfloat vertexPos[3 * 3] =
{
0.0f, 0.5f, 0.0f, //v0
-0.5f, -0.5f, 0.0f, //v1
0.5f, -0.5f, 0.0f //v2
};
glViewport(0, 0, esContext->width, esContext->height);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(userData->programObject);
glVertexAttrib4fv(0, color);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vertexPos);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES. 0 , 3);
glDisableVertexAttribArray(1);
}
在顶点着色器中声明顶点属性变量
通过
in
限定符声明顶点属性,属性也包含一个
布局限定符
。
示例:
layout(location = 0) in vec4 a_postion;
layout(location = 1) in vec2 a_texcoord;
layout(location = 2) in vec3 a_normal;
in
限定符只能用于 非bool值类型
的 标量、浮点向量、整型向量、无符号整形向量、矩阵
。
顶点属性变量 不能 声明为 数组 或者 结构。
在 顶点着色器 中 顶点属性的变量是 只读变量,不能修改。
以下代码编译会报错:
in vec4 a_pos;
uniform vec4 u_v;
void main()
{
a_pos = u_v; // can not assign to a_pos as it is read-only.
}
属性可以在顶点着色器内部声明, 如果 没有使用 就不会 被认为是活动属性。
如果 活动属性 数量大于 GL_MAX_VERTEX_ATTRIBS
, 这个顶点着色器就无法链接。
以下代码展示了如何获得 活动属性 的数量.
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numActiveAttributes);
查看活动属性列表和类型可以用 glGetActiveAttrib
命令查询。
将顶点属性绑定到顶点着色器中的属性变量
通用顶点属性映射到顶点着色器有以下三种方法:
- 索引可以在顶点着色器源代码中用 layout(location = N)限定符指定。
(推荐)
- OpenGL ES 3.0将通过顶点属性索引绑定到属性名称。
- 应用程序可以将顶点属性索引绑定到属性名称.
glBindAttribLocation
命令可用于将通用顶点属性索引绑定到顶点着色器的一个属性变量,在下一次程序链接时生效。
这个调用可以在 顶点着色器链接到程序对象之前 调用,可以绑定任何属性名称。
void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name)
index : 通用顶点属性索引
name : 属性变量名称
另一个选项是让 OpenGL ES 3.0 将属性变量名称绑定到一个通用顶点属性索引。在程序链接时进行。
应用程序可以通过 glGetAttribLocation
命令查询分配的绑定。
void glGetAttribLocation(GLuint program, const GLchar *name)
name : 属性变量名称
顶点缓冲区对象 (VBO)
OpenGL ES 3.0 支持两类缓冲区对象,
- 数组缓冲区对象
GL_ARRAY_BUFFER
- 元素数组缓冲区对象
GL_ELEMENT_ARRAY_BUFFER
以下是创建和绑定顶点缓冲区对象的示例
void initVertexBuffterObjects(vertex_t *vertexBuffer, GLushort *indices, GLuint numVertices,
GLuint numIndices, GLuint *vboIds )
{
//获取vboIds中两个未使用的缓冲区对象名称
glGenBuffers(2, vboIds);
//数组缓冲区 用于保存一个或多个顶点属性的索引
glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
//指定数组数据
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(vertex_t), vertexBuffer, GL_STATIC_DRAW);
//元素缓冲区 用于保存一个或多个图元的索引
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[1]);
//指定元素数据数据
glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(GLushort), indices, GL_STATIC_DRAW);
}
上面的代码 创建了两个缓冲区对象: 一个用于保存实际的顶点属性数据, 另一个用以保存组成图元的元素索引。
void glGenBuffer(GLsizei n, GLuint *bufferrs)
n : 返回缓冲区对象名称数量
buffers :指向n个条目的数组指针
void glBufferData(GLenum target, GLuint buffer)
target : GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER....
buffer : 分配给目标作为当前对象的缓冲区对象
glGenBuffer
分配了 n 个 缓冲区对象名称(0 以外的无符号整数
),并在buffers中返回它们。
glBindBuffer
用于指定当前缓冲区对象。第一次绑定缓冲区对象名称时,以默认状态保存,如果分配成功,则分配的对象为目标的当前缓冲区对象。
注意:在 glBindBuffer
之前,并不需要 glGenBuffers
。 应用程序可以用 glBindBuffer
指定一个未使用的缓冲区对象。不过建议OpenGL ES应用程序调用 glGenBuffers
,并使用其返回的缓冲区对象名称,而不是指定它们自己的缓冲区对象名称。
顶点数组数据 或者 元素数组数据 存储 用 glBufferData
命令创建和初始化。
void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage)
target : GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER....
size : 缓冲区数据存储大小,以字节数表示
data : 应用程序提供的缓冲区数据的指针, 可为 NULL
usage : 应用程序将如何使用缓冲区对象中存储的数据的提示
缓冲区对象数据存储内容可以用 glBufferSubData
命令初始化或者更新。
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const coid *data)
target : GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER....
offset : 缓冲区数据存储的偏移
size : 被修改的数据存储字节数
data : 需要被复制到缓冲区独享数据存储的客户数据指针
在用 glBufferData
或者 glBufferSubData
初始化或者更新 缓冲区对象数据存储 之后,客户数据存储不再需要的,
静态的几何图形 可以释放 客户数据存储,减少应用程序消耗的内存。 动态几何图形则无法做到
。
使用和不使用顶点缓冲区对象进行绘制的示例(例 6 - 5):
#include "esUtil.h"
#define VERTEX_POS_SIZE 3
#define VERTEX_COLOR_SIZE 4
#define VERTEX_POS_INDX 0
#define VERTEX_COLOR_INDX 1
void DrawPrimitiveWithoutVBOs(GLfloat *vertices, GLint vtxStride, GLint numIndices, GLushort *indices)
{
GLfloat *vtxBuf = vertices;
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(VERTEX_POS_INDX);
glEnableVertexAttribArray(VERTEX_COLOR_INDX);
glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, vtxStride, vtxBuf);
vtxBuf += VERTEX_POS_SIZE;
glVertexAttribPointer(VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, vtxStride, vtxBuf);
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices);
glDisableVertexAttribArray(VERTEX_POS_INDX);
glDisableVertexAttribArray(VERTEX_COLOR_INDX);
}
void DrawPrimitiveWithVBOs(ESContext *esContext, GLint numVertices, GLfloat *vtxBuf,
GLint vtxStride, GLint numIndices, GLushort *indices)
{
UserData *userData = (UserData*)esContext->userData;
GLuint offset = 0;
//vboIds[0] : 保存顶点属性数据
//vboIds[1] : 保存元素索引
if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0)
{
//只在第一次绘制时分配
glGenBuffers(2, userData->boIds);
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
glBufferData(GL_ARRAY_BUFFER, vtxStride * numIndices, vtxBuf, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numIndices, indices, GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
glEnableVertexAttribArray(VERTEX_POS_INDX);
glEnableVertexAttribArray(VERTEX_COLOR_INDX);
glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, vtxStride, (const void*)offset);
offset += VERTEX_POS_SIZE * sizeof(GLfloat);
glVertexAttribPointer(VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, vtxStride, (const void*)offset);
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0);
glDisableVertexAttribArray(VERTEX_POS_INDX);
glDisableVertexAttribArray(VERTEX_COLOR_INDX);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void Draw(ESContext *esContext)
{
UserData *userData = (UserData*)esContext->userData;
//三个顶点
GLfloat vertices[3 * VERTEX_POS_SIZE + VERTEX_COLOR_SIZE] =
{
-0.5f, 0.5f, 0.0f, // v0
1.0f, 0.0f, 0.0f, 1.0f, //c0
-1.0f, -0.5f, 0.0f, //v1
0.0f, 1.0f, 0.0f, 1.0f, //c1
0.0f, -0.5f, 0.0f, //v2
0.0f, 0.0f, 1.0f, 1.0f //c2
};
//索引缓冲数据
GLushort indices[3] = { 0, 1, 2 };
glViewport(0, 0, esContext->width, esContext->height);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(userData->programObject);
glUniform1f(userData->offsetLoc, 0.0f);
DrawPrimitiveWithoutVBOs(vertices, sizeof(GLfloat) * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE), 3, indices);
glUniform1f(userData->offsetLoc, 1.0f);
DrawPrimitiveWithVBOs(esContext, 3, vertices, sizeof(GLfloat) * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE), 3, indices);
}
每个属性使用一个缓冲区对象绘制(例 6 - 6):
#include "esUtil.h"
#define VERTEX_POS_SIZE 3 // x, y, and z
#define VERTEX_COLOR_SIZE 4 // r, g, b, and a
#define VERTEX_POS_INDX 0
#define VERTEX_COLOR_INDX 1
void DrawPrimitiveWithVBOs(ESContext *esContext, GLint numVertices, GLfloat **vtxBuf,
GLint *vtxStrides, GLint numIndices, GLushort *indices)
{
UserData *userData = (UserData*)esContext->userData;
//vboId[0] : 用于存储 顶点位置
//vboId[1] : 用于存储 顶点颜色
//vboId[2] : 用于存储 元素索引
if (userData->vboIds[0] == 0 && userData->vboIds[1] == 0 && userData->vboIds[2] == 0)
{
//只在第一次绘制时分配
glGenBuffers(3, userData->vboIds);
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
glBufferData(GL_ARRAY_BUFFER, vtxStrides[0] * numIndices, vtxBuf[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[1]);
glBufferData(GL_ARRAY_BUFFER, vtxStrides[1] * numIndices, vtxBuf[1], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numIndices, indices, GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
glEnableVertexAttribArray(VERTEX_POS_INDX);
glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, vtxStrides[0], 0);
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[1]);
glEnableVertexAttribArray(VERTEX_COLOR_INDX);
glVertexAttribPointer(VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, vtxStrides[1], 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2]);
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0);
glDisableVertexAttribArray(VERTEX_POS_INDX);
glDisableVertexAttribArray(VERTEX_COLOR_INDX);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
在应用程序结束使用缓冲区对象之后,可以用 glDeleteBuffers
命令删除它们。
void glDeleteBuffers(GLsizei n, const GLuint *buffers)
n : 删除的缓冲区对象数量
buffers : 包含要删除的缓冲区对象的有n个元素的数组
顶点数组对象(VAO)
要创建顶点数组对象,可以使用 glGenVertexArrays
。
void glGenVertexArrays(GLsizei n, GLuint *arrays)
n : 要返回的顶点数组对象名称的数量
arrays : 指向一个n个元素的数组的指针
void glBindVertexArray(GLuint array)
array : 被指定得为当前顶点数组对象的对象
用顶点数组绘图
#include "esUtil.h"
#define VERTEX_POS_SIZE 3 // x, y, and z
#define VERTEX_COLOR_SIZE 4 // r, g, b, and a
#define VERTEX_POS_INDX 0
#define VERTEX_COLOR_INDX 1
#define VERTEX_STRIDE (sizeof(GLfloat) * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE))
int Init(ESContext *esContext)
{
UserData *userData = (UserData*)esContext->userData;
const char vShaderStr = "#version 300 es \n"
"layout(location = 0) in vec4 a_position; \n"
"layout(location = 1) in vec4 a_color; \n"
"out vec4 v_color; \n"
"void main() \n"
"{ \n"
" v_color = a_color; \n"
" gl_Position = a_positon; \n"
"}";
const char fShaderStr = "#version 300 es \n"
"percision mediump float; \n"
"in vec4 v_color; \n"
"out vec4 o_fragColor; \n"
"void main() \n"
"{ \n"
" o_fragColor = v_color; \n"
"}";
GLuint programObject;
GLfloat vertices[3 * (VERTEX_POS_SIZE + VERTEX_COLOR_SIZE)] =
{
-0.5f, 0.5f, 0.0f, // v0
1.0f, 0.0f, 0.0f, 1.0f, //c0
-1.0f, -0.5f, 0.0f, //v1
0.0f, 1.0f, 0.0f, 1.0f, //c1
0.0f, -0.5f, 0.0f, //v2
0.0f, 0.0f, 1.0f, 1.0f //c2
};
GLushort indices[3] = { 0, 1, 2 };
//创建着色器程序
programObject = esLoadProgram(vShaderStr, fShaderStr);
if (programObject == 0) {
return GL_FALSE;
}
//保存着色器程序
userData->programObject = programObject;
glGenBuffers(2, userData->vboIds);
glBindBuffer(GL_ARRAY_BUFFER, &userData->vboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, &userData->vboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//创建顶点数组对象
glGenVertexArrays(1, &userData->vaoId);
//绑定顶点数组对象 并 设置·顶点属性
glBindVertexArray(userData->vaoId);
glBindBuffer(GL_ARRAY_BUFFER, &userData->vboIds[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
glEnableVertexAttribArray(VERTEX_POS_INDX);
glEnableVertexAttribArray(VERTEX_COLOR_INDX);
glVertexAttribPointer(VERTEX_POS_INDX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, VERTEX_STRIDE, (const void *)0);
glVertexAttribPointer(VERTEX_COLOR_INDX, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, VERTEX_STRIDE, (const void *)(VERTEX_POS_SIZE * VERTEX_COLOR_SIZE));
//重置顶点数组对象
glBindVertexArray(0);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
return GL_TRUE;
}
void Draw(ESContext *esContext)
{
UserData *userData = (UserData*)esContext->userData;
glViewport(0, 0, esContext->width, esContext->height);
glClearColor(GL_COLOR_BUFFER_BIT);
glUseProgram(userData->programObject);
//绑定顶点数组对象
glBindVertexArray(userData->vaoId);
//根据顶点数组对象属性绘制
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (const void*)0);
//返回默认的顶点数组对象
glBindVertexArray(0);
}
在应用程序结束使用缓冲区对象之后,可以用 glDeleteArrays
命令删除它们。
void glDeteletArrays(GLsizei n, GLuint *arrays)
n : 要删除的顶点数组对象的数量
arrays : 包含需要删除的顶点数组的有n个元素的数组
映射缓冲区对象
应用程序映射缓冲区 不使用 glBufferData
或者 glBufferSubData
加载的理由:
- 映射缓冲区可以减少应用程序的内存占用,因为可能只需要存储数据的一个副本。
- 在使用共享内存的架构上,映射缓冲区返回GPU存储的地址空间的直接指针。
通过映射缓冲区,应用程序可以避免复制步骤,从而实现更好的性能。
glMapBufferRange
命令返回指向所有或者一部分缓冲区对象数据存储的指针。
如果出现错误,则返回NULL
,glUnmapBuffer
命令可以取消之前的缓冲区映射,取消成功返回 GL_TRUE
,如果缓冲区映射之后已经破坏,则返回 GL_FLASE
。
void glMapBufferRange(Glenum target, GLintptr offset, GLsizeiptr length, GLbitField access)
target : GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER....
offset : 缓冲区数据存储的偏移量,以字节计算
length : 需要映射的缓冲区数据字节数
access : 访问标记的位域组合。
以下为 写入映射缓冲区对象
#include"esUtil.h"
GLfloat *vtxMappedBuf;
GLushort *idxMappedBuf;
glGenBuffers(2, userData->vboIds);
glBindBuffer(GL_ARRAY_BUFFER, userData->vboIds[0]);
glBufferData(GL_ARRAY_BUFFER, vtxStride * numVertices, NULL, GL_STATIC_DRAW);
vtxMappedBuf = (GLfloat*)glMapBufferRange(GL_ARRAY_BUFFER, 0, vtxStride*numVertices,
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
if (vtxMappedBuf == NULL) {
esLogMessage("Error mapping vertex buffer object");
return;
}
//复制数据到映射缓冲区
memcpy(vtxMappedBuf, vtxBuf, vtxStride * numVertics);
//取消数组缓冲区对象映射
if (glUnmapBuffer(GL_ARRAY_BUFFER) == GL_FALSE)
{
esLogMessage("Error unmapping array buffer object.");
return;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * numIndices, NULL, GL_STATIC_DRAW);
idxMappedBuf = (GLushort*)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLushort) * numIndices,
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
if (idxMappedBuf == NULL)
{
esLogMessage("Error mapping element buffer object.");
return;
}
//复制数据到映射缓冲区
memcpy(idxMappedBuf, indices, sizeof(GLushort) * numIdices);
//取消元素数组缓冲区对象映射
if (glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER) == GL_FALSE)
{
esLogMessage("Error unmapping element buffer object");
return;
}
刷新映射的缓冲区
应用程序可能希望用 glMapBufferRange
来映射缓冲区对象的一个范围,但是只更新部分子区域,
可以用 GL_MAP_FLUSH_EXPLICIT_BIT
和 GL_MAP_WRITE_BIT
组合映射。
当完成部分更新时,可以用 glFlushMappedBufferRange
指出这个事实。
如果没有明确的调用 glFlushMappedBufferRange
刷新修改后的区域,它的内容将是未定义的。
void *glFlushMappedBufferRange(GLenum target,Glintptr offset,GLsizeiptr length)
target : GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER....
offset : 从映射缓冲区七十点的偏移量,以字节数表示
length: 从偏移点开始刷新的缓冲区字节数
复制缓冲区对象
到此,我们已知道 glBufferData
、glBufferSubData
、glMapBufferRange
加载缓冲区对象。
OpenGL ES 3.0 还可以用 glCopyBufferSubData
从一个缓冲区对象将数据完全复制到设备。
void glCopyBufferSubData(GLenum readtarget, GLenum writetarget,
GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size)
readtarget : 读取的缓冲区对象目标
writetarget :写入的缓冲区对象目标
readoffset : 需要复制的读缓冲区数据中的偏移量,以字节表示
writeoffset : 需要渎职的写缓冲区数据中的偏移量,以字节表示
size : 从读缓冲区数据都知道写缓冲区数据的字节数
调用 glCopyBufferSubData
将从绑定的 readtarget
的缓冲区复制指定的字节到 writetarget
,缓冲区绑定更具每个目标的最后一次 glBindBuffer
调用确定。
小结
本文介绍了在OpenGL ES 3.0中指定顶点属性和数据的方法:
- 如何使用
glVertexAttrib*
函数指定常量顶点属性和用glVertexAttrib[I]Pointer
函数指定顶点数组。 - 如何在顶点缓冲区对象中创建和存储顶点属性以及元素数据。
- 顶点数组状态在顶点数组对象中如何封装,以及如何使用
VAO
(顶点数组对象)改进性能。 - 加载缓冲区对象数据的各种方法:
glBuffer[Sub]Data
、glMapBufferRange
和glCopyBufferSubData
.