理论
VBO
void prepare()
{
//创建一个VBO,但是还没有分配显存
GLuint vbo = 0;
GL_CALL( glGenBuffers(1, &vbo));
cout << "vbo = " << vbo << endl;
//销毁一个VBO
GL_CALL(glDeleteBuffers(1, &vbo));
cout << "delete vbo = " << vbo << endl;
//创建多个VBO
GLuint vbos[] = { 0,0,0 };
GL_CALL(glGenBuffers(3,vbos));
//销毁多个VBO
GL_CALL(glDeleteBuffers(3, vbos));
}
VBO绑定和数据更新
VBO多属性数据存储
#include <iostream>
//#include "thrirdParty/include/GLFW/glfw3.h"
#include "glad/glad.h" //需要先引用glad的头文件。 用于加载 OpenGL 函数指针
#include "GLFW/glfw3.h" // 用于创建窗口和处理输入。
#include <assert.h>
#include "wrapper/checkerror.h"
#include "application/application.h"
using namespace std;
/*
* 目标:学习VBO相关操作
* 1.vbo的创建和销毁
* 2.练习绑定vbo,向里面传输数据
* glBindBuffer
* glBufferData
* 3.练习vbo与多属性数据的存储
* SingleBuffer
* InterLeavedBuffer
*/
void prepareVBO()
{
//创建一个VBO,但是还没有分配显存
GLuint vbo = 0;
GL_CALL( glGenBuffers(1, &vbo));
cout << "vbo = " << vbo << endl;
//销毁一个VBO
GL_CALL(glDeleteBuffers(1, &vbo));
cout << "delete vbo = " << vbo << endl;
//创建多个VBO
GLuint vbos[] = { 0,0,0 };
GL_CALL(glGenBuffers(3,vbos));
//销毁多个VBO
GL_CALL(glDeleteBuffers(3, vbos));
}
void prepare() //准备
{
cout << "prepare " << endl;
float vertices[] = {
//6个点为一个矩形
0.5f,0.5f,0.0f,
0.5f,-0.5f,0.0f,
-0.5f,0.5f,0.0f,
-0.5f,-0.5f,0.0f,
};
//生成一个vbo
GLuint vbo = 0;
GL_CALL(glGenBuffers(1, &vbo));
// 绑定当前VBO,到openGL状态机的当前vbo插槽上。
// vbo: 绑定VBO的编号,0代表不绑定VBO
//GL_ARRAY_BUFFER:表示当前VBO插槽。
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
// 向当前VBO传输数据,也是在开辟内存。
// GL_STATIC_DRAW 模型数据不会频繁改变
// GL_DYNAMIC_DRAW 模型数据会频繁改变
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GL_CALL(glDeleteBuffers(1, &vbo));
}
void prepareSingleBuffer()
{
//1.准备顶点位置数据与颜色数据
float positions[] = {
-0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.0f,0.5f,0.0f,
};
float colors[] = {
1.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,
0.0f,0.0f,1.0f,
};
//2.为位置&颜色数据各自生成一个VBO
GLuint posVbo = 0, colorVbo = 0;
GL_CALL(glGenBuffers(1, &posVbo));
GL_CALL(glGenBuffers(1, &colorVbo));
//3.给两个分开的VBO各自填充数据
//positions填充数据
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, posVbo));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW));
//colors填充数据
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, colorVbo));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW));
}
void prepareInterLeavedBuffer()
{
float vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,0.0f,0.0f,
0.5f,-0.5f,0.0f,0.0f,1.0f,0.0f,
0.0f,0.5f,0.0f,0.0f,0.0f,1.0f
};
//2.为位置&颜色数据各自生成一个VBO
GLuint vbo = 0;
GL_CALL(glGenBuffers(1, &vbo));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));
}
void onResize(int width,int height)
{
GL_CALL(glViewport(0, 0, width, height));
cout << "onResize " << endl;
}
void frameBufferSizeCallback(GLFWwindow* win, int width, int height)
{
std::cout << "窗体的最新大小为:" << width << "高度为:" << height << std::endl;
//更新窗体的大小
glViewport(0, 0, width, height);
}
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
// key 字母按键码
// scancode 物理按键码
// action:0抬起1按下2长按
// mods:是否有shift(1)或ctrl(2)
cout << "key = " << key << " scancode = " << scancode << " action = " << action << " mods = " << mods << endl;
if (key == GLFW_KEY_W)
{
//按下了w
}
else if (action == GLFW_PRESS)
{
//按下了
}
else if (action == GLFW_RELEASE)
{
//抬起
}
}
int main(int argc,char**argv)
{
cout << "===================================" << endl;
if (app->init(800,600) == -1)
{
return -1;
}
prepareInterLeavedBuffer();
//设置监听帧缓冲窗口大小回调函数。
//glfwSetFramebufferSizeCallback(win, frameBufferSizeCallback);
//glfwSetKeyCallback(win, keyCallback);
app->setResizeCallBack(onResize);
app->setkeyCallBack(keyCallback);
//设置OpenGL视口以及清理颜色
glViewport(0,0,800,600);
glClearColor(0.2f,0.3f,0.3f,1.0f);
// 3. 执行窗体循环
//
while (app->update() == 0 )
{
//GL_CALL( glClear(1) );//加上gl_call如果产生错误可以打印出来,虽然vs智能提示有问题
GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
//渲染操作
}
// 4. 退出程序前做相关清理
app->destroy();
cout << "===================================" << endl;
const double M_PI = 3.14159265358979323846;
double radians = M_PI / 2; // 90度,转换为弧度
double sineValue = sin(radians);
std::cout << "sin(" << radians << ") = " << sineValue << std::endl;
system("pause");
return 0;
}
VAO
在 OpenGL 中,VAO(Vertex Array Object,顶点数组对象)是一种特殊的对象,它用于封装和存储顶点数组的状态。这种状态包括顶点缓冲区对象(VBO)、顶点属性指针(如位置、纹理坐标、颜色等)以及其他与顶点数组相关的配置,他存放描述信息,专门用来管理VBO。
使用 VAO 的主要目的是:
1. **提高性能**:通过减少状态切换的开销,VAO 可以提高渲染性能。当需要渲染具有相同顶点数组配置的多个对象时,只需切换一次 VAO 状态,而不是每次渲染时都重新配置顶点属性。
2. **简化代码**:VAO 允许开发者将顶点数组的配置(如顶点属性指针)与渲染代码分离,使得渲染代码更加简洁和易于管理。
3. **避免错误**:手动配置顶点属性指针容易出错,而 VAO 可以帮助避免这些错误,因为它可以存储和恢复顶点数组的完整状态。
### 创建和使用 VAO
以下是使用 VAO 的基本步骤:
1. **生成 VAO**:
```cpp
GLuint vao;
glGenVertexArrays(1, &vao);
```
2. **绑定 VAO**:
```cpp
glBindVertexArray(vao);
```
3. **配置顶点属性**:
- 创建并绑定 VBO。
- 配置顶点属性指针。
```cpp
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 配置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
```
4. **解绑 VAO**:
```cpp
glBindVertexArray(0);
```
5. **渲染时使用 VAO**:
当需要渲染使用该 VAO 配置的对象时,只需绑定该 VAO。
```cpp
glBindVertexArray(vao);
// 绘制调用
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
```
### 示例代码
以下是一个完整的示例,展示如何创建和使用 VAO:
```cpp
#include <GL/glew.h>
#include <iostream>
// 顶点数据
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
void setupVertexArrayObject() {
// 生成 VAO 和 VBO
GLuint vao, vbo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
// 绑定 VAO
glBindVertexArray(vao);
// 绑定 VBO
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 配置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 解绑 VAO 和 VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void render() {
// 绑定 VAO
glBindVertexArray(vao);
// 绘制调用
glDrawArrays(GL_TRIANGLES, 0, 3);
// 解绑 VAO
glBindVertexArray(0);
}
int main() {
// 初始化 GLEW 和 OpenGL
// ...
setupVertexArrayObject();
// 渲染循环
while (true) {
// 清除颜色缓冲区
glClear(GL_COLOR_BUFFER_BIT);
// 渲染
render();
// 交换缓冲区
// ...
}
return 0;
}
```
在这个示例中,我们创建了一个 VAO 和一个 VBO,并在 VAO 中配置了顶点属性指针。在渲染时,只需绑定该 VAO,然后调用绘制函数即可。这样可以大大简化渲染代码并提高性能。
void prepareInterLeavedBuffer()
{
float vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,0.0f,0.0f,
0.5f,-0.5f,0.0f,0.0f,1.0f,0.0f,
0.0f,0.5f,0.0f,0.0f,0.0f,1.0f
};
//2.为位置&颜色数据各自生成一个VBO
GLuint vbo = 0;
GL_CALL(glGenBuffers(1, &vbo));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));
//生成VAO并且绑定
GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// 分别将数据放入VAO
//描述位置属性
glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
//颜色属性
//glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),(void*)(3*sizeof(float)));
/*
* glVertexAttribPointer参数详解:
* index
类型:GLuint
描述:指定顶点属性的索引。这个索引对应于着色器中顶点属性的 layout(location = index) 指定的位置。例如,如果你的顶点着色器中有一个位置属性,你可以使用 layout(location = 0) in vec3 position; 来指定它的位置为 0。
size
类型:GLint
描述:指定每个顶点属性的组件数量。例如,一个位置向量通常有 3 个组件(x, y, z),而一个纹理坐标可能有 2 个组件(u, v)。常见的值有 1、2、3、4。
type
类型:GLenum
描述:指定缓冲区中数据的类型。常见的类型包括:
GL_FLOAT:浮点数
GL_INT:整数
GL_UNSIGNED_INT:无符号整数
GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT:其他数值类型
normalized
类型:GLboolean
描述:指定是否对缓冲区中的数据进行归一化。如果设置为 GL_TRUE,则对非浮点数据进行归一化处理,即将整数数据映射到 [-1.0, 1.0](对于有符号类型)或 [0.0, 1.0](对于无符号类型)。如果设置为 GL_FALSE,则不进行归一化,直接使用缓冲区中的值。
stride
类型:GLsizei
描述:指定连续顶点属性之间的步长(以字节为单位)。步长是从一个顶点属性的末尾到下一个顶点属性的起始位置的字节偏移量。如果所有顶点属性都紧密排列,则步长等于一个顶点属性的总字节大小。如果顶点属性之间有间隙,则步长会更大。
pointer
类型:const void*
描述:指定指针偏移量(以字节为单位),用于从绑定的缓冲区对象中指定顶点属性的起始位置。通常设置为 NULL,表示从缓冲区的当前绑定点开始读取数据。如果需要从缓冲区的特定位置开始读取数据,则可以指定非零偏移量。
*/
glBindVertexArray(0); //将VAO进行解绑
}
void prepareInterLeavedBuffer()
{
float vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,0.0f,0.0f,
0.5f,-0.5f,0.0f,0.0f,1.0f,0.0f,
0.0f,0.5f,0.0f,0.0f,0.0f,1.0f
};
//2.为位置&颜色数据各自生成一个VBO
GLuint vbo = 0;
GL_CALL(glGenBuffers(1, &vbo));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW));
//生成VAO并且绑定
GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// 分别将数据放入VAO
//描述位置属性
glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT), (void*)0);
//glBindBuffer(GL_ARRAY_BUFFER, vbo);//只有绑定了vbo,下面的属性描述才于此有关系
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GL_FLOAT),(void*)(3*sizeof(GL_FLOAT)));
glBindVertexArray(0); //将VAO进行解绑
}