OpenGL

https://blog.tryhardzhang.xyz/2024/08/02/graphics/opengl/Opengl/

https://blog.tryhardzhang.xyz/2024/07/30/intern/FFMPE%E4%B8%8ESDL%E6%92%AD%E6%94%BE%E5%99%A8%E6%9E%B6%E6%9E%84/

https://blog.tryhardzhang.xyz/2024/07/20/intern/%E8%A7%86%E9%A2%91%E6%92%AD%E6%94%BE%E5%99%A8/

OpenGL

  • 与窗口分离

OpenGL本身不负责创建窗口、管理窗口事件或者处理输入设备。这些任务通常由其他库或操作系统提供的窗口系统接口来处理。

由于viewport视口

  • 右手坐标系

GLFW

GLFW(Graphics Library Framework)是一个用于创建窗口、处理输入以及管理OpenGL上下文的开源库。它简化了OpenGL程序的开发,提供了跨平台的接口,使得开发者可以专注于渲染和图形处理,而不必担心底层平台特定的窗口系统和输入处理。GLFW支持Windows、macOS和Linux等多个操作系统。

Glad 是实现函数指针的映射

opengl是状态机 保存自己渲染的状态和管线的状态

1
2
3
4
5
6
7
glfwInit():初始化GLFW库。
glfwTerminate():终止GLFW库,释放资源。
glfwCreateWindow(width, height, title, monitor, share):创建一个窗口并返回其句柄。
glfwMakeContextCurrent(window):设置指定窗口的OpenGL上下文为当前上下文。
glfwWindowShouldClose(window):检查窗口是否应关闭。
glfwSwapBuffers(window):交换窗口的前后缓冲。
glfwPollEvents():处理所有待处理的事件。

Glad

  1. 加载OpenGL函数指针: OpenGL函数的地址需要在运行时通过特定的方式获取,这与直接调用标准库中的函数不同。Glad帮助开发者在运行时加载这些函数指针,以便应用程序能够调用最新的OpenGL功能。
  2. 管理OpenGL扩展: OpenGL是一个不断发展的标准,不同的显卡和驱动程序可能支持不同的OpenGL扩展。Glad允许你指定需要的扩展,并生成相应的加载代码,以便在应用程序中使用这些扩展。
  3. 跨平台支持: Glad支持多种操作系统(如Windows、Linux、macOS)和多种OpenGL版本,使得开发者可以编写跨平台的OpenGL代码。

Viewport可以定义应该在哪个视口进行渲染

双缓存

glfwSwapBuffers(window);

image-20240725170542865

Shader 如何从CPU获取数据

image-20240725173954896

  • Layout 锚点信息 告诉信息布局 分别是什么

Shader

Vertex Buffer Object (VBO)

VBO 是一种用于存储顶点数据的缓冲区对象。它允许将顶点数据(例如顶点坐标、法线、纹理坐标等)存储在 GPU 内存中,以便快速访问和渲染。使用 VBO 可以显著提高渲染性能,因为数据传输的瓶颈被减少了。

  1. 获取VBO的index
  2. 绑定VBO
  3. 给VBO分配显存空间 传输数据
  4. 告诉shader数据解析方式
  5. 激活锚点

Vertex Array Object (VAO)

VAO 是一种用于存储顶点属性配置的对象。它保存了顶点属性指针的状态以及与之关联的 VBO。VAO 的作用是简化顶点属性配置的过程,通过绑定 VAO,可以快速恢复先前配置好的顶点属性状态。

程序中关系

VAO 管理和维护与绘制相关的 VBO 及其顶点属性状态。当我们创建和配置 VAO 时,会指定使用哪些 VBO 以及如何解析它们的数据。这样,当我们使用 VAO 进行绘制时,所有与 VBO 相关的状态都会自动应用。

流程

  • 模型数据建立
  • 发送到GPU
  • GPU把顶点传输到vertexShader里
  • VertexShader 进行空间变换操作,并行处理
  • 到FragmentShader对各个顶点之间进行插值着色

VAO 模式绘制

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
181
182
183
184
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

unsigned VBO = 0;
unsigned VAO = 0;
unsigned int shaderProgram = 0;

// 渲染绘制
void rend()
{
glBindVertexArray(VAO);
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLES, 0, 3); // 图形类型 第0个起始 3个
glUseProgram(0); // 关掉程序
}

// 构建模型 完成VBO VAO
void initModel()
{
float vertices[] =
{
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

glGenVertexArrays(1, &VAO); // 获取VAO
glBindVertexArray(VAO); // 绑定VAO


// 1. 获取VBO index
glGenBuffers(1, &VBO); // 获取VBO的index 可以获取多个VBO index
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定哪种buffer的哪一个
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 显存分配,类型、大小、位置、如何使用数据(GL_STATIC_DRAW 模型数据不会改变)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 做锚定点 layout 锚定点0,3个数据,数据类型,是否归一化,跨步步长,数据读取开始位置
glEnableVertexAttribArray(0); // 激活锚定点
glBindBuffer(GL_ARRAY_BUFFER, 0); // 取消绑定
glBindVertexArray(0);
}

// Shader编写
void initShader(const char* _vertexPath, const char* _fragPath)
{
std::string _vertexCode("");
std::string _fragCode("");

std::ifstream _vShaderFile;
std::ifstream _fShaderFile;

_vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); // 扩展 允许trycatch
_fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); // 扩展 允许trycatch

try
{
_vShaderFile.open(_vertexPath);
_fShaderFile.open(_fragPath);

std::stringstream _vShaderStream, _fShaderStream;
_vShaderStream << _vShaderFile.rdbuf();
_fShaderStream << _fShaderFile.rdbuf();

_vertexCode = _vShaderStream.str();
_fragCode = _fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::string errStr = "read shader fail";
std::cout << errStr << std::endl;
}

const char* _vShaderStr = _vertexCode.c_str(); // 提取
const char* _fShaderStr = _fragCode.c_str();

// shader的编译链接
unsigned int _vertexID = 0, _fragID = 0;
char _infoLog[512];
int _successFlag = 0;

// vertexShader 编译
_vertexID = glCreateShader(GL_VERTEX_SHADER); // 创建shaderID
glShaderSource(_vertexID, 1, &_vShaderStr, NULL); // 传代码
glCompileShader(_vertexID);

glGetShaderiv(_vertexID, GL_COMPILE_STATUS, &_successFlag); // 检查编译状态
if (!_successFlag)
{
glGetShaderInfoLog(_vertexID, 512, NULL, _infoLog);
std::string errStr(_infoLog);
std::cout << errStr << std::endl;
}

// fragmentShader 编译
_fragID = glCreateShader(GL_FRAGMENT_SHADER); // 创建shaderID
glShaderSource(_fragID, 1, &_fShaderStr, NULL); // 传代码
glCompileShader(_fragID);

glGetShaderiv(_fragID, GL_COMPILE_STATUS, &_successFlag); // 检查编译状态
if (!_successFlag)
{
glGetShaderInfoLog(_fragID, 512, NULL, _infoLog);
std::string errStr(_infoLog);
std::cout << errStr << std::endl;
}

// 链接 加入编译好的shader
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, _vertexID);
glAttachShader(shaderProgram, _fragID);
glLinkProgram(shaderProgram);

glGetProgramiv(shaderProgram, GL_LINK_STATUS, &_successFlag);
if (!_successFlag)
{
glGetProgramInfoLog(shaderProgram, 512, NULL, _infoLog);
std::string errStr(_infoLog);
std::cout << errStr << std::endl;
}
// 去掉中间状态文件 只保留最后编译好的
glDeleteShader(_vertexID);
glDeleteShader(_fragID);
}

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

// 当有任何输入时 比如键盘或鼠标事件
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}

int main()
{
glfwInit(); // opengl 状态初始化
// 版本选择
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //(配置项,配置结果)

GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Core", NULL, NULL); // 窗口定义 // 窗口宽高 名字

if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window); // 当前上下文窗口绑定为当前窗口

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) // 初始化函数指针
{
std::cout << "Fialed to initialize GLAD" << std::endl;
return -1;
}

glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 窗口大小变化会调用函数

initModel();
initShader("vertexShader.glsl", "fragmentShader.glsl");

while (!glfwWindowShouldClose(window))
{
processInput(window);

glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色
glClear(GL_COLOR_BUFFER_BIT); // 清除画布颜色, GL_COLOR_BUFFER_BIT是下一帧要显示的所有像素信息

// render
rend();

glfwSwapBuffers(window); // 双缓存
glfwPollEvents(); // 操作系统获取输入和其他事件并处理 用于processInput
}
glfwTerminate();
return 0;
}

OPENGL 部分配置问题

GLFW_OPENGL_CORE_PROFILE:

  • 选择核心配置文件(Core Profile)。
  • 核心配置文件排除了一些较旧的、不再推荐使用的 OpenGL 功能。它仅支持现代的 OpenGL 功能和特性。
  • 使用核心配置文件时,必须使用现代的 OpenGL 函数和特性,例如顶点缓冲对象(VBO)、顶点数组对象(VAO)和着色器等。传统的固定管线功能(如直接使用 glBegin 和 glEnd 的绘图方法)在核心配置文件中不可用。
  • 绑定VAO是强制的

GLFW_OPENGL_COMPAT_PROFILE:

  • 选择兼容配置文件(Compatibility Profile)。
  • 兼容配置文件包含了所有 OpenGL 功能,包括新的和旧的特性。这意味着你可以在一个应用程序中使用传统的固定管线功能和现代的 OpenGL 功能。
  • 兼容配置文件对于需要支持老旧硬件或依赖于传统 OpenGL 功能的应用程序很有用。
1
2
3
4
5
6
glfwInit(); // opengl 状态初始化
// 版本选择
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); //(配置项,配置结果)
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //(配置项,配置结果)

多个VBO的情况

VBO1和VBO2是两个 完全不同的缓存 为什么VBO2会覆盖掉VBO1的结果导VBO1不显示

因为锚定点的问题

image-20240726163641559

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
// 渲染绘制
void rend()
{
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// glBindVertexArray(VAO);
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLES, 0, 3); // 图形类型 第0个起始 3个
glUseProgram(0); // 关掉程序
}

// 构建模型 完成VBO VAO
void initModel()
{
float vertices[] =
{
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

// glGenVertexArrays(1, &VAO); // 获取VAO
// glBindVertexArray(VAO); // 绑定VAO


// 1. 获取VBO index
glGenBuffers(1, &VBO); // 获取VBO的index 可以获取多个VBO index
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定哪种buffer的哪一个
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 显存分配,类型、大小、位置、如何使用数据(GL_STATIC_DRAW 模型数据不会改变)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 做锚定点 layout 锚定点0,3个数据,数据类型,是否归一化,跨步步长,数据读取开始位置
glEnableVertexAttribArray(0); // 激活锚定点
glBindBuffer(GL_ARRAY_BUFFER, 0); // 取消绑定
// glBindVertexArray(0);

float vertices2[] =
{
-0.0f, -0.1f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

// glGenVertexArrays(1, &VAO); // 获取VAO
// glBindVertexArray(VAO); // 绑定VAO


// 1. 获取VBO index
glGenBuffers(1, &VBO2); // 获取VBO的index 可以获取多个VBO index
glBindBuffer(GL_ARRAY_BUFFER, VBO2); // 绑定哪种buffer的哪一个
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW); // 显存分配,类型、大小、位置、如何使用数据(GL_STATIC_DRAW 模型数据不会改变)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 做锚定点 layout 锚定点0,3个数据,数据类型,是否归一化,跨步步长,数据读取开始位置
glEnableVertexAttribArray(0); // 激活锚定点
glBindBuffer(GL_ARRAY_BUFFER, 0); // 取消绑定
// glBindVertexArray(0);
}

多个VAO VAO 记录了锚点信息 所以不需要重新设置锚点

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

// 渲染绘制
void rend()
{
glBindVertexArray(VAO);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLES, 0, 3); // 图形类型 第0个起始 3个
glUseProgram(0); // 关掉程序

glBindVertexArray(VAO2);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
glUseProgram(shaderProgram);
glDrawArrays(GL_TRIANGLES, 0, 3); // 图形类型 第0个起始 3个
glUseProgram(0); // 关掉程序
}

// 构建模型 完成VBO VAO
void initModel()
{
float vertices[] =
{
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

glGenVertexArrays(1, &VAO); // 获取VAO
glBindVertexArray(VAO); // 绑定VAO


// 1. 获取VBO index
glGenBuffers(1, &VBO); // 获取VBO的index 可以获取多个VBO index
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定哪种buffer的哪一个
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 显存分配,类型、大小、位置、如何使用数据(GL_STATIC_DRAW 模型数据不会改变)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 做锚定点 layout 锚定点0,3个数据,数据类型,是否归一化,跨步步长,数据读取开始位置
glEnableVertexAttribArray(0); // 激活锚定点
glBindBuffer(GL_ARRAY_BUFFER, 0); // 取消绑定
glBindVertexArray(0); // 结束VAO绑定

float vertices2[] =
{
-0.0f, -0.1f, 0.0f,
0.8f, -0.5f, 0.0f,
0.0f, 0.8f, 0.0f
};

glGenVertexArrays(1, &VAO2); // 获取VAO
glBindVertexArray(VAO2); // 绑定VAO


// 1. 获取VBO index
glGenBuffers(1, &VBO2); // 获取VBO的index 可以获取多个VBO index
glBindBuffer(GL_ARRAY_BUFFER, VBO2); // 绑定哪种buffer的哪一个
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW); // 显存分配,类型、大小、位置、如何使用数据(GL_STATIC_DRAW 模型数据不会改变)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 做锚定点 layout 锚定点0,3个数据,数据类型,是否归一化,跨步步长,数据读取开始位置
glEnableVertexAttribArray(0); // 激活锚定点
glBindBuffer(GL_ARRAY_BUFFER, 0); // 取消绑定
glBindVertexArray(0); // 结束VAO绑定
}

Shader与C++传输

uniform变量有限 定义了uniform如果未使用会被系统删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 渲染绘制
void rend()
{
glUseProgram(shaderProgram); // 提前启用
float _time = glfwGetTime();
float _green = sin(_time) * 0.5f + 0.5f;
int _location = glGetUniformLocation(shaderProgram, "ourColor"); // 从已经构建好滴shader程序获取变量
glUniform4f(_location, 0.0f, _green, 0.0f, 1.0f); // 4f:传输4个float

glBindVertexArray(VAO);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);

glDrawArrays(GL_TRIANGLES, 0, 3); // 图形类型 第0个起始 3个
glUseProgram(0); // 关掉程序
}

顶点索引

剔除重复顶点 传输顶点索引 VBO VAO

EBO Element Buffer Object

EBOs are a powerful tool in OpenGL that enhance the efficiency and flexibility of rendering operations. They are especially useful for complex models and animations, allowing for more compact and efficient data representation and manipulation.

image-20240729112647758

Texture

步骤

定义UV坐标 获取插值结果 与实际坐标相乘 获得实际插值

STB_IMAGE 读取图片

  1. **stbi_load**从文件中加载图像,并返回图像数据的指针,同时提供图像的宽度、高度和通道数。

  2. **stbi_load_from_memory**从内存中加载图像数据,适用于已经在内存中的图像。

  3. **stbi_load_from_file**从文件指针加载图像,类似于 stbi_load,但使用 FILE* 作为输入。

  4. **stbi_load_from_callbacks**使用自定义回调接口加载图像,适用于自定义的输入/输出系统。

  5. **stbi_image_free**释放由 stbi_load 系列函数分配的图像数据内存。

  6. **stbi_info**获取图像的基本信息(宽度、高度、通道数)而不加载图像数据。

  7. **stbi_set_flip_vertically_on_load**设置在加载图像时是否垂直翻转图像的标志。

  8. **stbi_failure_reason**返回最后一次失败的原因描述。

  9. **stbi_is_hdr**检查图像文件是否为 HDR(高动态范围)图像。

  10. **stbi_loadf**加载 HDR 图像,并以浮点数据的形式返回图像数据。

Texture Wrapping

坐标超出0-1的范围 需要重复采样

image-20240729145015630

Texture Filter

通过u*widthv*height 获取坐标位小数 需要取整

Nearest 找最近的点 bilinear 找四个求平均值

图片小使用nearest 因为像素差异变大

图片大使用bilinear 更加平滑

摄像机

摄像机矩阵

1
glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, _name.c_str()), 1, GL_FALSE, glm::value_ptr(_matrix));

glGetUniformLocation获取当前_name变量的shader或锚定符

glUniformMatrix4fv 向shader的location传入数据 GL_FALSE(列优先)

1
2
glm::lookAt<float, glm::packed_highp>(const glm::highp_vec3 &eye, const glm::highp_vec3 &center, 
    const glm::highp_vec3 &up) // 正交投影

初始呈像

image-20240730105833151

Lookat角度 并没有开启深度检测

image-20240730110045717
1
2
3
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除画布颜色, GL_COLOR_BUFFER_BIT是下一帧要显示的所有像素信息
glEnable(GL_DEPTH_TEST);

加入深度检测后

1f706e2c617d71f42aab1f5cc26f615

摄像机的移动旋转

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

// 渲染绘制
void rend()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除画布颜色, GL_COLOR_BUFFER_BIT是下一帧要显示的所有像素信息
glEnable(GL_DEPTH_TEST);

glm::vec3 modelVecs[] = {
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)
};

// 摄像机矩阵
_viewMatrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
_projMatrix = glm::perspective(glm::radians(45.0f), (float)_width / (float)_height, 0.1f, 100.0f);

glBindTexture(GL_TEXTURE_2D, _texture);

// 画十个立方体
for(int i=0;i<10;i++)
{
glm::mat4 _modelMatrix(1.0f);
_modelMatrix = glm::translate(_modelMatrix, modelVecs[i]); //平移

_shader.start();
_shader.setMatrix("_modelMatrix", _modelMatrix);
_shader.setMatrix("_viewMatrix", _viewMatrix);
_shader.setMatrix("_projMatrix", _projMatrix);
glBindVertexArray(VAO);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
glDrawArrays(GL_TRIANGLES, 0, 36); // 图形类型 第0个起始 3个
// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
_shader.end(); // 关掉程序
}
}
image-20240730114754720

Rotate move运算规则

image-20240730120042651

rotate矩阵在右边

image-20240730120336423
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
void rend()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除画布颜色, GL_COLOR_BUFFER_BIT是下一帧要显示的所有像素信息
glEnable(GL_DEPTH_TEST);

glm::vec3 modelVecs[] = {
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)
};

// 摄像机矩阵
_viewMatrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
_projMatrix = glm::perspective(glm::radians(45.0f), (float)_width / (float)_height, 0.1f, 100.0f);

glBindTexture(GL_TEXTURE_2D, _texture);

// 画十个立方体
for(int i=0;i<10;i++)
{
glm::mat4 _modelMatrix(1.0f);
_modelMatrix = glm::translate(_modelMatrix, modelVecs[i]); //平移
_modelMatrix = glm::rotate(_modelMatrix, glm::radians((float)glfwGetTime() * (i+1) * 10), glm::vec3(0.0f, 1.0f, 0.0f));

_shader.start();
_shader.setMatrix("_modelMatrix", _modelMatrix);
_shader.setMatrix("_viewMatrix", _viewMatrix);
_shader.setMatrix("_projMatrix", _projMatrix);
glBindVertexArray(VAO);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
glDrawArrays(GL_TRIANGLES, 0, 36); // 图形类型 第0个起始 3个
// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
_shader.end(); // 关掉程序
}
}

LookAt作用

image-20240730121846652

image-20240730140845075

cameraPos - cameraTarget 由减数到被减数的向量

cameraFront需要是一个单位向量

image-20240730142937534

processInput每次循环都会被调用 检测是否使用按键

camera.cpp

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
#include "Camera.h"

void Camera::lookAt(glm::vec3 _pos, glm::vec3 _front, glm::vec3 _up)
{
m_position = _pos;
m_front = glm::normalize(_front);
m_up = _up;

m_vMatrix = glm::lookAt(m_position, m_position + m_front, m_up);
}

void Camera::update()
{
m_vMatrix = glm::lookAt(m_position, m_position + m_front, m_up);
}

glm::mat4 Camera::getMatrix()
{
return m_vMatrix;
}

void Camera::move(CAMERA_MOVE _mode)
{
switch (_mode)
{
case CAMERA_MOVE::MOVE_LEFT:
m_position -= glm::normalize(glm::cross(m_front, m_up)) * m_speed;
break;
case CAMERA_MOVE::MOVE_RIGHT:
m_position += glm::normalize(glm::cross(m_front, m_up)) * m_speed;
break;
case CAMERA_MOVE::MOVE_FRONT:
m_position += m_speed * m_front;
break;
case CAMERA_MOVE::MOVE_BACK:
m_position -= m_speed * m_front;
break;
default:
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 当有任何输入时 比如键盘或鼠标事件
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);

if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_FRONT);
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_BACK);
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_LEFT);
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_RIGHT);
}
}

摄像机转动

pitch yaw and roll

image-20240730155713477

yaw x的正方向走向z的正方向为yaw的正值

yaw x的正方向走向z的负方向是负值

image-20240730185904041image-20240730185923378

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
void Camera::pitch(float _yOffset)
{
m_pitch += _yOffset * m_sensitivity;

if (m_pitch >= 89.0f)
{
m_pitch = 89.0f;
}
if (m_pitch <= -89.0f)
{
m_pitch = -89.0f;
}

m_front.y = sin(glm::radians(m_pitch));
m_front.x = cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));
m_front.z = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));

update();
}

void Camera::yaw(float _x0ffset)
{
m_yaw += _x0ffset * m_sensitivity;

m_front.y = sin(glm::radians(m_pitch));
m_front.x = cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));
m_front.z = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));

update();
}

void Camera::setSentivity(float _s)
{
m_sensitivity = _s;
}
image-20240730190433724

需要检测是不是firstmove 记录开始的坐标 先记录再对比再剪除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Camera::onMouseMove(double _xpos, double _ypos)
{
if (m_firstMove)
{
m_xpos = _xpos;
m_ypos = _ypos;
m_firstMove = false;
return;
}

float _xOffset = _xpos - m_xpos;
float _yOffset = -(_ypos - m_ypos);

m_xpos = _xpos;
m_ypos = _ypos;

pitch(_yOffset);
yaw(_xOffset);
}

windows 的窗体位置再左上角 向上坐标减小 向下坐标增大 所以会相反

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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#include "Base.h"
#include "Shader.h"
#include "ffImage.h"
#include "Camera.h"

unsigned VBO = 0;
unsigned VAO = 0;
unsigned int _texture = 0;
ffImage* _pImage = NULL;
Shader _shader;
Camera _camera;

glm::mat4 _viewMatrix(1.0f);
glm::mat4 _projMatrix(1.0f);
int _width = 800;
int _height = 600;

// 渲染绘制
void rend()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除画布颜色, GL_COLOR_BUFFER_BIT是下一帧要显示的所有像素信息
glEnable(GL_DEPTH_TEST);

glm::vec3 modelVecs[] = {
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)
};

// 摄像机矩阵
_camera.update();
// _viewMatrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
_projMatrix = glm::perspective(glm::radians(45.0f), (float)_width / (float)_height, 0.1f, 100.0f);

glBindTexture(GL_TEXTURE_2D, _texture);

// 画十个立方体
for(int i=0;i<10;i++)
{
glm::mat4 _modelMatrix(1.0f);
_modelMatrix = glm::translate(_modelMatrix, modelVecs[i]); //平移
_modelMatrix = glm::rotate(_modelMatrix, glm::radians((float)glfwGetTime() * (i+1) * 10), glm::vec3(0.0f, 1.0f, 0.0f));

_shader.start();
_shader.setMatrix("_modelMatrix", _modelMatrix);
_shader.setMatrix("_viewMatrix", _camera.getMatrix());
_shader.setMatrix("_projMatrix", _projMatrix);
glBindVertexArray(VAO);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
glDrawArrays(GL_TRIANGLES, 0, 36); // 图形类型 第0个起始 3个
// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
_shader.end(); // 关掉程序
}
}

// 构建模型 完成VBO VAO
void initModel()
{
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,

-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,

-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};

//unsigned int indices[] =
//{
// 0, 1, 3,
// 1, 2, 3
//};

glGenVertexArrays(1, &VAO); // 获取VAO VAO是管理容器
glBindVertexArray(VAO); // 绑定VAO

//unsigned int EBO = 0;
//glGenBuffers(1, &EBO); // 向系统获取一个EBO
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// 1. 获取VBO index
glGenBuffers(1, &VBO); // 获取VBO的index 可以获取多个VBO index
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定哪种buffer的哪一个
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 显存分配,类型、大小、位置、如何使用数据(GL_STATIC_DRAW 模型数据不会改变)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); // 做锚定点 layout 锚定点0,3个数据,数据类型,是否归一化,跨步步长,数据读取开始位置
// glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(sizeof(float) * 3)); // 锚定点1 传递颜色信息
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(sizeof(float) * 3));

glEnableVertexAttribArray(0); // 激活锚定点0
glEnableVertexAttribArray(1); // 激活锚定点1
// glEnableVertexAttribArray(2);

glBindBuffer(GL_ARRAY_BUFFER, 0); // 取消绑定
glBindVertexArray(0); // 结束VAO绑定
}

void initTexture()
{
// 创建纹理
_pImage = ffImage::readFromFile("res/example.jpg");

glGenTextures(1, &_texture); // 获取texture
glBindTexture(GL_TEXTURE_2D, _texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Texture Wrapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Texture Filter
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pImage->getWidth(), _pImage->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, _pImage->getData()); // 传入数据
}

// Shader编写
void initShader(const char* _vertexPath, const char* _fragPath)
{
_shader.initShader(_vertexPath, _fragPath);
}

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

// 当有任何输入时 比如键盘或鼠标事件
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);

if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_FRONT);
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_BACK);
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_LEFT);
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
{
_camera.move(CAMERA_MOVE::MOVE_RIGHT);
}
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
_camera.onMouseMove(xpos, ypos);
}

int main()
{
glfwInit(); // opengl 状态初始化
// 版本选择
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); //(配置项,配置结果)
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //(配置项,配置结果)

GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Core", NULL, NULL); // 窗口定义 // 窗口宽高 名字

if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window); // 当前上下文窗口绑定为当前窗口

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) // 初始化函数指针
{
std::cout << "Fialed to initialize GLAD" << std::endl;
return -1;
}

glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 窗口大小变化会调用函数

_camera.lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, 1.0f, 0.0f));
_camera.setSpeed(0.01f);

// 鼠标控制
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // 鼠标不显示
glfwSetCursorPosCallback(window, mouse_callback);

initModel();
initTexture();
initShader("vertexShader.glsl", "fragmentShader.glsl");

while (!glfwWindowShouldClose(window))
{
processInput(window);
// render
rend();

glfwSwapBuffers(window); // 双缓存
glfwPollEvents(); // 操作系统获取输入和其他事件并处理 用于processInput
}
glfwTerminate();
return 0;
}

光照

image-20240731114207200

image-20240731120813607

Phong Shader 模型

环境光 Shader

1
2
3
4
5
6
7
8
9
Fragment Shader
void main()
{
float ambientStrength = 0.1; // 环境光强度系数
vec3 ambient = ambientStrength * lightColor;

vec3 result = ambient * objectColor;
FragColor = vec4(result, 1.0);
}

漫反射 Shader

image-20240731143355028

image-20240731154359851

平移部分不会影响法线向量的计算 为0可以消除平移 的影响

gl_Position 将顶点的坐标转换到视图空间之中 FragPos是世界坐标

漫反射 法线矩阵证明

image-20240731153514547

在这里 dot可以转化为转置后的矩阵乘法

补充

1
_modelMatrix = glm::translate(_modelMatrix, glm::vec3(3.0f, 0.0f, -3.0f)); // 与上次的结果产生了叠加

可以使用 _modelMatrix = glm::mat4(1.0f) 清空

transpose(inverse(_modelMatrix))

对于法线向量的正确变换,仅仅使用模型矩阵的逆矩阵是不够的。需要使用逆矩阵的转置矩阵来确保法线的正确性。

  • 为什么使用逆矩阵? 对于位置向量,我们使用模型矩阵来变换它们。然而,对于法线向量,特别是在非均匀缩放的情况下,如果直接使用模型矩阵来变换,法线方向可能会失真。因此,使用逆矩阵来反向缩放,确保法线向量的方向不受不均匀缩放的影响。
  • 为什么转置? 转置操作将行向量转换为列向量(或反之),这是确保法线方向保持正确的必要步骤,因为它对法线向量施加了正确的几何变换。
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
#version 330 core
out vec4 FragColor; // 定义为out的变量会被输出到下一个步骤自动处理

in vec2 outUV;
in vec3 outFragPos; // 经过偏移之后的世界坐标
in vec3 outNormal;

// uniform vec4 ourColor;
uniform sampler2D ourTexture;
uniform vec3 light_color;
uniform vec3 light_pos;
uniform float ambient_strength; // 环境光强度

void main()
{
// 环境光
vec3 _ambient = light_color * ambient_strength;
// 漫反射
vec3 _normal = normalize(outNormal);
vec3 _lightDir = normalize(light_pos - outFragPos);
float _diff = max(dot(_normal, _lightDir), 0.0f);
vec3 _diffuse = light_color * _diff;

vec3 result = _ambient + _diffuse;
FragColor = texture(ourTexture, outUV) * vec4(result, 1.0f);
// FragColor = texture(ourTexture, outUV)* ambient;
};

镜面反射

image-20240731172722456

image-20240731173808343 image-20240731190203265

材质系统

image-20240731191227383

image-20240731192208031

材质和光都有自己的属性

image-20240731192532460

更改光照和材质属性重写

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
#version 330 core
out vec4 FragColor; // 定义为out的变量会被输出到下一个步骤自动处理

in vec2 outUV;
in vec3 outFragPos; // 经过偏移之后的世界坐标
in vec3 outNormal;

struct Material
{
vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;

float m_shiness; // 镜面反射指数
};

uniform Material myMaterial;

struct Light
{
vec3 m_pos;
vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;
};

uniform Light mylight;

// uniform vec4 ourColor;
uniform sampler2D ourTexture;
uniform vec3 view_pos;

void main()
{
// 环境光
vec3 _ambient = mylight.m_ambient * myMaterial.m_ambient;
// 漫反射
vec3 _normal = normalize(outNormal);
vec3 _lightDir = normalize(mylight.m_pos - outFragPos);
float _diff = max(dot(_normal, _lightDir), 0.0f);
vec3 _diffuse = mylight.m_diffuse * _diff * myMaterial.m_diffuse;
// 镜面反射
float _specular_strength = 0.5;
vec3 _viewDir = normalize(view_pos - outFragPos);
vec3 _reflectDir = reflect(-_lightDir, outNormal);

float _spec = pow(max(dot(_viewDir, _reflectDir), 0.0f), myMaterial.m_shiness);

vec3 _specular = mylight.m_specular * _spec * myMaterial.m_specular;

// Phone 结果
vec3 result = _ambient + _diffuse + _specular;

FragColor = texture(ourTexture, outUV) * vec4(result, 1.0f);
// FragColor = texture(ourTexture, outUV)* ambient;
};

材质简化策略 纹理RGB可以代替环境光和漫反射的材质

image-20240801110738103

1
2
3
4
5
6
// 环境光材质与diffuse材质合并 漫反射材质利用物体本身的材质进行代表
struct Material{
sampler2D diffuse;
vec3 specular;
float shininess;
}

image-20240801112306740

image-20240801112845796

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 环境光
vec3 _ambient = mylight.m_ambient * vec3(texture(myMaterial.m_diffuse, outUV));
// 漫反射
vec3 _normal = normalize(outNormal);
vec3 _lightDir = normalize(mylight.m_pos - outFragPos);
float _diff = max(dot(_normal, _lightDir), 0.0f);
vec3 _diffuse = mylight.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse, outUV));
// 镜面反射
float _specular_strength = 0.5;
vec3 _viewDir = normalize(view_pos - outFragPos);
vec3 _reflectDir = reflect(-_lightDir, outNormal);

float _spec = pow(max(dot(_viewDir, _reflectDir), 0.0f), myMaterial.m_shiness);

vec3 _specular = mylight.m_specular * _spec * vec3(texture(myMaterial.m_specular, outUV));

// Phone 结果
vec3 result = _ambient + _diffuse + _specular;

FragColor = texture(ourTexture, outUV) * vec4(result, 1.0f);
// FragColor = texture(ourTexture, outUV)* ambient;

光照分类 平行光 点光源 聚光灯计算方案

平行光 通过w区分

image-20240801150050579

Fragment得到的是图元 插值获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
vec3 lDir = normalize(mylight.m_direction);
// 环境光
vec3 _ambient = mylight.m_ambient * vec3(texture(myMaterial.m_diffuse, outUV));
// 漫反射
vec3 _normal = normalize(outNormal);
float _diff = max(dot(_normal, -lDir), 0.0f);
vec3 _diffuse = mylight.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse, outUV));
// 镜面反射
float _specular_strength = 0.5;
vec3 _viewDir = normalize(view_pos - outFragPos);
vec3 _reflectDir = reflect(lDir, outNormal);

float _spec = pow(max(dot(_viewDir, _reflectDir), 0.0f), myMaterial.m_shiness);

vec3 _specular = mylight.m_specular * _spec * vec3(texture(myMaterial.m_specular, outUV));

// Phone 结果
vec3 result = _ambient + _diffuse + _specular;

FragColor = texture(ourTexture, outUV) * vec4(result, 1.0f);

点光源

image-20240801151356835

image-20240801151503101

聚光灯

image-20240801151900058

else 中diffuse和ambient都为贴图整合 实际上是环境光的值

聚光灯边缘优化

边缘较为生硬 且光照吧随着距离没有衰减

优化1

image-20240802120108624image-20240802120912149

lightDir 指向光源的向量 light.direction 光本身的方向

优化2

image-20240802140541628

多光源Shader编写

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
#version 330 core
out vec4 FragColor; // 定义为out的变量会被输出到下一个步骤自动处理

in vec2 outUV;
in vec3 outFragPos; // 经过偏移之后的世界坐标 多个顶点插值形成的图源
in vec3 outNormal; // 法线

struct Material
{
sampler2D m_diffuse; // 默认是0所以不用去赋值
sampler2D m_specular;

float m_shiness; // 镜面反射指数
};

uniform Material myMaterial;

struct DirLight
{
vec3 m_direction;

vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;
};

struct PointLight
{
vec3 m_pos;

vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;

float m_c;
float m_l;
float m_q;
};

struct SpotLight
{
vec3 m_pos;
vec3 m_direction;
float m_cutOff;
float m_outCutOff;

vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;

float m_c;
float m_l;
float m_q;
};

#define MAX_POINT_NUMBER 4
uniform DirLight _dirLight;
uniform PointLight _pointLight[MAX_POINT_NUMBER];
uniform SpotLight _spotLight;

uniform vec3 view_pos;

// 平行光
vec3 calculateDir(DirLight _light, vec3 _normal, vec3 _viewDir)
{
vec3 _lightDir = normalize(_light.m_direction);
// 环境光
vec3 _ambient = _light.m_ambient * vec3(texture(myMaterial.m_diffuse, outUV));

// 漫反射
float _diff = max(dot(_normal, -_lightDir), 0.0f);
vec3 _diffuse = _light.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse, outUV));

// 镜面反射
vec3 _reflectDir = reflect(_lightDir, _normal);
float _spec = pow(max(dot(_reflectDir, _viewDir), 0.0f), myMaterial.m_shiness);
vec3 _specular = _light.m_specular * _spec * vec3(texture(myMaterial.m_specular, outUV));

return (_ambient + _diffuse + _specular);
}

// 点光源
vec3 calculatePoint(PointLight _light, vec3 _normal, vec3 _viewDir, vec3 _fragPos)
{
// 物体指向光源的方向
vec3 _lightDir = normalize(_fragPos - _light.m_pos);
// 环境光
vec3 _ambient = _light.m_ambient * vec3(texture(myMaterial.m_diffuse, outUV));
// 漫反射
float _diff = max(dot(_normal, -_lightDir), 0.0f);
vec3 _diffuse = _light.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse, outUV));
// 镜面反射
vec3 _reflectDir = reflect(_lightDir, _normal);
float _spec = pow(max(dot(_reflectDir, _viewDir), 0.0f), myMaterial.m_shiness);
vec3 _specular = _light.m_specular * _spec * vec3(texture(myMaterial.m_specular, outUV));
// 衰减系数计算
float _dist = length(_light.m_pos - _fragPos);
float _attenuation = 1.0f / (_light.m_c + _light.m_l * _dist + _light.m_q * _dist * _dist);

return (_ambient + _diffuse + _specular) * _attenuation;
}

vec3 calculateSpot(SpotLight _light, vec3 _normal, vec3 _viewDir, vec3 _fragPos)
{
// 物体指向光源的方向
vec3 _lightDir = normalize(_fragPos - _light.m_pos);
// 边缘模糊系数
vec3 _spotDir = normalize(_light.m_direction);
float _cosTheta = dot(_lightDir, -_spotDir);
float _epsilon = _light.m_cutOff - _light.m_outCutOff;
float _intensity = clamp((_cosTheta - _light.m_outCutOff) / _epsilon, 0.0f, 1.0f);

// 环境光
vec3 _ambient = _light.m_ambient * vec3(texture(myMaterial.m_diffuse, outUV));
// 漫反射
float _diff = max(dot(_normal, -_lightDir), 0.0f);
vec3 _diffuse = _light.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse, outUV));
// 镜面反射
vec3 _reflectDir = reflect(_lightDir, _normal);
float _spec = pow(max(dot(_reflectDir, _viewDir), 0.0f), myMaterial.m_shiness);
vec3 _specular = _light.m_specular * _spec * vec3(texture(myMaterial.m_specular, outUV));
// 衰减系数计算
float _dist = length(_light.m_pos - _fragPos);
float _attenuation = 1.0f / (_light.m_c + _light.m_l * _dist + _light.m_q * _dist * _dist);

return (_ambient + _diffuse + _specular) * _attenuation * _intensity;
}

void main()
{
vec3 _normal = normalize(outNormal);
vec3 _viewDir = normalize(view_pos - outFragPos);

vec3 _result = calculateDir(_dirLight, _normal, _viewDir);

for (int i = 0; i < MAX_POINT_NUMBER; i++)
{
_result += calculatePoint(_pointLight[i], _normal, _viewDir, outFragPos);
}

_result += calculateSpot(_spotLight, _normal, _viewDir, outFragPos);

FragColor = vec4(_result, 1.0f);
};