前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NDK OpenGLES3.0 开发(四):VBO、EBO 和 VAO

NDK OpenGLES3.0 开发(四):VBO、EBO 和 VAO

作者头像
字节流动
发布2020-06-03 10:15:38
9870
发布2020-06-03 10:15:38
举报
文章被收录于专栏:字节流动字节流动

VBO 和 EBO

VBO(Vertex Buffer Object)是指顶点缓冲区对象,而 EBO(Element Buffer Object)是指图元索引缓冲区对象,VAO 和 EBO 实际上是对同一类 Buffer 按照用途的不同称呼。

OpenGLES2.0 编程中,用于绘制的顶点数组数据首先保存在 CPU 内存,在调用 glDrawArrays 或者 glDrawElements 等进行绘制时,需要将顶点数组数据从 CPU 内存拷贝到显存。

但是很多时候我们没必要每次绘制的时候都去进行内存拷贝,如果可以在显存中缓存这些数据,就可以在很大程度上降低内存拷贝带来的开销。

OpenGLES3.0 编程中, VBO 和 EBO 的出现就是为了解决这个问题。

VBO 和 EBO 的作用是在显存中提前开辟好一块内存,用于缓存顶点数据或者图元索引数据,从而避免每次绘制时的 CPU 与 GPU 之间的内存拷贝,可以改进渲染性能,降低内存带宽和功耗。

OpenGLES3.0 支持两类缓冲区对象:顶点数组缓冲区对象、图元索引缓冲区对象。

GL_ARRAY_BUFFER 标志指定的缓冲区对象用于保存顶点数组,GL_ELEMENT_ARRAY_BUFFER 标志指定的缓存区对象用于保存图元索引。

VBO(EBO)的创建和更新:

代码语言:javascript
复制
// 创建 2 个 VBO(EBO 实际上跟 VBO 一样,只是按照用途的另一种称呼)
glGenBuffers(2, m_VboIds);

// 绑定第一个 VBO,拷贝顶点数组到显存
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// 绑定第二个 VBO(EBO),拷贝图元索引数据到显存
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

GL_STATIC_DRAW 标志标识缓冲区对象数据被修改一次,使用多次,用于绘制。

本例中顶点着色器和片段着色器增加 color 属性:

代码语言:javascript
复制
//顶点着色器
#version 300 es                            
layout(location = 0) in vec4 a_position;   // 位置变量的属性位置值为 0 
layout(location = 1) in vec3 a_color;      // 颜色变量的属性位置值为 1
out vec3 v_color;                          // 向片段着色器输出一个颜色                          
void main()                                
{                                          
    v_color = a_color;                     
    gl_Position = a_position;              
};
代码语言:javascript
复制
//片段着色器
#version 300 es
precision mediump float;
in vec3 v_color;
out vec4 o_fragColor;
void main()
{
    o_fragColor = vec4(v_color, 1.0);
}

顶点数组数据和图元索引数据:

代码语言:javascript
复制
// 4 vertices, with(x,y,z) ,(r, g, b, a) per-vertex
GLfloat vertices[] =
        {
                -0.5f,  0.5f, 0.0f,       // v0
                1.0f,  0.0f, 0.0f,        // c0
                -0.5f, -0.5f, 0.0f,       // v1
                0.0f,  1.0f, 0.0f,        // c1
                0.5f, -0.5f, 0.0f,        // v2
                0.0f,  0.0f, 1.0f,        // c2
                0.5f,  0.5f, 0.0f,        // v3
                0.5f,  1.0f, 1.0f,        // c3
        };
// Index buffer data
GLushort indices[6] = { 0, 1, 2, 0, 2, 3};

VBO更新后内存中的数据结构

由于顶点位置和颜色数据在同一个数组里,一起更新到 VBO 里面,所以需要知道 2 个属性的步长和偏移量。

为获得数据队列中下一个属性值(比如位置向量的下个 3 维分量)我们必须向右移动 6 个 float ,其中 3 个是位置值,另外 3 个是颜色值,那么步长就是 6 乘以 float 的字节数(= 24 字节)。

同样,也需要指定顶点位置属性和颜色属性在 VBO 内存中的偏移量。

对于每个顶点来说,位置顶点属性在前,所以它的偏移量是 0 。而颜色属性紧随位置数据之后,所以偏移量就是 3 * sizeof(GLfloat) ,用字节来计算就是 12 字节。

使用 VBO 和 EBO 进行绘制。

代码语言:javascript
复制
glUseProgram(m_ProgramObj);

//不使用 VBO 的绘制
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), vertices);

glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (vertices + 3));

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

//使用 VBO 的绘制
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)(3 *sizeof(GLfloat)));

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);

VAO

VAO(Vertex Array Object)是指顶点数组对象,主要用于管理 VBO 或 EBO ,减少 glBindBuffer 、glEnableVertexAttribArray、 glVertexAttribPointer 这些调用操作,高效地实现在顶点数组配置之间切换

VAO 与 VBO 之间的关系

基于上小节的例子创建 VAO :

代码语言:javascript
复制
// 创建并绑定 VAO
glGenVertexArrays(1, &m_VaoId);
glBindVertexArray(m_VaoId);

// 在绑定 VAO 之后,操作 VBO ,当前 VAO 会记录 VBO 的操作
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, (3+3)*sizeof(GLfloat), (const void *)(3 *sizeof(GLfloat)));

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[1]);


glBindVertexArray(GL_NONE);

使用 VAO 进行绘制:

代码语言:javascript
复制
// 是不是精简了很多?
glUseProgram(m_ProgramObj);

glBindVertexArray(m_VaoId);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节流动 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • VBO 和 EBO
  • VAO
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档