OpenGL学习笔记——入门篇

OpenGL程序的配置与基本结构

第一步:下载配置

1.GLFW:一个专门针对OpenGL的C语言库
2.GLAD:OpenGL函数指针库
3.stb_image:纹理图片导入
4.GLM:OpenGL数学函数(向量,矩阵等的变换)

第二步:环境配置

创建项目并打开项目属性;
设置 c/c++目录 -> 包含目录;
设置 c/c++目录 -> 库目录;
设置 链接器 -> 输入 -> 附加依赖项(opengl32.lib;glfw3.lib;);
设置 链接器 -> 系统 -> 子系统(窗口);
设置 链接器 -> 高级 -> 入口点(mainCTRStartup);
添加项目已有源文件(glad.c);
新建源文件(stb_image.cpp):#define STB_IMAGE_IMPLEMENTATION #include <stb_image.h>

第三步:编写自定义类

1.Shader:着色器类

简化源代码,对着色器文件进行读取编译
新建头文件(Shader.h):

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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
unsigned int ID;
// 构造函数在动态生成着色器
Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
{
// 1. 从文件中检索顶点/片段/几何着色器源代码
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
std::ifstream gShaderFile;
// 确保 ifstream 对象可以引发异常:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// 打开文件
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// 将文件的缓冲区内容读入流
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// 关闭文件处理程序
vShaderFile.close();
fShaderFile.close();
// 将流转换为字符串
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
// 如果存在几何着色器路径, 也会加载几何着色器
if (geometryPath != nullptr)
{
gShaderFile.open(geometryPath);
std::stringstream gShaderStream;
gShaderStream << gShaderFile.rdbuf();
gShaderFile.close();
geometryCode = gShaderStream.str();
}
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char * fShaderCode = fragmentCode.c_str();
// 2. 编译着色器
unsigned int vertex, fragment;
int success;
char infoLog[512];
// 编译顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// 编译片断着色器
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// 如果给出几何着色, 编译几何着色器
unsigned int geometry;
if (geometryPath != nullptr)
{
const char * gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
checkCompileErrors(geometry, "GEOMETRY");
}
// 着色器程序
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if (geometryPath != nullptr)
glAttachShader(ID, geometry);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// 删除着色器, 因为它们现在已经链接到我们的程序, 不再需要
glDeleteShader(vertex);
glDeleteShader(fragment);
if (geometryPath != nullptr)
glDeleteShader(geometry);

}
// 激活着色器
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// 实用的uniform函数
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
void setVec2(const std::string &name, const glm::vec2 &value) const
{
glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec2(const std::string &name, float x, float y) const
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
}
void setVec3(const std::string &name, const glm::vec3 &value) const
{
glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec3(const std::string &name, float x, float y, float z) const
{
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
}
void setVec4(const std::string &name, const glm::vec4 &value) const
{
glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec4(const std::string &name, float x, float y, float z, float w)
{
glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
}
void setMat2(const std::string &name, const glm::mat2 &mat) const
{
glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
void setMat3(const std::string &name, const glm::mat3 &mat) const
{
glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
void setMat4(const std::string &name, const glm::mat4 &mat) const
{
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
private:
// 检查着色器编译/链接错误的实用功能。
void checkCompileErrors(GLuint shader, std::string type)
{
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n"
<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n"
<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif

2.Camera:摄像机类

合并操作,简化代码
新建头文件(Camera.h)

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
#ifndef CAMERA_H
#define CAMERA_H
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
// 为照相机移动定义了几种可能的选项。用作抽象, 以远离窗口系统的特定输入方法
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
// 默认摄像机数值
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVTY = 0.1f;
const float ZOOM = 45.0f;
// 一个抽象的摄像机类, 处理输入和计算相应的欧拉角度, 向量和矩阵在 OpenGL 中的使用
class Camera
{
public:
// 摄像机的属性
glm::vec3 Position;
glm::vec3 Front;
glm::vec3 Up;
glm::vec3 Right;
glm::vec3 WorldUp;
// 欧拉角
float Yaw;
float Pitch;
// 摄像机选项
float MovementSpeed;
float MouseSensitivity;
float Zoom;
// 使用向量构造的构造函数
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
{
Position = position;
WorldUp = up;
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// 具有标量值的构造函数
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
{
Position = glm::vec3(posX, posY, posZ);
WorldUp = glm::vec3(upX, upY, upZ);
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// 返回使用欧拉角和LookAt矩阵计算的视图矩阵
glm::mat4 GetViewMatrix()
{
return glm::lookAt(Position, Position + Front, Up);
}
// 处理从任何类似键盘输入系统接收到的输入。接受摄像机定义的枚举形式的输入参数 (从窗口系统中抽象出来)
void ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD)
Position += Front * velocity;
if (direction == BACKWARD)
Position -= Front * velocity;
if (direction == LEFT)
Position -= Right * velocity;
if (direction == RIGHT)
Position += Right * velocity;
}
// 处理从鼠标输入系统接收的输入。接受 x 和 y 方向的偏移值。
void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
{
xoffset*= MouseSensitivity;
yoffset*= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
// 确保当Pitch超出界限时, 屏幕不会被翻转
if (constrainPitch)
{
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
}
// 使用更新的欧拉角度更新Front、Right和Up向量
updateCameraVectors();
}
// 处理从鼠标滚轮事件接收的输入。只需要在垂直轮轴上输入
void ProcessMouseScroll(float yoffset)
{
if (Zoom >= 1.0f && Zoom <= 45.0f)
Zoom -= yoffset;
if (Zoom <= 1.0f)
Zoom = 1.0f;
if (Zoom >= 45.0f)
Zoom = 45.0f;
}
private:
// 计算从相机 (更新) 的欧拉角度的前矢量
void updateCameraVectors()
{
// Calculate the new Front vector
glm::vec3 front;
front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
front.y = sin(glm::radians(Pitch));
front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
Front = glm::normalize(front);
// Also re-calculate the Right and Up vector
Right = glm::normalize(glm::cross(Front, WorldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
Up = glm::normalize(glm::cross(Right, Front));
}
};
#endif

第四步:编写着色器代码文件

顶点着色器:*.vs;片断着色器:*.fs;

  1. *.vs:一般情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #version 330 core
    // 布局输入
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aColor;
    layout (location = 2) in vec2 aTexCoord;
    // 输出
    out vec3 ourColor;
    out vec2 TexCoord;
    // 局外设定
    uniform mat4 model; // 模型本身的变化矩阵
    uniform mat4 view; // LookAt矩阵
    uniform mat4 projection; // 投影矩阵

    void main()
    {
    gl_Position = projection * view * model * vec4(aPos, 1.0); // 乘法是由右往左看的
    ourColor = aColor;
    TexCoord = vec2(aTexCoord.x, aTexCoord.y);
    }
  2. *.fs:纹理图片混合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #version 330 core
    out vec4 FragColor;

    in vec3 ourColor;
    in vec2 TexCoord;

    // 纹理采样器
    uniform sampler2D texture[n]; // n组纹理采样器

    void main()
    {
    // linearly interpolate between both textures (80% container, 20% awesomeface)
    FragColor = mix(texture(texture[0], TexCoord), texture(texture[1], TexCoord), 0.2);
    }

第五步:创建源文件

立即渲染模式

注:现在已经不常用了,因此只是简单介绍

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
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
int main() {
glfwInit(); // 初始化
GLFWwindow* window = glfwCreateWindow(800, 600, "HelloWindow", NULL, NULL); // 创建窗口
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){ // 加载函数指针
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
while (!glfwWindowShouldClose(window)) { // 渲染循环
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 渲染背景
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES); // 渲染图形
glColor3f(1.0, 0.0, 0.0);
glVertex3f(0.0, 1.0, 0.0);
glColor3f(0.0, 1.0, 0.0);
glVertex3f(-1.0, -1.0, 0.0);
glColor3f(0.0, 0.0, 1.0);
glVertex3f(1.0, -1.0, 0.0);
glEnd();
glfwSwapBuffers(window); // 交换缓存
glfwPollEvents(); // 挂起事件
}
glfwTerminate(); // 终止函数
return 0;
}

核心模式

  1. 头文件引入,以及全局变量和函数定义

    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
    // 环境配置头文件
    #include <glad/glad.h>
    #include <GLFW/glfw3.h>
    #include <stb_image.h>
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    #include <glm/gtc/type_ptr.hpp>
    #include <iostream>
    // 自定义类头文件
    #include "Shader.h"
    #include "Camera.h"
    // 函数声明
    void framebuffer_size_callback(GLFWwindow* window, int width, int height);
    void processInput(GLFWwindow *window);
    void mouse_callback(GLFWwindow* window, double xpos, double ypos);
    void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
    // 窗口大小设定
    const unsigned int SCR_WIDTH = 800;
    const unsigned int SCR_HEIGHT = 600;
    // 摄像机初始化
    Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
    float lastX = SCR_WIDTH / 2.0f;
    float lastY = SCR_HEIGHT / 2.0f;
    bool firstMouse = true;
    // 时间
    float deltaTime = 0.0f; // time between current frame and last frame
    float lastFrame = 0.0f;
  2. glfw:初始化

    1
    2
    3
    4
    5
    6
    7
    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); // uncomment this statement to fix compilation on OS X
    #endif
  3. glfw:窗口创建

    1
    2
    3
    4
    5
    6
    7
    8
    GLFWwindow* window = glfwCreateWindow(窗口宽度, 窗口高度, "窗口标题", NULL, NULL);
    if (window == NULL)
    {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
    }
    glfwMakeContextCurrent(window);
  4. 窗口函数

    1
    2
    3
    4
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 窗口大小改变的响应函数
    glfwSetCursorPosCallback(window, mouse_callback); // 鼠标移动响应函数
    glfwSetScrollCallback(window, scroll_callback); // 鼠标滚轮响应函数
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // 隐藏鼠标
  5. glad:加载OpenGL函数指针

    1
    2
    3
    4
    5
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
    }
  6. Shader:建立编译着色器项目

    1
    Shader ourShader("shader.vs", "shader.fs"); // *.vs是顶点着色器;*.fs是片断着色器
  7. 设置顶点数据和缓冲并配置顶点属性

    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
    float vertices[n][] = { // n组顶点数组
    // 位置(x,y,z) // 颜色(R,G,B) // 纹理(X,Y)
    0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上
    0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下
    -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下
    -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // 左上
    };
    unsigned int indices[n][] = { // n组索引数组
    0, 1, 3, // 第一个三角形
    1, 2, 3 // 第二个三角形
    };
    glm::vec3 cubePositions[n] = { // n组模型位置
    glm::vec3(0.0f, 0.0f, 0.0f),
    glm::vec3(2.0f, 5.0f, -15.0f),
    glm::vec3(-1.5f, -2.2f, -2.5f),
    glm::vec3(-3.8f, -2.0f, -12.3f),
    glm::vec3(2.4f, -0.4f, -3.5f),
    glm::vec3(-1.7f, 3.0f, -7.5f),
    glm::vec3(1.3f, -2.0f, -2.5f),
    glm::vec3(1.5f, 2.0f, -2.5f),
    glm::vec3(1.5f, 0.2f, -1.5f),
    glm::vec3(-1.3f, 1.0f, -1.5f)
    };
    unsigned int VBOs[n], VAOs[n], EBOs[n];
    glGenVertexArrays(n, VAOs);
    glGenBuffers(n, VBOs);
    glGenBuffers(n, EBOs);
    for(int m = 0;m < n;m++){
    // 绑定顶点数组对象
    glBindVertexArray(VAOs[m]);
    // 绑定顶点缓冲对象
    glBindBuffer(GL_ARRAY_BUFFER, VBOs[m]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[n]), vertices[n], GL_STATIC_DRAW);
    // 绑定索引数组对象
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[m]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[n]), indices[n], GL_STATIC_DRAW);
    // 位置属性传入
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 颜色属性传入
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    // 纹理属性传入
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    }
  8. 纹理图片的导入

    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
    unsigned int textures[n];
    glGenTextures(n, textures);
    for(int m = 0;m < n;m++){
    glBindTexture(GL_TEXTURE_2D, textures[m]);
    // 设置纹理覆盖参数
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //
    // 设置纹理填充参数
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // 加载图片,创建纹理并生成MIP map
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
    unsigned char* data = stbi_load("图片路径和文件名", &width, &height, &nrChannels, 0);
    if (data)
    {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
    std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);
    }
    ourShader.use();
    // 告诉OpenGL每个采样器的纹理单位的归属
    for(int m = 0;m < n;m++){
    ourShader.setInt("texture[m]",m); // *** 等于 片断着色器中uniform sampler2D的最后一个
    }
  9. 渲染循环

    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
    while (!glfwWindowShouldClose(window))
    {
    // 求不同电脑的心跳或帧秒
    float currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;
    // 键盘输入事件
    processInput(window);
    // 渲染背景
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    // 在相应的纹理单元上绑定纹理
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture[1]);
    ···等等
    // 渲染内容
    ourShader.use();
    // 将投影矩阵传递给着色器 (请注意, 在这种情况下, 它可以更改每个帧)
    glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
    ourShader.setMat4("projection", projection);
    // 摄像机/视图变换
    glm::mat4 view = camera.GetViewMatrix();
    ourShader.setMat4("view", view);
    glBindVertexArray(VAOs[m]);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // EBO方式渲染图元
    for (unsigned int i = 0; i < 10; i++)
    {
    glm::mat4 model;
    model = glm::translate(model, cubePositions[i]);
    float angle = 20.0f * (i);
    model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
    ourShader.setMat4("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 36); // 直接使用VBO的方式渲染图元
    }
    ···等等
    // glfw: 交换缓冲区以及挂起输入输出事件
    glfwSwapBuffers(window);
    glfwPollEvents();
    }
  10. glfw:终止与释放

    1
    2
    3
    4
    5
    // 可选: 释放所有资源一旦他们的目标已经使用过:
    glDeleteVertexArrays(n, VAOs);
    glDeleteBuffers(n, VBOs);
    glfwTerminate();
    return 0;
  11. 其他函数

    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
    // 处理所有输入: 查询 GLFW 相关按键是否按下/松开此框架并相应做出反应
    void processInput(GLFWwindow *window)
    {
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    glfwSetWindowShouldClose(window, true);
    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
    camera.ProcessKeyboard(FORWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
    camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
    camera.ProcessKeyboard(LEFT, deltaTime);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
    camera.ProcessKeyboard(RIGHT, deltaTime);
    }
    // glfw: 每当鼠标移动时, 就会调用此回调
    void mouse_callback(GLFWwindow* window, double xpos, double ypos)
    {
    if (firstMouse)
    {
    lastX = xpos;
    lastY = ypos;
    firstMouse = false;
    }
    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // 自 y 坐标从下到上反向
    lastX = xpos;
    lastY = ypos;
    camera.ProcessMouseMovement(xoffset, yoffset);
    }
    // glfw: 每当鼠标滚轮滚动时, 就会调用此回调
    void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
    {
    camera.ProcessMouseScroll(yoffset);
    }
    // glfw: 每当窗口大小改变时,就会调用此回调
    void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    {
    glViewport(0, 0, width, height);
    }

OpenGL常用函数功能解析

OpenGL常见几何图元绘制

参考资料

  1. LearnOpenGL-CN:LearnOpenGL系列教程的中文版
-------------本文结束感谢您的阅读-------------
亲,可以打赏点吗?.