Qt+OpenGL入门教程(四)——VBO、VAO和EBO

前面我们已经简单绘制了一个三角形,但这只是个小demo是远远不够的,当顶点数据很多时,解析很麻烦时我们应该如何处理呢?接下来我们来介绍一下在OpenGL开发中帮助我们提升渲染性能的几种数据对象。

注意:所有代码都是基于上一篇修改,看代码时候,一定多看看看我写的注释!

VBO(Vertex Buffer Object)顶点缓冲对象

VBO会在GPU上创建内存,用于存储我们的顶点数据。
VBO的缓冲类型是GL_ARRAY_BUFFER。

代码如下:

widget.cpp

#include "widget.h"GLuint VBO;  // VBOWidget::Widget(QWidget *parent): QOpenGLWidget(parent)
{
}Widget::~Widget()
{
}void Widget::initializeGL()
{// 初始化OpenGL函数,将Qt里面的函数指针指向显卡的函数initializeOpenGLFunctions();glClearColor(0.0f, 0.0f, 0.0f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);shaderProgram.create();shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/gl.vert");shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl.frag");shaderProgram.link();GLfloat vertices[] = {0.0f, 0.5f, 0.0f,0.5f, -0.5f, 0.0f,-0.5f, -0.5f, 0.0f,};// 创建VBOglGenBuffers(1, &VBO);// 绑定VBOglBindBuffer(GL_ARRAY_BUFFER, VBO);// 为当前绑定到target的缓冲区对象创建一个新的数据存储glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 解绑glBindBuffer(GL_ARRAY_BUFFER, 0);
}void Widget::resizeGL(int w, int h)
{glViewport(0, 0, w, h);
}void Widget::paintGL()
{shaderProgram.bind();// 启用顶点属性(允许顶点着色器读取GPU数据)glEnableVertexAttribArray(0);// 绑定顶点缓冲对象glBindBuffer(GL_ARRAY_BUFFER, VBO);// 设置解析规则,GPU能够取到正确的数据供着色器使用glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);glDrawArrays(GL_TRIANGLES, 0, 3);
}

总结:

将顶点数据存储在VBO中可以减少CPU与GPU之间的数据传输次数,提高渲染效率!

VAO(Vertex Array Object)顶点数组对象

VAO用于存储多个顶点属性的状态设置。它包含了一系列函数调用的状态,这些函数调用设置了顶点属性指针和绑定了顶点缓冲区对象(VBO),以便在绘制时使用。

代码如下:

widget.cpp

#include "widget.h"GLuint VBO;  // VBO
GLuint VAO;  // VAOWidget::Widget(QWidget *parent): QOpenGLWidget(parent)
{
}Widget::~Widget()
{
}void Widget::initializeGL()
{// 初始化OpenGL函数,将Qt里面的函数指针指向显卡的函数initializeOpenGLFunctions();glClearColor(0.0f, 0.0f, 0.0f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);shaderProgram.create();shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/gl.vert");shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl.frag");shaderProgram.link();GLfloat vertices[] = {0.0f, 0.5f, 0.0f,0.5f, -0.5f, 0.0f,-0.5f, -0.5f, 0.0f,};// 创建VAOglGenVertexArrays(1, &VAO);// 创建VBOglGenBuffers(1, &VBO);// 绑定VAO(VAO是没有缓冲类型的)glBindVertexArray(VAO);// 绑定VBOglBindBuffer(GL_ARRAY_BUFFER, VBO);// 为当前绑定到target的缓冲区对象创建一个新的数据存储glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 设置顶点属性指针glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);// 启用顶点属性(允许顶点着色器读取GPU数据)glEnableVertexAttribArray(0);// 解绑glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);
}void Widget::resizeGL(int w, int h)
{glViewport(0, 0, w, h);
}void Widget::paintGL()
{shaderProgram.bind();glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);
}

总结:

看完这个代码你会发现,paintGL函数里面缺少了顶点数据的解析,只需要绑定VAO即可。

以下是VAO的作用:

  • 简化顶点属性设置(将顶点属性设置封装在一个对象中,可以更容易地管理和使用顶点属性)
  • 提高渲染效率(通过预定义顶点属性设置状态,可以避免在每次绘制调用时重复设置相同的顶点属性)
  • 提高可读性和维护性(将顶点属性设置组织成一个单独的对象,使代码更具有可读性和维护性)**。

VAO和VBO的关系

  • VBO是纯数据的缓冲区
  • VAO是一个数组,保存每一类顶点属性的解析结果,OpenGL中貌似最多支持16种顶点属性,这里的顶点属性就是glVertexAttribPointer方法的第一个参数指定的,通常0表示顶点坐标,1表示顶点颜色
  • 使用VAO的好处是,你只需要针对VBO做一次解析,将结果存储到VAO中,每一帧渲染使用VAO的指针来访问缓冲区数据,而不需要每一帧都做解析

EBO/IBO(Element/Index Buffer Object)索引缓冲对象

既然有了VBO和VAO,那EBO还有什么用途呢?接下来我们一起研究一下!

EBO主要用来存储顶点的索引信息,那为什么需要存储索引呢?

举个栗子:

假如我们要绘制两个三角形来组成一个矩形(OpenGL主要处理三角形),顶点数据应该是这样的。

GLfloat vertices[] = {0.5f, 0.5f, 0.0f,    // 右上0.5f, -0.5f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f,  // 左下-0.5f, -0.5f, 0.0f,  // 左下-0.5f, 0.5f, 0.0f,   // 左上0.5f, 0.5f, 0.0f,    // 右上
};

细心的你可能会发现有两个点的坐标是重复的,那么这样是不就相当于存储了无用数据嘛?是不是降低了传输效率呢?所以就用到了EBO。

代码如下:

widget.cpp

#include "widget.h"GLuint VBO;  // VBO
GLuint VAO;  // VAO
GLuint EBO;  // EBOWidget::Widget(QWidget *parent): QOpenGLWidget(parent)
{
}Widget::~Widget()
{
}void Widget::initializeGL()
{// 初始化OpenGL函数,将Qt里面的函数指针指向显卡的函数initializeOpenGLFunctions();glClearColor(0.0f, 0.0f, 0.0f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);shaderProgram.create();shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/gl.vert");shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/gl.frag");shaderProgram.link();// 去重后的顶点数据GLfloat vertices[] = {0.5f, 0.5f, 0.0f,0.5f, -0.5f, 0.0f,-0.5f, -0.5f, 0.0f,-0.5f, 0.5f, 0.0f,};// 顶点下标索引GLuint indices[] = {0, 1, 2,0, 2, 3};// 创建VAOglGenVertexArrays(1, &VAO);// 创建VBOglGenBuffers(1, &VBO);// 绑定VAO(VAO是没有缓冲类型的)glBindVertexArray(VAO);// 绑定VBOglBindBuffer(GL_ARRAY_BUFFER, VBO);// 为当前绑定到target的缓冲区对象创建一个新的数据存储glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 设置顶点属性指针glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);// 启用顶点属性(允许顶点着色器读取GPU数据)glEnableVertexAttribArray(0);// 解绑VBOglBindBuffer(GL_ARRAY_BUFFER, 0);// 创建EBOglGenBuffers(1, &EBO);// 绑定EBOglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);// 解绑VAO(在EBO后解绑,paintGL中就不用绑定EBO了)glBindVertexArray(0);
}void Widget::resizeGL(int w, int h)
{glViewport(0, 0, w, h);
}void Widget::paintGL()
{shaderProgram.bind();glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
}

总结:

  • 索引数组的类型必须用GLuint,不要误用GLfloat,否则将无法绘制成功
  • 使用EBO可以减少需要传输到GPU的数据量。相比直接传输顶点数据,使用索引数组可以更有效地管理和重用顶点数据
  • 绘制函数发生变化,不再是glDrawArrays而是glDrawElements

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/798494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全面探究 LangChain Text Splitters

全面探究 LangChain Text Splitters 0. 引言1. 文本拆分器的类型2. 探究各个文本拆分器2-1. Split by HTML header2-2. Split by HTML section2-3. Split by character2-4. Split code2-5. MarkdownHeaderTextSplitter2-6. Recursively split JSON2-7. Recursively split by ch…

力扣---分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1: 输入:head [1,4,3,2,5,2], x 3 输出&a…

【Nginx】配置Nginx实现跨域策略

弹琵琶又见当年镜前你梳头 拨一首满花春秀 今日月下再醉孤酒 雨落枝头年复一年谁白发留 让爱随相思入梦左右 梦见我们还挽着手 🎵 马天宇《青衣》 在现代web开发中,跨源资源共享(CORS,Cross-Origin Resource…

C语言调用Python

目录 1.直接调用python语句 头文件引用 2.调用无参有参函数 1、调用无参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 2、调用有参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 C语言调用python需要我们已经安装好了libpython3的 dev依赖…

DFS序列

什么是DFS序 DFS序是指对一棵树进行DFS时,每个节点被访问到的顺序。DFS序分成两个部分:进入该节点的顺序和退出该节点的顺序。 如何求DFS序 对于DFS中当前节点 1:计数 2:进入当前节点的顺序等于当前计数 3:想所有…

Shell脚本从0到会基础学习(个人学习)

一、前言 本人在找工作的时候,发现有的职位要会shell或者是python其中一种脚本语言。由于博主学过python。所以专门开栏学习,只做个人学习。一天学会那种~ 二、练习 2.1 我的第一个脚本--HelloWorld 代码 运行结果 2.2 输出系统常量和 自定义变量 #!…

5. 4 二重循环将二维数组的某列、某矩形转大写

5. 4 二重循环将二维数组的某列、某矩形转大写 1. 把每一行的b都变成大写 assume cs:codesg,ds:data,ss:stack data segmeNTstr db aaaaabbbbbcccccdb aaaaabbbbbcccccdb aaaaabbbbbcccccdb aaaaabbbbbccccc,$ data endsstack segmentdb 10 dup(0) stack endscodesg SEgments…

一种遥感影像多类变化检测方法

多任务学习孪生网络的遥感影像多类变化检测 马惠1, 刘波2, 杜世宏2 1.河南省国土空间调查规划院,郑州 450016 2.北京大学遥感与地理信息系统研究所,北京 100871 摘要: 精确掌握土地覆盖/利用的变化及变化类型对国土空间规划、生态环境监测、灾害评估等有着重要意义,然而现有…

渲染一帧特效需要多少钱?云渲染特效每帧成本

特效渲染的成本受到诸多因素的影响,每帧的渲染费用是评估整个项目预算的重要依据。随着云渲染技术的发展,其高效率和可伸缩性赢得了业界的广泛关注。对于影视制作公司和独立创作者而言,掌握云渲染特效的单帧成本是管理和优化预算分配的关键。…

PCL 点到三角形的距离(3D)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 给定三角形ABC和点P,设Q为描述ABC上离P最近的点。求Q的一个方法:如果P在ABC内,那么P的正交投影点就是离P最近的点Q。如果P投影在ABC之外,最近的点则必须位于它的一条边上。在这种情况下,Q可以通过计算线段AB、…

设计模式学习笔记 - 设计模式与范式 -行为型:5.策略模式(下):策略模式的应用,如何实现支持给不同大小文件排序的小程序

概述 上篇文章,主要介绍了策略模式的原理和实现,以及如何利用策略模式来移除 if-esle 或者 switch 分支判断逻辑。本章结合 “给文件排序” 的例子,来详细讲下策略模式的设计意图和应用场景。 此外,我还会通过进一步分析、重构&…

算法:计数类dp

文章目录 一、举个栗子例子1:爬楼梯问题例子2:不同路径例子3:计数子序列 二、基本思路三、典型例题一、ACWing:900. 整数划分1、解法一1.1、状态转移方程1.2、参考代码 O(n) 超时 2、解法二:类似完全背包问题1.1、状态…

黑豹程序员-Spring Task实现定时任务

定时任务 项目中,我们有一个特殊的要求,无需人为去触发,而是自动去触发程序。通常有一定的频率,每天,某时等。 实现的四种方式 1、java自身提供定时任务java.util.Timer类,但太过简单,几乎无…

博客部署002-centos安装nginx

1、centos 如何安装nginx? 在CentOS系统上安装Nginx的过程相对直接,通常可以通过系统自带的Yum包管理器来安装。以下是安装Nginx的最新稳定版的步骤: 1.1 更新系统软件包 在安装Nginx之前,首先确保系统软件包是最新的,运行…

如何使用GraphQL和Apollo构建一个宝可梦应用

宝可梦是一个由视频游戏、动画系列与电影、交换卡牌游戏以及其他相关媒体组成的日本媒体特许经营权。 在本文中,我们将使用一个宝可梦GraphQL API,该API提供有关不同宝可梦的数据。 我们将使用Apollo和GraphQL来处理数据获取,以及React来构…

谨慎使用通过光纤传输的HDMI光纤线,存严重缺陷

严重缺陷: 1.只能单向传输 只能单向传输,从一端到另一端,和二极管一样,只能单向传输信号。某些情况你需要变更传输方向时,你将欲哭无泪.传统的HDMI线,不带放大器的,都可以双向传输.网上搜索布…

解密极验滑块验证码的w参数

一、极验请求分析 滑块测试网站入口 我们首先访问极验滑块验证码测试主页,以便获取后续请求中的url。 滑块验证过程抓包 通过抓包工具分析滑块验证过程,发现与第三代滑块验证码相比,第四代简化了验证过程,加密参数w的生成也变得…

【00150】金融理论与实务-2023年10月自考真题

目录 一、单选题 二、多选题 三、判断说明题 四、计算题 五、简答题

Vue3【进阶】

简介 https://cn.vuejs.org/guide/introduction.html 创建vue3工程 【基于 vue-cli创建】 基本和vue-cli的过程类似,只是选择的时候用vue3创建 【基于vite创建】【推荐】 【官网】https://vitejs.cn/ 【可以先去学一下webpack】 步骤 【https://cn.vitejs.…

#QT项目实战(天气预报)

1.IDE:QTCreator 2.实验: 3.记录: (1)调用API的Url a.调用API获取IP whois.pconline.com.cn/ipJson.jsp?iphttp://whois.pconline.com.cn/ipJson.jsp?ip if(window.IPCallBack) {IPCallBack({"ip":&quo…