前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >顶点属性、顶点数组和缓冲区对象

顶点属性、顶点数组和缓冲区对象

作者头像
103style
发布2022-12-19 13:18:38
7330
发布2022-12-19 13:18:38
举报

转载请以链接形式标明出处: 本文出自:103style的博客

OpenGL ES 3.0学习汇总


指定顶点属性数据

所有OpenGL ES 3.0实现必须支持最少16个顶点属性。 以下代码实现了如何查询OpenGL ES 3.0实现真正支持的顶点属性数量。

代码语言:javascript
复制
GLint maxVertexAttribs;
glGetInterv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
常量顶点属性

常量顶点属性对于一个图元的所有顶点都相同,所以对一个图元的所有顶点只需指定一个值。 可以用以下函数指定:

代码语言:javascript
复制
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);

glVertexAttrib1fglVertexAttrib1fv 在通用顶点属性中加载(x, 0.0, 0.0, 1.0); glVertexAttrib2fglVertexAttrib2fv 在通用顶点属性中加载(x, y, 0.0, 1.0); glVertexAttrib4fglVertexAttrib3fv 在通用顶点属性中加载(x, y, z, 1.0); glVertexAttrib5fglVertexAttrib4fv 在通用顶点属性中加载(x, y, z, w);

顶点数组

顶点数组指定每个顶点的属性 ,是保存在 应用程序 地址空间 (OpenGL ES 称为客户空间) 的缓冲区。 用 glVertexAttribPointerglVertexAttribIPointer 函数指定。

  • 一个缓冲区 中存储所有顶点属性—— 结构数组
  • 单独的缓冲区 中保存 每个顶点 的属性—— 数组结构

性能上, 结构数组 的分配方法 在OpenGL ES 3.0的硬件实现中更高效。 原因是,每个顶点的属性数据可以顺序方式读取,这最有可能造成高效的内存访问方式。 缺点 是在需要修改特定属性时,将 造成顶点缓冲区跨距更新。当顶点缓冲区以缓冲区对象提供时,需要 重新加载整个顶点属性缓冲区。可以通过 将动态的顶点属性保存在单独的缓冲区 来避免这种效率低下的情况。

结构数组

代码语言:javascript
复制
 #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));

数组结构

代码语言:javascript
复制
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

顶点属性在内部保存为 单精度浮点数, 如果数据类型不是,将被转换为 单精度浮点数

代码语言:javascript
复制
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 函数。

在常量顶点属性和顶点数组之间选择

glEnableVertexAttribArrayglDisableVertexAttribArray 分别用于启用和禁用通用顶点属性数组。

代码语言:javascript
复制
glEnableVertexAttribArray(GLint index)
glDisableVertexAttribArray(GLint index)

index : 指定通用顶点属性索引, 范围[0, size - 1]

以下示例是 使用常量和顶点数组属性 绘制三角形

代码语言:javascript
复制
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);
}
在顶点着色器中声明顶点属性变量
为绘制一个或者多个图元指定和绑定顶带你属性.png
为绘制一个或者多个图元指定和绑定顶带你属性.png

通过 in 限定符声明顶点属性,属性也包含一个 布局限定符。 示例:

代码语言:javascript
复制
layout(location = 0) in vec4 a_postion;
layout(location = 1) in vec2 a_texcoord;
layout(location = 2) in vec3 a_normal;

in 限定符只能用于 非bool值类型标量、浮点向量、整型向量、无符号整形向量、矩阵。 顶点属性变量 不能 声明为 数组 或者 结构。 在 顶点着色器 中 顶点属性的变量是 只读变量,不能修改

以下代码编译会报错:

代码语言:javascript
复制
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, 这个顶点着色器就无法链接。

以下代码展示了如何获得 活动属性 的数量.

代码语言:javascript
复制
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numActiveAttributes);

查看活动属性列表和类型可以用 glGetActiveAttrib 命令查询。

将顶点属性绑定到顶点着色器中的属性变量
为绘制一个或者多个图元指定和绑定顶带你属性
为绘制一个或者多个图元指定和绑定顶带你属性

通用顶点属性映射到顶点着色器有以下三种方法:

  • 索引可以在顶点着色器源代码中用 layout(location = N)限定符指定。 (推荐)
  • OpenGL ES 3.0将通过顶点属性索引绑定到属性名称。
  • 应用程序可以将顶点属性索引绑定到属性名称.

glBindAttribLocation 命令可用于将通用顶点属性索引绑定到顶点着色器的一个属性变量,在下一次程序链接时生效。 这个调用可以在 顶点着色器链接到程序对象之前 调用,可以绑定任何属性名称。

代码语言:javascript
复制
void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name)

index : 通用顶点属性索引
name : 属性变量名称

另一个选项是让 OpenGL ES 3.0 将属性变量名称绑定到一个通用顶点属性索引。在程序链接时进行。 应用程序可以通过 glGetAttribLocation 命令查询分配的绑定。

代码语言:javascript
复制
void glGetAttribLocation(GLuint program, const GLchar *name)

name : 属性变量名称
顶点缓冲区对象 (VBO)

OpenGL ES 3.0 支持两类缓冲区对象,

  • 数组缓冲区对象 GL_ARRAY_BUFFER
  • 元素数组缓冲区对象 GL_ELEMENT_ARRAY_BUFFER

以下是创建和绑定顶点缓冲区对象的示例

代码语言:javascript
复制
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);
}

上面的代码 创建了两个缓冲区对象: 一个用于保存实际的顶点属性数据, 另一个用以保存组成图元的元素索引。

代码语言:javascript
复制
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 命令创建和初始化。

代码语言:javascript
复制
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 命令初始化或者更新。

代码语言:javascript
复制
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):

代码语言:javascript
复制
#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):

代码语言:javascript
复制
#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 命令删除它们。

代码语言:javascript
复制
void  glDeleteBuffers(GLsizei n, const GLuint *buffers)

n : 删除的缓冲区对象数量
buffers : 包含要删除的缓冲区对象的有n个元素的数组
顶点数组对象(VAO)

要创建顶点数组对象,可以使用 glGenVertexArrays

代码语言:javascript
复制
void glGenVertexArrays(GLsizei n, GLuint *arrays)

n : 要返回的顶点数组对象名称的数量
arrays : 指向一个n个元素的数组的指针

void glBindVertexArray(GLuint array)

array : 被指定得为当前顶点数组对象的对象

用顶点数组绘图

代码语言:javascript
复制
#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 命令删除它们。

代码语言:javascript
复制
void glDeteletArrays(GLsizei n, GLuint *arrays)

n : 要删除的顶点数组对象的数量
arrays :  包含需要删除的顶点数组的有n个元素的数组
映射缓冲区对象

应用程序映射缓冲区 不使用 glBufferData 或者 glBufferSubData 加载的理由:

  • 映射缓冲区可以减少应用程序的内存占用,因为可能只需要存储数据的一个副本。
  • 在使用共享内存的架构上,映射缓冲区返回GPU存储的地址空间的直接指针。

通过映射缓冲区,应用程序可以避免复制步骤,从而实现更好的性能。

glMapBufferRange 命令返回指向所有或者一部分缓冲区对象数据存储的指针。 如果出现错误,则返回NULLglUnmapBuffer 命令可以取消之前的缓冲区映射,取消成功返回 GL_TRUE,如果缓冲区映射之后已经破坏,则返回 GL_FLASE

代码语言:javascript
复制
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 : 访问标记的位域组合。

以下为 写入映射缓冲区对象

代码语言:javascript
复制
#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_BITGL_MAP_WRITE_BIT 组合映射。 当完成部分更新时,可以用 glFlushMappedBufferRange 指出这个事实。 如果没有明确的调用 glFlushMappedBufferRange 刷新修改后的区域,它的内容将是未定义的。

代码语言:javascript
复制
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: 从偏移点开始刷新的缓冲区字节数
复制缓冲区对象

到此,我们已知道 glBufferDataglBufferSubDataglMapBufferRange 加载缓冲区对象。 OpenGL ES 3.0 还可以用 glCopyBufferSubData 从一个缓冲区对象将数据完全复制到设备。

代码语言:javascript
复制
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]DataglMapBufferRangeglCopyBufferSubData.
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • OpenGL ES 3.0学习汇总
  • 指定顶点属性数据
    • 常量顶点属性
      • 顶点数组
        • 顶点属性使用的数据格式
          • 在常量顶点属性和顶点数组之间选择
            • 在顶点着色器中声明顶点属性变量
              • 将顶点属性绑定到顶点着色器中的属性变量
                • 顶点缓冲区对象 (VBO)
                  • 顶点数组对象(VAO)
                    • 映射缓冲区对象
                      • 刷新映射的缓冲区
                        • 复制缓冲区对象
                          • 小结
                          相关产品与服务
                          对象存储
                          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档