LearnOpenGL学习笔记(三) - 变换与坐标

本文最后更新于 2024年7月15日 晚上

快乐的一天。

变换

缩放

[S10000S20000S300001](xyz1)=(S1xS2yS3z1)\begin{bmatrix} \color{red}{S_1} & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}{S_2} & \color{green}0 & \color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}{S_3} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} \color{red}{S_1} \cdot x \\ \color{green}{S_2} \cdot y \\ \color{blue}{S_3} \cdot z \\ 1 \end{pmatrix}

三维空间的变换矩阵一般都是四维矩阵。w分量与具体变换无关。

位移

[100Tx010Ty001Tz0001](xyz1)=(x+Txy+Tyz+Tz1)\begin{bmatrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}{T_x} \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}{T_y} \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}{T_z} \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} x + \color{red}{T_x} \\ y + \color{green}{T_y} \\ z + \color{blue}{T_z} \\ 1 \end{pmatrix}

可以看到,因为有了w分量,所以才能实现位移。

w分量被称为齐次坐标(Homogeneous Coordinates)。齐次坐标为0的向量被称为方向向量,它无法被位移。

旋转

[10000cosθsinθ00sinθcosθ00001](xyz1)=(xcosθysinθzsinθy+cosθz1)\begin{bmatrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}{\cos \theta} & - \color{green}{\sin \theta} & \color{green}0 \\ \color{blue}0 & \color{blue}{\sin \theta} & \color{blue}{\cos \theta} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} x \\ \color{green}{\cos \theta} \cdot y - \color{green}{\sin \theta} \cdot z \\ \color{blue}{\sin \theta} \cdot y + \color{blue}{\cos \theta} \cdot z \\ 1 \end{pmatrix}
[cosθsinθ00sinθcosθ0000100001](xyz1)=(cosθxsinθysinθx+cosθyz1)\begin{bmatrix} \color{red}{\cos \theta} & - \color{red}{\sin \theta} & \color{red}0 & \color{red}0 \\ \color{green}{\sin \theta} & \color{green}{\cos \theta} & \color{green}0 & \color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} \color{red}{\cos \theta} \cdot x - \color{red}{\sin \theta} \cdot y \\ \color{green}{\sin \theta} \cdot x + \color{green}{\cos \theta} \cdot y \\ z \\ 1 \end{pmatrix}
[cosθ0sinθ00100sinθ0cosθ00001](xyz1)=(cosθx+sinθzysinθx+cosθz1)\begin{bmatrix} \color{red}{\cos \theta} & \color{red}0 & \color{red}{\sin \theta} & \color{red}0 \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}0 \\ - \color{blue}{\sin \theta} & \color{blue}0 & \color{blue}{\cos \theta} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} \color{red}{\cos \theta} \cdot x + \color{red}{\sin \theta} \cdot z \\ y \\ - \color{blue}{\sin \theta} \cdot x + \color{blue}{\cos \theta} \cdot z \\ 1 \end{pmatrix}

上面三个矩阵分别是以x、y、z轴旋转θ°的旋转矩阵。

这些矩阵复合可能会导致万向节死锁。沿任意旋转轴旋转θ°的旋转矩阵如下:


[cosθ+Rx2(1cosθ)RxRy(1cosθ)RzsinθRxRz(1cosθ)+Rysinθ0RyRx(1cosθ)+Rzsinθcosθ+Ry2(1cosθ)RyRz(1cosθ)Rxsinθ0RzRx(1cosθ)RysinθRzRy(1cosθ)+Rxsinθcosθ+Rz2(1cosθ)00001]\begin{bmatrix} \cos \theta + \color{red}{R_x}^2(1 - \cos \theta) & \color{red}{R_x}\color{green}{R_y}(1 - \cos \theta) - \color{blue}{R_z} \sin \theta & \color{red}{R_x}\color{blue}{R_z}(1 - \cos \theta) + \color{green}{R_y} \sin \theta & 0 \\ \color{green}{R_y}\color{red}{R_x} (1 - \cos \theta) + \color{blue}{R_z} \sin \theta & \cos \theta + \color{green}{R_y}^2(1 - \cos \theta) & \color{green}{R_y}\color{blue}{R_z}(1 - \cos \theta) - \color{red}{R_x} \sin \theta & 0 \\ \color{blue}{R_z}\color{red}{R_x}(1 - \cos \theta) - \color{green}{R_y} \sin \theta & \color{blue}{R_z}\color{green}{R_y}(1 - \cos \theta) + \color{red}{R_x} \sin \theta & \cos \theta + \color{blue}{R_z}^2(1 - \cos \theta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

组合

当矩阵相乘时,在最右边的矩阵是第一个与向量相乘的,所以应该从右向左读矩阵乘法。

建议:在组合矩阵时,先进行缩放操作,然后是旋转,最后才是位移

GLM

GLM是在OpenGL中使用各类数学函数的头文件库。

1
2
3
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

glm::vec4 vec(x,y,z,w)用于创建一个名为vec的四维向量。

glm::mat4 trans = glm::mat4(1.0f)用于创建一个名为trans的四维矩阵,它是一个单位矩阵。

直接使用glm::mat4创建矩阵,会生成一个零矩阵。

当需要对一个向量进行变换操作时,用它乘以对应的变换矩阵即可。

平移

glm::translate(mat4, vec3)用于创建一个位移变换矩阵。mat4一般就是单位矩阵,vec3是位移向量。

缩放

glm::scale(mat4, vec3)用于创建缩放变换矩阵。mat4是单位矩阵,vec3是各轴缩放系数。

旋转

glm::rotate(mat4, radians, vec3)用于创建旋转矩阵。mat4是单位矩阵,radians是需要旋转的弧度,vec3是旋转轴。

radians是一个glm::radians类型变量,表示弧度。通过glm::radians(float angle)可把角度转换为弧度。

数据传递

定义mat4类型的uniform,使用如下代码传递:

1
2
3
4
5
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
//参数二:传递矩阵的个数
//参数三:是否转置。使用GLM(列主序)时无需转置
//参数四:把GLM矩阵转换为OpenGL可以读懂的类型
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

存在多个对象时,需要单独对每个对象进行变换。

1
2
3
4
5
6
7
8
9
10
11
12
shader.use();
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans,glm::vec3(-0.5f, 0.0f, 0.0f));
trans = glm::scale(trans,glm::vec3(sin(glfwGetTime()),sin(glfwGetTime()),0));
unsigned int transLoc = glGetUniformLocation(shader.ID,"trans");
glUniformMatrix4fv(transLoc,1,GL_FALSE,glm::value_ptr(trans));
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
trans = glm::mat4(1.0f);
trans = glm::translate(trans,glm::vec3(0.5f, -0.5f, 0.0f));
trans = glm::rotate(trans,100*glm::radians((float)glfwGetTime()),glm::vec3(0,0,1));
glUniformMatrix4fv(transLoc,1,GL_FALSE,glm::value_ptr(trans));
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);

坐标

通常情况下,顶点坐标不一定在NDC范围以内。我们需要在顶点着色器中自行把这些坐标转化为NDC坐标。

这个坐标转化的过程类似于流水线。

在坐标转换的过程中,有三个变换矩阵非常重要:

  • 模型(Model)矩阵
  • 观察(View)矩阵
  • 投影(Projection)矩阵

坐标变换以下列顺序进行:

局部坐标 -> 世界坐标 -> 观察坐标 -> 裁剪坐标 -> 屏幕坐标

coordinate_systems

坐标变换概述:

  1. 局部坐标是对象相对于局部原点的坐标,也是物体起始的坐标。
  2. 下一步是将局部坐标变换为世界空间坐标,世界空间坐标是处于一个更大的空间范围的。这些坐标相对于世界的全局原点,它们会和其它物体一起相对于世界的原点进行摆放。
  3. 接下来我们将世界坐标变换为观察空间坐标,使得每个坐标都是从摄像机或者说观察者的角度进行观察的。
  4. 坐标到达观察空间之后,我们需要将其投影到裁剪坐标。裁剪坐标会被处理至-1.0到1.0的范围内,并判断哪些顶点将会出现在屏幕上。
  5. 最后,我们将裁剪坐标变换为屏幕坐标,我们将使用一个叫做视口变换(Viewport Transform)的过程。视口变换将位于-1.0到1.0范围的坐标变换到由glViewport函数所定义的坐标范围内。最后变换出来的坐标将会送到光栅器,将其转化为片段。

局部空间

单个物体所在的坐标空间。只在单个物体上有意义。

世界空间

每个物体摆放的不同位置。

使用模型矩阵将局部左边转换为世界坐标。

观察空间

又称摄像机空间或视觉空间,是从摄像机的视角所观察到的空间。

使用观察矩阵,将世界坐标转换为观察坐标。

裁剪空间

对于任何屏幕上不可见的坐标,都应当被剔除。剔除完以后,剩下的坐标就是屏幕上可见的片段。

使用投影矩阵将观察坐标变换到裁剪坐标。投影矩阵指定了一个范围的坐标,比如在每个维度上的-1000到1000。投影矩阵接着会将在这个指定的范围内的坐标变换为标准化设备坐标的范围(-1.0, 1.0)。所有在范围外的坐标不会被映射到在-1.0到1.0的范围之间,所以会被裁剪掉。

若一个图元一部分在裁剪范围内,一部分在范围外,在交界处将会生成新的顶点。

投影矩阵创建了一个观察箱(Viewing Box),这个观察箱被称为平截头体(Frustum,Unity里称为视锥)。

投影(Projection)是把特定范围内的坐标转换到NDC范围内的过程。

所有顶点被变换到裁剪空间后,一次透视除法将会执行。具体表现为:位置向量的x,y,z分量分别除以齐次w分量。这一步骤由顶点着色器自动执行。

随后,最终坐标会被映射到屏幕空间中,并被变换为Fragment。

投影矩阵分为正交(Orthographic)和透视(Perspective)投影矩阵。

正交投影矩阵所框定的范围类似于一个长方体。变换后,每个向量的w分量都不会改变。

使用glm::ortho(left, right, bottom, top, near, far)创建正射投影矩阵。

前两个参数为Frustum的左右坐标,第三、第四个参数为底部和顶部。第五第六个参数为近平面和远平面。

投射投影矩阵所框定的范围类似于一个四边台体。它会修改每个顶点坐标的w值,使得离观察者越远的顶点坐标,w分量就越大。这样,在执行透视除法时,越远的顶点坐标,其x、y、z值会被除的越多,就好像被缩小了一样,从而达成“近大远小”的效果。

使用glm::mat4 proj = glm::perspective(glm::radians(angle), (float)width / (float)height, near, far)创建投影矩阵。

参数一定义了视野(Field of view,FOV)的值,通常为45.0f.

参数二定义了宽高比。

参数三、四为近平面和远平面。

组合

1
2
V_{clip} = M_{projection} \cdot M_{view} \cdot M_{model} \cdot V_{local}

经过上面的变换后,就能得到应当被赋给gl_Position的坐标。随后,Vertex Shader对其进行透视除法和裁剪。

实践

首先创建模型矩阵。一种直观理解模型矩阵的方式是,Unity中的Transform组件。

image-20240713165542375

模型矩阵对物体本身进行平移、旋转、缩放操作,对应Transform的三个属性。

要绘制世界坐标不同的物体的时候,只需要创建不同的模型矩阵即可。

1
2
3
glm::mat4 model;
//该模型矩阵让物体绕x轴旋转-55°
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));

随后创建观察矩阵。当没有观察矩阵时,摄像机处于世界空间原点。在使用上面那个模型矩阵变换的情况下,物体同样处在世界原点。所以我们要往后退,以看到物体。而往后退等价于让物体往后退,所以有观察矩阵:

1
2
3
4
glm::mat4 view;
// 注意,我们将矩阵向我们要进行移动场景的反方向移动。
// OpenGL使用右手坐标系,z轴正方向指向屏幕外。
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

最后创建投影矩阵。

1
2
glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);

然后修改Vertex Shader,并传入变换矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aCol;
layout (location = 2) in vec2 aTex;

out vec3 col;
out vec2 uv;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

void main(){
gl_Position = proj*view*model*vec4(aPos,1.0);
col = aCol;
uv = aTex;
}
1
2
3
4
5
6
7
unsigned int modelLoc = glGetUniformLocation(shader.ID,"model");
unsigned int viewLoc = glGetUniformLocation(shader.ID,"view");
unsigned int projLoc = glGetUniformLocation(shader.ID,"proj");
glUniformMatrix4fv(modelLoc,1,GL_FALSE,glm::value_ptr(model));
glUniformMatrix4fv(viewLoc,1,GL_FALSE,glm::value_ptr(view));
glUniformMatrix4fv(projLoc,1,GL_FALSE,glm::value_ptr(proj));
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);

Z-Buffer

绘制立方体时,使用Z-Buffer解决覆盖问题。

GLFW自动生成Z-Buffer存储深度信息。片段想要输出它的颜色时,OpenGL会将它的深度值和z缓冲进行比较,如果当前的片段在其它片段之后,它将会被丢弃,否则将会覆盖。这个过程称为深度测试(Depth Testing),它是由OpenGL自动完成的。

深度测试默认关闭,使用glEnable(GL_DEPTH_TEST)开启。

同时,深度缓冲也需要在每帧清除。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

绘制许多物体时,不妨使用for循环+改变model矩阵的方式。

完整代码

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
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "shader_s.h"
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
using namespace std;

float offsetX=0;
float offsetY=0;
float offsetZ=0;

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

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
};

void process_input(GLFWwindow* window) {
if(glfwGetKey(window,GLFW_KEY_RIGHT)==GLFW_PRESS) {
offsetX+=0.01f;
}
else if(glfwGetKey(window,GLFW_KEY_LEFT)==GLFW_PRESS) {
offsetX-=0.01f;
}
if(glfwGetKey(window,GLFW_KEY_UP)==GLFW_PRESS) {
offsetY+=0.01f;
}
else if(glfwGetKey(window,GLFW_KEY_DOWN)==GLFW_PRESS) {
offsetY-=0.01f;
}
}

void mouse_scroll_zoom(GLFWwindow* window, double xoffset, double yoffset) {
if(yoffset>0) {
offsetZ+=0.1f;
}
else if(yoffset<0) {
offsetZ-=0.1f;
}
}

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

GLFWwindow* sys_init(int width, int height) {
//glfw初始化
glfwInit();
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,"LearnOpenGL",NULL,NULL);
if(window==NULL) {
cout<<"Failed to create window"<<endl;
glfwTerminate();
return NULL;
}
glfwMakeContextCurrent(window);
if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
cout<<"Failed to load proc"<<endl;
glfwTerminate();
return NULL;
}
glViewport(0,0,800,600);
glfwSetFramebufferSizeCallback(window,frame_buffer_size_callback);
glfwSetScrollCallback(window,mouse_scroll_zoom);
return window;
}

void quick_config_buffers(unsigned int GLTYPE, unsigned int* handle, void* data, unsigned int size) {
glGenBuffers(1, handle);
glBindBuffer(GLTYPE, *handle);
glBufferData(GLTYPE, size, data, GL_STATIC_DRAW);
}

void quick_config_vao(unsigned int *VAO, unsigned int *VBO, unsigned int *EBO) {
glGenVertexArrays(1, VAO);
glBindVertexArray(*VAO);

glBindBuffer(GL_ARRAY_BUFFER, *VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *EBO);

// 激活顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

glBindVertexArray(0); // Unbind VAO
}

unsigned int quick_config_texture(const char* path,unsigned int IMAGETYPE) {
unsigned int texture;
stbi_set_flip_vertically_on_load(true);
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D,texture);
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
int width,height,channels;
unsigned char* data = stbi_load(path, &width, &height,&channels,0);
if(data) {
glTexImage2D(GL_TEXTURE_2D,0,IMAGETYPE,width,height,0,IMAGETYPE,GL_UNSIGNED_BYTE,data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
cout<<"Failed to load image"<<endl;
return 0;
}
stbi_image_free(data);
return texture;
}

int main() {
//初始化系统与函数加载
GLFWwindow* window = sys_init(800,600);
if(window==NULL) return -1;
//加载着色器
Shader shader("Shaders/vertex.glsl","Shaders/fragment.glsl");
//配置VBO、EBO、VAO
unsigned int VBO, EBO, VAO;
quick_config_buffers(GL_ARRAY_BUFFER, &VBO, vertices, sizeof(vertices));
quick_config_vao(&VAO, &VBO, &EBO);
//加载、配置纹理
unsigned int textures[2];
textures[0]=quick_config_texture("Imgs/container.jpg",GL_RGB);
textures[1]=quick_config_texture("Imgs/awesomeface.png",GL_RGBA);
shader.use();
shader.setInt("texture1",0);
shader.setInt("texture2",1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,textures[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,textures[1]);
glEnable(GL_DEPTH_TEST);
while(!glfwWindowShouldClose(window)) {
process_input(window);
glClearColor(0.2f,0.3f,0.4f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use();
//变换
glm::mat4 view = glm::mat4(1.0f);
view = glm::translate(view,glm::vec3(0.0f+offsetX,0.0f+offsetY,-3.0f+offsetZ));
glm::mat4 proj = glm::perspective(glm::radians(45.0f),800.0f/600.0f,0.1f,100.0f);
shader.setMat4("view",view);
shader.setMat4("proj",proj);
glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++)
{
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, cubePositions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, (float)glfwGetTime()*glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
//收尾操作
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteTextures(2,textures);
glfwTerminate();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTex;

out vec2 uv;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

void main(){
gl_Position = proj*view*model*vec4(aPos,1.0);
uv = aTex;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTex;

out vec2 uv;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

void main(){
gl_Position = proj*view*model*vec4(aPos,1.0);
uv = aTex;
}

LearnOpenGL学习笔记(三) - 变换与坐标
http://example.com/2024/07/13/LearnOpenGL学习笔记(三) - 变换与坐标/
作者
Yoi
发布于
2024年7月13日
许可协议