通过OpenGL绘制正方形

主要通过使用索引数组传值(GL_ELEMENT_ARRAY_BUFFER)的方式,减少顶点输入

  • 片段着色器FragmentShader.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #ifndef _FRAGMENTSHADER_H_
    #define _FRAGMENTSHADER_H_
    const char * fragmentShaderSource =
    "#version 330 core\n"
    //out:声明输出变量
    "out vec4 FragColor;\n"
    "void main(){\n"
    "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" //输出为固定颜色
    "}\0";
  • 顶点着色器VertexShader.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #ifndef _VERTEXSHADER_H_
    #define _VERTEXSHADER_H_

    const char* vertexShaderSource =
    "#version 330 core\n" //声明版本并使用核心模式
    //in:顶点着色器中声明所有的输入顶点属性,现阶段只关心位置数据,所以只需要设置一个顶点属性。
    //GLSL(着色器语言)有一个向量类型,包含1--4个float分量。
    //layout (location = 0)设定了输入变量的位置值,相当于一个索引,可以在代码中通过glVertexAttribPointer指定传入参数、通过glEnableVertexAttribArray启用数据,放入aPos中。
    "layout (location = 0) in vec3 aPos;\n"
    "void main(){\n"
    //为了设置顶点着色器输出,必须把位置数据赋值给预定义的gl_Position变量,而gl_Position本身是一个vec4类型,在main函数最后需要将三分量的值转为四分量的值
    "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "}\0";

    #endif // !_VERTEXSHADER_H_
  • main函数

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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//绘制正方形
#include <glad\glad.h>
#include <GLFW\glfw3.h>
#include "VertexShader.h"
#include "FragmentShader.h"
#include <iostream>

/*
OpenGL基于3维坐标系,OpenGL的大部分工作就是3D坐标系转为2D坐标系。该转化过程由OpenGL图形渲染管线完成。分为两步:一、3D坐标转2D坐标;二、2D坐标转实际有颜色的像素
*/

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

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(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window." << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}

//顶点着色器:是可编程着色器之一。现代OpenGL需要至少设置一个顶点和一个片段着色器。
unsigned int vertexShader; //vertexShader:用ID来引用着色器对象
vertexShader = glCreateShader(GL_VERTEX_SHADER); //创建一个着色器对象,GL_VERTEX_SHADER:顶点着色器

glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); //参数1:将要编译的着色器;参数2:源码字符串数量;参数3:顶点着色器真正的源码;参数4:指定字符串长度的数组。
glCompileShader(vertexShader);
//检查着色器编译时是否报错
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR:SHADER::VERTEX::COMPOLEATION_FAILED\n" << infoLog << std::endl;
}

//片段着色器:计算像素最后的颜色输出
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPLATION_FAILED\n" << infoLog << std::endl;
}

//将着色器链接到一个用来渲染的着色器程序
//着色器程序对象:是多个着色器合并之后并最终链接完成的版本。如果想要使用着色器,必须链接为一个着色器程序对象,然后渲染对象时激活这个着色器程序。
unsigned int shaderProgram;
shaderProgram = glCreateProgram(); //glCreateProgram:创建一个着色器程序,并返回ID引用
glAttachShader(shaderProgram, vertexShader); //附加着色器
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram); //链接着色器

//检测链接着色器程序是否失败
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADERPROGRAM::COMPOLEATION_FAILED\n" << infoLog << std::endl;
}

//着色器对象链接到程序对象之后,删除着色器对象。
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

//定义一个正方形顶点数组,作为输入发送给图形渲染管线的第一个处理阶段:顶点着色器。其会在GPU上创建内存用于储存顶点数据。
//还需配置OpenGL如何解释该内存,指定其如何发送给显卡。顶点着色器会处理在内存中指定数量的顶点。
//绘制正方形时,使用索引缓冲对象(Element Buffer Object,EBO)用以节省内存。
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
};

unsigned int VBO; //顶点缓冲对象:OpenGL对象都有一个独一无二的ID
glGenBuffers(1, &VBO); //glGenBuffers: 生成一个VBO对象,VBO保存ID。

unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, 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);

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
while (!glfwWindowShouldClose(window)) {
//检测输入
processInput(window);

//渲染
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

//得到一个程序对象,调用useProgram函数,激活这个程序对象。之后,着色器调用和渲染调用都将使用这个程序对象。
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);

glfwSwapBuffers(window);//交换指定窗口双缓冲区的前后缓冲
glfwPollEvents();//该函数会调用相关的窗口和输入回调
}

glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);

glfwTerminate();
return 0;
}

void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
}
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}