OpenGL教程:3. 绘制一个矩形与EBO

上一节我们实现了一个三角形的绘制。现在思考下如何绘制一个矩形?因为OpenGL中最小的绘制片元是一个三角面片(不考虑点和线哦),所以答案是绘制两个三角形就是一个矩形了。

顶点数据

首先确定顶点的浮点数据:

1
2
3
4
5
6
7
8
float 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, // 右上角
};

因为是两个三角形,所以就有六个顶点。

VAO和VBO

然后定义矩形的vao和vbo,并解释数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//initialize vbo
unsigned int vbo;
glGenBuffers(1, &vbo); //spawn a new vbo
glBindBuffer(GL_ARRAY_BUFFER, vbo); //bind vbo with ARRAY_BUFFER
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW); //transport vertices data to buffer memory

//initialize vao
unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

//set vertices pointer
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);
glEnableVertexAttribArray(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
   //initialize shader
const char *vshSource = "#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";
const char *fshSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(0.1f, 0.4f, 0.6f, 1.0f);\n"
"}\n\0";

//compile vshader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vshSource, nullptr);
glCompileShader(vertexShader);

//compile fshader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fshSource, nullptr);
glCompileShader(fragmentShader);

//spawn shader program
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

绘制

绘制阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
while (!glfwWindowShouldClose(window)) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(shaderProgram);

glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);

glfwSwapBuffers(window);
glfwPollEvents();
}

image-20230119152557222

代码

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//
// Created by inver on 2022/9/11.
//

#include <iostream>
#include <cmath>
#include "glad/glad.h"
#include "GLFW/glfw3.h"

int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

GLFWwindow *window = glfwCreateWindow(800, 600, "矩形", nullptr, nullptr);
if (window == nullptr) {
std::cout << "Error: Fail to create window! \n";
glfwTerminate();
return -1;
}

glfwMakeContextCurrent(window);

if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
std::cout << "Error: Fail to initialize GLAD! \n";
return -1;
}

//End initialize glfwSource and glad

float 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, // 右上角
};

//initialize vbo
unsigned int vbo;
glGenBuffers(1, &vbo); //spawn a new vbo
glBindBuffer(GL_ARRAY_BUFFER, vbo); //bind vbo with ARRAY_BUFFER
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW); //transport vertices data to buffer memory
//GL_STATIC_DRAW means static data (not dynamic)

//initialize vao
unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

//set vertices pointer
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);

//initialize shader
const char *vshSource = "#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";
const char *fshSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(0.1f, 0.4f, 0.6f, 1.0f);\n"
"}\n\0";

//compile vshader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vshSource, nullptr);
glCompileShader(vertexShader);

//compile fshader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fshSource, nullptr);
glCompileShader(fragmentShader);

//spawn shader program
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

while (!glfwWindowShouldClose(window)) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(shaderProgram);

glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);

glfwSwapBuffers(window);
glfwPollEvents();
}

glDeleteProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glfwTerminate();
}

优化思路:采用EBO

我们在绘制矩形的时候发现,我们多用了两个顶点。那有没有办法在绘制四个顶点的矩形呢?答案是采用EBO(Element Buffer Object),即元素缓冲对象。说白了,这东西就是根据你顶点的索引来绘制。有点抽象,但举个例子就好理解了。

正常来说,矩形的顶点应该如下:

1
2
3
4
5
6
float vertices[] = {
0.5f, 0.5f, 0.0f, // 右上角,索引[0]
0.5f, -0.5f, 0.0f, // 右下角,索引[1]
-0.5f, -0.5f, 0.0f, // 左下角,索引[2]
-0.5f, 0.5f, 0.0f // 左上角,索引[3]
};

索引类似于数组下标,但这里是按照三维向量为一个单位的。所以这里有四个索引。

EBO可以按照如下的索引数组来绘制图形:

1
2
3
4
unsigned int indices[] = {
0, 1, 3, //第一个三角形
1, 2, 3 //第二个三角形
};

每一行表示一个三角面片,其值是顶点的索引。这里表示第一个三角面片是由顶点索引0、索引1和索引3组成的,第二个三角面片是由顶点索引1、索引2和索引3组成的。

image-20230119154040949

这种方法更好!

要使用EBO,只需在VAO和VBO后面多增加一个生成EBO的代码:

1
2
3
4
5
6
//initialize ebo
unsigned int ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

然后在绘制的时候,使用glDrawElements函数绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
while (!glfwWindowShouldClose(window)) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(shaderProgram);

glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); //使用glDrawElements
glBindVertexArray(0);

glfwSwapBuffers(window);
glfwPollEvents();
}

glDrawElements函数的定义如下:

1
2
// 绘制模式、索引个数(indices数组的元素个数)、indices数组的类型、indices数组的指针(如果使用了ebo就不需要了,直接nullptr)
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void * indices);

从函数的定义来看,也可以不使用EBO来实现,只需:

1
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices);		//使用glDrawElements

前提是,EBO被禁用了,不然会出现错误!

以下是使用了EBO的代码:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//
// Created by inver on 2022/9/11.
//

#include <iostream>
#include <cmath>
#include "glad/glad.h"
#include "GLFW/glfw3.h"

int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

GLFWwindow *window = glfwCreateWindow(800, 600, "矩形", nullptr, nullptr);
if (window == nullptr) {
std::cout << "Error: Fail to create window! \n";
glfwTerminate();
return -1;
}

glfwMakeContextCurrent(window);

if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
std::cout << "Error: Fail to initialize GLAD! \n";
return -1;
}

//End initialize glfwSource and glad

float 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 // 左上角
};

unsigned int indices[] = {
0, 1, 3, //第一个三角形
1, 2, 3 //第二个三角形
};

//initialize vbo
unsigned int vbo;
glGenBuffers(1, &vbo); //spawn a new vbo
glBindBuffer(GL_ARRAY_BUFFER, vbo); //bind vbo with ARRAY_BUFFER
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW); //transport vertices data to buffer memory
//GL_STATIC_DRAW means static data (not dynamic)

//initialize vao
unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

//set vertices pointer
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);
glEnableVertexAttribArray(0);

//initialize ebo
unsigned int ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

//initialize shader
const char *vshSource = "#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";
const char *fshSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(0.1f, 0.4f, 0.6f, 1.0f);\n"
"}\n\0";

//compile vshader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vshSource, nullptr);
glCompileShader(vertexShader);

//compile fshader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fshSource, nullptr);
glCompileShader(fragmentShader);

//spawn shader program
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

while (!glfwWindowShouldClose(window)) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

glUseProgram(shaderProgram);

glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);

glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glfwTerminate();
}
Author

InverseDa

Posted on

2022-11-03

Updated on

2023-03-30

Licensed under

Comments

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×