GLFW入门教程02.05:VAO与VBO

目的

本文介绍使用GLSL绘图。

坐标

想要用GLSL绘图,首先要有对应坐标。

坐标系

OpenGL显示界面中心点为坐标原点,宽度为[-1,1],高度为[-1,1],不论实际宽高为多少都是这个范围。

数组

将三角形坐标转换为数组。

二维三角形的三个顶点为

1
2
3
4
5
float vertices[] = {
-0.5f,-0.5f, 0.0f,
0.5f,-0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

这个是以C++数组的形式存储数据,但是此数据发送到GPU中该如何存储呢?

渲染流程

渲染流程

图片来自LearnOpenGL,这个教程比Nehe OpenGL更新更好,推荐。

VBO

顶点数据数组传递至显卡后,存储在GPU显存上的一段存储空间对象里面,这个对象叫做VBO(Vertex Buffer Object,顶点缓冲对象)。VBO在C++中表现为一个 unsigned int 类型变量,理解成为GPU内存对象的一个ID编号。

VAO

VBO除了表示顶点外,还可以表示颜色。那么问题来了,如果一次渲染多个VBO呢?

VAO(Vertex Array Object,顶点数组对象),用于存储一个Mesh网络所有的顶点属性描述信息。

简单来说,VAO包含一个或多个VBO。

VAO和VBO必须同时出现。

顶点着色器

顶点着色器的作用是把存储在VBO中的顶点数据进行处理并准备绘图。

片段着色器

片段着色器的作用是把已经会好的图进行上色。纹理也是由RGB等颜色组成的,所以纹理填充是在这里实施的。但是没有直接操作片段着色器的接口,所以要把颜色、纹理等通过顶点着色器传递给片段着色器。

代码

指定GL版本

首先告诉GLFW当前使用的是OpenGL 3+ API

1
2
3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

初始化glad

初始化glad的glfw接口,通过glad我们可以使用几乎所有的OpenGL相关的API。

1
2
3
4
5
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
fprintf(stderr, "Failed to initialize GLAD\n");
return -1;
}

顶点着色器

顶点着色器是类C代码,以字符串形式提供

1
2
3
4
5
6
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";

片段着色器

片段着色器是类C代码,以字符串形式提供

1
2
3
4
5
6
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";

着色器加载

加载着色器代码,编译代码,检查编译状态,使用着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// vertex shader 创建并编译顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors 检测是否编译成功
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
fprintf(stderr, "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n");
}
// fragment shader 创建并编译片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors 检测是否编译成功
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
fprintf(stderr, "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n");
}
// link shaders 创建并链接着色器程序对象
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors 检测链接是否成功
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
fprintf(stderr, "ERROR::SHADER::PROGRAM::LINKING_FAILED\n");
}
glDeleteShader(vertexShader); //删除顶点着色器
glDeleteShader(fragmentShader); //删除片段着色器

三角形

定义三角形顶点数组

1
2
3
4
5
float vertices[] = {
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
0.0f, 0.5f, 0.0f // top left
};

创建VAO和VBO

1
2
3
unsigned int VAO,VBO;
glGenBuffers(1, &VBO);
glGenVertexArrays(1,&VAO);

使用VAO和VBO

1
2
3
4
5
6
glBindVertexArray(VAO);
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都绑定到此VAO中。当然,目前只有一个VBO。

绑定VBO,并将数组内容传递给VBO,但是此时VBO只是知道有一个长度为9数组。

我们还需要告诉VBO如何使用这个数组,即3个一组使用,每个元素都是float。

显示

1
2
3
4
5
glUseProgram(shaderProgram);	//使用着色器程序
glBindVertexArray(VAO); // 绑定VAO seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized

glDrawArrays(GL_TRIANGLES, 0, 3);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //线框模式GL_LINE / 填充模式GL_FILL

先告知使用着色器程序,然后使用VAO,开始绘图,绘图模式为轮廓模式。

释放资源

1
2
3
glDeleteBuffers(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);

注意这里的释放顺序。

效果

编译运行

运行效果


GLFW入门教程02.05:VAO与VBO
https://blog.jackeylea.com/glfw/how-to-use-vao-and-vbo/
作者
JackeyLea
发布于
2024年8月12日
许可协议