0%

【光栅化渲染器】(一)环境配置

之前我们实现过一个光线追踪器,从这一节开始我们要实现一个运行在 CPU 上的简单的光栅化渲染器,并在实现过程中进一步理解渲染管线的流程和各种细节。首先从配置环境和熟悉 OpenGL 开始。

1 环境配置

我们使用 GLFW 和 GLAD 来显示最终渲染的图片,这些可以参照LearnOpenGL CN上的教程来进行配置。

使用 GLM 作为数学库,下载地址:Releases · g-truc/glm · GitHub

也可以使用和光线追踪器中一样的 STB 图像库来输出图片。

2 OpenGL 基础

下面是一个简单的 OpenGL 程序,但展示了所有 OpenGL 程序的通用流程和框架,包含详细注释:

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 <iostream>

// 一些回调函数
// 每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理
// 这里我们在改变窗口大小的时候也改变视口大小
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// 检测是否按下 ESC 键,如果按下则将WindowShouldClose属性设置为true,这样既可退出渲染循环
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}

// 图像设置
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 顶点着色器代码,存放在字符串中
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";

// 片元着色器代码
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";

int main()
{
// 初始化 GLFW,并配置版本信息等
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

// 使用 GLFW 创建一个窗口
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);

// GLAD 用来来管理OpenGL的函数指针,使其在不同版本不同硬件上能够正确运行
// 因此在使用 OpenGL 函数前需要先初始化 GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}


// 创建并编译顶点着色器代码
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
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::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// 创建并编译片元着色器代码
unsigned int 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::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// 将着色器代码链接为一个着色器程序
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// 检查链接错误
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// ;链接完成后即可删除着色器对象
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

// 定义一个三角形
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};

// 创建顶点缓冲区VBA和顶点数组缓冲区VAO
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 绑定顶点数组缓冲
glBindVertexArray(VAO);
// 绑定顶点缓冲,顶点缓冲是GL_ARRAY_BUFFER类型
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 将上面的顶点数据复制到顶点缓冲中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 链接顶点属性,即告诉OpenGL如何解析顶点数据
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

// 解除缓冲区绑定,非必要
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

// 渲染循环
while (!glfwWindowShouldClose(window))
{
// 是否按下ESC关闭窗口
processInput(window);

// 设置清空屏幕缓冲所用的颜色
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
// 用设置的颜色清空颜色缓冲,可选的还有深度缓冲和模板缓冲
glClear(GL_COLOR_BUFFER_BIT);

// 渲染
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);

// 使用双缓冲渲染,屏幕显示前缓冲区,渲染在后缓冲进行,因此每一帧交换缓冲区
glfwSwapBuffers(window);
glfwPollEvents();
}

// 释放资源
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);

glfwTerminate();
return 0;
}

程序运行效果如图:

image-20220519114457379

---- 本文结束 知识又增加了亿点点!----

文章版权声明 1、博客名称:LycTechStack
2、博客网址:https://lz328.github.io/LycTechStack.github.io/
3、本博客的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系博主进行删除处理。
4、本博客所有文章版权归博主所有,如需转载请标明出处。