1024 程序员节
移动的镜头
上一篇文章链接:【OpenGL学习笔记⑥】——3D变换【旋转的正方体 ⭐实现地月系统⭐ 旋转+平移+缩放】.
下一篇文章链接:【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】.
OpenGL总学习目录: https://blog.csdn.net/Wang_Dou_Dou_/article/details/121240714.
● 说明:依次实现摄像机的 前进、后退、左移、右移、上移、下移 功能。
● 在设置一个摄影机之前,我们需要学习透视投影(Perspective Projection)。
● 首先,我们简单来看一下有透视投影和没有的区别:【可以发现,右边的立方体更自然】
● 透视(Perspective):正如实际生活的景象,离我们越远的东西看起来更小。这个奇怪的效果称之为透视(Perspective)。透视的效果在我们看一条无限长的高速公路或铁路时尤其明显,正如下面图片显示的那样:
● 说明:由于透视,这两条线在很远的地方看起来会相交。这正是透视投影想要模仿的效果,它是使用透视投影矩阵来完成的。
● 要创建一个透视投影矩阵,我们需要用到 glm::perspective()
函数,其所做的就是创建了一个定义了可视空间的大平截头体,任何在这个平截头体以外的东西最后都不会出现在裁剪空间体积内,并且将会受到裁剪。(一个透视平截头体可以被看作一个不均匀形状的箱子,在这个箱子内部的每个坐标都会被映射到裁剪空间上的一个点)。下面是一张透视平截头体的图片:
● 在 GLM 中可以这样创建一个透视投影矩阵:
glm::mat4 projection_1 = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
● 参数说明:(所有在近平面和远平面内且处于平截头体内的顶点都会被渲染)
① 第一个参数:表示 fov 的值(视野的 Field of View 的缩写),即观察空间的大小。通常设置为 45.0f(一个真实的观察效果)。
② 第二个参数:宽高比。可由视口的宽除以高所得。
③ 第三个参数:平截头体的近平面,通常设置为 0.1f 。
④ 第四个参数:平截头体的远平面,通常设置为 100.0f 。
● 在学习完透视矩阵后,我们接下来弄的这个摄影机照出来的镜头就会很 “自然”。
● OpenGL 本身没有摄像机(Camera)的概念,但我们可以通过把场景中的所有物体往相反方向移动的方式来 “模拟” 出摄像机。(就像我们坐在一辆动车里面,当窗外对面的动车向后走的时候,我们会有 “我们的动车在前进” 的感觉)
● 当我们要设置一个摄像机(或观察空间)的时候,我们需要把所有的世界坐标变换为相对于摄像机的观察坐标。即创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。
●摄像机位置简单来说就是世界空间中一个指向摄像机位置的向量。我们把摄像机位置设置为如下,图在 1.4 中:
glm::vec3 cameraPos = glm::vec3(0.0f, 1.0f, -5.0f);
◆ 注:z 轴是指向屏幕内部的(在小编自己探索所设定的左手坐标系,详见 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 ⭐实现地月系统⭐ 旋转+平移+缩放】),如果我们希望摄像机向后移动,我们就沿着 z 轴的负方向移动。
● 摄像机的方向:指的是摄像机指向哪个方向。现在我们让摄像机指向场景原点:(0, 0, 0)。
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
● 小编研究很久也不太懂这个向量,姑且叫它 “在世界坐标系里的正视向量” 吧。【在几乎所有正常情况下,一般设为(0,1,0),即正视效果】
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
● 在摄影机的视角里面,我们把摄影机作为 “新的坐标原点”,建立如下的 camera坐标体系:【注意:这和上一篇文章的那个坐标系有所不同,小编也不知道为什么,但事实是这样的】
● 代码如下:
camera_Z_axis = glm::normalize(cameraTarget);
camera_X_axis = glm::normalize(glm::cross(cameraTarget, cameraUp));
camera_Y_axis = glm::normalize(glm::cross(camera_X_axis, cameraTarget));
◆ 说明:normalize()
是归一化函数,cross()
是叉乘函数【这里遵循右手原则】。还有就是,用叉乘这个想法确实妙,如果你能和我一样感受到上面三个公式的漂亮,那 !
● 现在我们虽然设置了摄影机的位置和三个坐标向量,那接下来,我们要定义出一个 “转换矩阵”(在官网一般叫这个为 LookAt 矩阵),用它来把世界坐标系里面的 东西的位置、向量 变换到 camera的观察空间。我们用 3 个相互垂直的轴和摄像机的世界坐标创建 LookAt 矩阵:
glm::mat4 view_1 = LookAt(this->position, this->position + this->camera_Z_axis, this->camera_Y_axis) // 观察矩阵
● 参数说明:【注:该函数在 “Camera 类” 里面,所以有 “this->”,这将在后面细讲 】
① 第一个参数:相机位置【在世界坐标系里的位置】
② 第二个参数:目标位置【即摄像机镜头看向的位置,一般设为(0, 0, 0),即看向中心位置】
③ 第三个参数:在 camera坐标系 里的正视向量【一般设为(0,1,0)】
● 假如我们要实现下图中的效果,则摄影机的相关参数需设置如下:【注:相机一开始的默认位置为(0, 0, 0),看向 z 轴负方向,和电脑屏幕看向我们一样】
第一个参数 = glm::vec3(0.0f, 1.0f, -5.0f);
第二个参数 = glm::vec3(0.0f, 0.0f, 0.0f);
第三个参数 = glm::vec3(0.0f, 1.0f, 0.0f);
● 要让摄像机移动,那首先我们必须设置一个摄像机系统,view_1 是一 “观察矩阵”,地位和 transform (变换矩阵)相同,LookAt 函数现在成了:
glm::mat4 view_1 = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); // 观察矩阵
◆ 说明:我们首先将摄像机位置设置为之前定义的 cameraPos 。方向是当前的位置加上我们刚刚定义的方向向量。这样能保证无论我们怎么移动,摄像机都会注视着目标方向。
● 要实现上述功能,我们需要写如下 6 段代码:
① 在主函数中,这是一个全局变量,bool 型变量只用来存储某一个键是否按下,按下为 true,未按下为 false,具体用途在 ③ 。
bool keys[1024]; // 专门存储按过的键(定义为全局变量)
② 该函数定义在 main()
内,用来告诉 glfw ,窗口会有键盘输入,方便进行后面的联动响应【KeyCallback
是一个函数名,详见 ③ 】
glfwSetKeyCallback(window_1, KeyCallback);
③ 定义KeyCallback()
函数,注意这是固定格式。(和快排的sort()
函数的cmp
内置函数定义流程类似)
void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mode) // 固定格式
{
if( key == GLFW_KEY_ESCAPE && action == GLFW_PRESS )
{
glfwSetWindowShouldClose(window, GL_TRUE); // 触发窗口关闭的机制
cout << "按下了窗口关闭键 esc = "<< key << endl;
}
if( key >= 0 && key <= 1024 )
{
if( action == GLFW_PRESS )
keys[key] = true; // true 代表按下了键
else if( action == GLFW_RELEASE )
keys[key] = false; // true 代表释放了键
}
}
④ 再在 “draw loop 画图的 while 循环” 里面定义一个与 “Camera 类” 进行链接的调用函数(该摄像机类会在后面讲),故 camera
就是 “Camera 类” 的一个实例,ProcessKeyboard()
是 “Camera 类” 里的一个按键处理函数,也是我们自己定义的,在 ⑤ 中详写。
void Key_Movement()
{
if( keys[GLFW_KEY_Q]) // 向前, 按 Q 键
camera.ProcessKeyboard(FORWARD, deltaTime);
if( keys[GLFW_KEY_E] ) // 向后, 按 E 键
camera.ProcessKeyboard(BACKWARD, deltaTime);
if( keys[GLFW_KEY_A] ) // 向左, 按 A 键
camera.ProcessKeyboard(LEFT, deltaTime);
if( keys[GLFW_KEY_D] ) // 向右, 按 D 键
camera.ProcessKeyboard(RIGHT, deltaTime);
if( keys[GLFW_KEY_W] ) // 向上, 按 W 键
camera.ProcessKeyboard(UPWARD, deltaTime);
if( keys[GLFW_KEY_S] ) // 向下, 按 S 键
camera.ProcessKeyboard(DOWNWARD, deltaTime);
}
⑤ Camera_Movement
是枚举枚举类型,在“Camera.h”头文件里面。而ProcessKeyboard()
在 “Camera 类” 里。deltaTime 是一个关于时间变量,在主函数中计算而得出,在 ⑥ 中详写。注:velocity 的英文是 “速度” 。
enum Camera_Movement { // 枚举类型
FORWARD, // 向前
BACKWARD, // 向后
LEFT, // 向左
RIGHT, // 向右
UPWARD, // 向上
DOWNWARD // 向下
};
void ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = this->movementSpeed * deltaTime;
if(direction == FORWARD)
this->position += this->camera_Z_axis * velocity;
if(direction == BACKWARD)
this->position -= this->camera_Z_axis * velocity;
if(direction == LEFT)
this->position -= this->camera_X_axis * velocity;
if(direction == RIGHT)
this->position += this->camera_X_axis * velocity;
if(direction == UPWARD)
this->position += this->camera_Y_axis * velocity;
if(direction == DOWNWARD)
this->position -= this->camera_Y_axis * velocity;
}
◆ 原理说明: 当我们按下 W、S、Q、E、A、D 键的任意一个,摄像机的位置都会相应更新。
<1> 向前移动: 当 前 位 置 向 量 + Z 轴 的 单 位 向 量 ∗ 速 度 当前位置向量 + Z轴的单位向量 * 速度 当前位置向量+Z轴的单位向量∗速度
<2> 向后移动: 当 前 位 置 向 量 − Z 轴 的 单 位 向 量 ∗ 速 度 当前位置向量 - Z轴的单位向量 * 速度 当前位置向量−Z轴的单位向量∗速度
<3> 向左移动: 当 前 位 置 向 量 − X 轴 的 单 位 向 量 ∗ 速 度 当前位置向量 - X轴的单位向量 * 速度 当前位置向量−X轴的单位向量∗速度
<4> 向右移动: 当 前 位 置 向 量 + X 轴 的 单 位 向 量 ∗ 速 度 当前位置向量 + X轴的单位向量 * 速度 当前位置向量+X轴的单位向量∗速度
<5> 向上移动: 当 前 位 置 向 量 + Y 轴 的 单 位 向 量 ∗ 速 度 当前位置向量 + Y轴的单位向量 * 速度 当前位置向量+Y轴的单位向量∗速度
<6> 向下移动: 当 前 位 置 向 量 − Y 轴 的 单 位 向 量 ∗ 速 度 当前位置向量 - Y轴的单位向量 * 速度 当前位置向量−Y轴的单位向量∗速度
⑥ deltaTime 和 lastTime 都是全局变量,在主函数中。通过如下处理,我们就能实现 “按着键时,摄像机一直移动” 的功能。
GLfloat deltaTime = 0.0f;
GLfloat lastTime = 0.0f;
/* 以下函数在 draw loop 画图的 while 循环中 */
GLfloat currentTime = glfwGetTime();
deltaTime = currentTime - lastTime;
lastTime = currentTime;
● 在顶点着色器中,我们要将 “3.1中的观察矩阵(view_1)” 传入顶点着色器中,这样才能实现 “按键→摄像机移动”。 g l _ P o s t i o n = 观 察 矩 阵 M v i e w × 透 视 投 影 矩 阵 p r o j e c t i o n × 变 换 矩 阵 t r a n s f o r m × 原 始 行 向 量 gl\_Postion={观察矩阵M}_{view}\times {透视投影矩阵}_{projection}\times {变换矩阵}_{transform}\times 原始行向量 gl_Postion=观察矩阵Mview×透视投影矩阵projection×变换矩阵transform×原始行向量
#version 330 core
layout (location = 0) in vec3 position;
layout(location = 1) in vec3 color; // 颜色变量的顶点属性位置值为 1
out vec3 ourColor;
uniform mat4 transform_1;
uniform mat4 projection_1;
uniform mat4 view_1;
void main()
{
ourColor = color;
gl_Position = projection_1 * view_1 * transform_1 * vec4(position, 1.0f); // 注意矩阵乘法要从右向左读
}
◆ 注:此处只能用 “gl_Position ” 这个变量名。OpenGL 后续将会自动进行透视除法和裁剪。
● 在上面我们已经理清楚了摄影机的各种原理,接下来可以直接将其进行封装,成为一个类,方便我们后面调用。我们将其命名为 “Camera.h”,设置为头文件。
#include
using namespace std;
#include
#include
#include "glm\glm.hpp"
#include "glm\gtc\matrix_transform.hpp"
enum Camera_Movement { // 枚举类型
FORWARD, // 向前
BACKWARD, // 向后
LEFT, // 向左
RIGHT, // 向右
UPWARD, // 向上
DOWNWARD // 向下
};
const GLfloat SPEED = 6.0f; // 初始速度
class Camera
{
public:
// 构造函数
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 5.0f) ,glm::vec3 target = glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f)) :movementSpeed(SPEED)
{
this->position = position;
this->camera_Z_axis = target;
this->camera_Y_axis = up;
this->camera_X_axis = glm::normalize(glm::cross(this->camera_Z_axis, this->camera_Y_axis));
this->updateCameraVectors(); // 实时更新
}
// 观察矩阵
glm::mat4 GetViewMatrix()
{
return glm::lookAt(this->position, this->position + this->camera_Z_axis, this->camera_Y_axis);
}
void ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = this->movementSpeed * deltaTime;
if(direction == FORWARD)
this->position += this->camera_Z_axis * velocity;
if(direction == BACKWARD)
this->position -= this->camera_Z_axis * velocity;
if(direction == LEFT)
this->position -= this->camera_X_axis * velocity;
if(direction == RIGHT)
this->position += this->camera_X_axis * velocity;
if(direction == UPWARD)
this->position += this->camera_Y_axis * velocity;
if(direction == DOWNWARD)
this->position -= this->camera_Y_axis * velocity;
}
glm::vec3 GetPosition() // 下一篇文章使用
{
return this->position;
}
private:
// 摄影机的属性
glm::vec3 position; // 相机当前位置
glm::vec3 camera_Z_axis; // 摄影机的 Z 轴向量
glm::vec3 camera_X_axis; // 摄影机的 X 轴向量
glm::vec3 camera_Y_axis; // 摄影机的 Y 轴向量
GLfloat movementSpeed; // 镜头移动速度
void updateCameraVectors()
{
this->camera_Z_axis = glm::normalize(this->camera_Z_axis);
this->camera_X_axis = glm::normalize(glm::cross(this->camera_Z_axis, this->camera_Y_axis));
this->camera_Y_axis = glm::normalize(glm::cross(this->camera_X_axis, this->camera_Z_axis));
}
};
● 片元着色器代码如下:
#version 330 core
in vec3 ourColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(ourColor,1.0f);
}
● 另一个头文件 Shader.h 依旧沿用第③篇中的代码【OpenGL学习笔记③】——⭐着色器【GLSL Uniform 彩色三角形 变色正方形】⭐主函数如下:
/* 引入相应的库 */
#include
using namespace std;
#define GLEW_STATIC
#include"Shader.h"
#include"Camera.h"
#include // 注:这一部分要根据个人情况进行设定
#include
#include"SOIL2\include\stb_image.h"
#include"SOIL2\include\SOIL2.h"
#include"glm\glm.hpp"
#include"glm\gtc\matrix_transform.hpp"
#include"glm\gtc\type_ptr.hpp"
/* 编写各顶点位置 */
float vertices_1[] = {
// x、y、z 坐标 // color
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // red 红色面
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // green 绿色面
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // blue 蓝色面
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, // yellow 黄色面
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, // purple 紫色面
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 1.0f, // cyan 青色面
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
};
const GLint WIDTH = 600, HEIGHT = 600;
bool keys[1024]; // 专门存储按过的键
Camera camera(glm::vec3(1.0f, 1.0f, -5.0f),glm::vec3(-1.0f, -1.0f, 5.0f), glm::vec3(0.0f, 1.0f, 0.0f));
void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mode); // 对键盘的响应函数
void Key_Movement(); // 与 Camera 类互动的函数
GLfloat deltaTime = 0.0f;
GLfloat lastTime = 0.0f;
int main()
{
/* 初始化 glfw */
glfwInit();
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // 缩放关闭
/* 窗口捕获与处理 */
GLFWwindow* window_1 = glfwCreateWindow(WIDTH, HEIGHT, "NJUPT_Learn OpenGL Key Test", nullptr, nullptr);
int screenWidth_1, screenHeight_1;
glfwGetFramebufferSize(window_1, &screenWidth_1, &screenHeight_1);
cout << "screenWidth_1 = " << screenWidth_1 << ", screenHeight = " << screenHeight_1 << endl;
glfwMakeContextCurrent(window_1);
glfwSetKeyCallback(window_1, KeyCallback); // 注册到 glfw 里面,会进行联动响应
/* 初始化 glew */
glewInit();
/* 开启深度测试 */
glEnable(GL_DEPTH_TEST);
/* 将我们自己设置的着色器文本传进来 */
Shader ourShader = Shader("shader_v.txt", "shader_f.txt"); // 相对路径
/* 设置顶点缓冲对象(VBO) + 设置顶点数组对象(VAO) */
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_1), vertices_1, GL_STATIC_DRAW);
/* 设置链接顶点属性 */
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0); // 通道 0 打开
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)));
glEnableVertexAttribArray(1); // 通道 1 打开
/* draw loop 画图循环 */
while (!glfwWindowShouldClose(window_1))
{
GLfloat currentTime = glfwGetTime();
deltaTime = currentTime - lastTime;
lastTime = currentTime;
/* 视口 + 时间 */
glViewport(0, 0, screenWidth_1, screenHeight_1);
glfwPollEvents(); // 获取键盘鼠标
Key_Movement(); // 获取键盘的小动作
/* 渲染 + 清除颜色缓冲 */
glClearColor(0.5f, 0.8f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* 绘制图形 */
ourShader.Use(); // 调用着色器程序
glBindVertexArray(VAO); // 绑定 VAO
for(int i = 0; i < 2; i++) // 准备绘制两个正方体
{
glm::mat4 transform_1;
glm::mat4 view_1 = camera.GetViewMatrix(); // 求得观察矩阵
if( i == 0 ) // 大立方体
{
transform_1 = glm::translate(transform_1, glm::vec3(0.0f, 0.0f, 0.0f));
float new_size = cos(currentTime) * 0.2f + 0.8f;
transform_1 = glm::scale(transform_1, glm::vec3(new_size, new_size, new_size));
}
else // 小立方体
{
transform_1 = glm::translate(transform_1, glm::vec3(0.0f, 1.0f, 0.0f));
transform_1 = glm::rotate(transform_1, currentTime, glm::vec3(0.2f, 1.0f, 0.0f));
transform_1 = glm::scale(transform_1, glm::vec3(0.15f, 0.15f, 0.15f));
}
glm::mat4 projection_1 = glm::perspective(glm::radians(45.0f), (float)screenWidth_1/(float)screenHeight_1, 0.1f, 100.0f);
int transform_1_Location = glGetUniformLocation(ourShader.Program, "transform_1");
glUniformMatrix4fv(transform_1_Location, 1, GL_FALSE, glm::value_ptr(transform_1));
int projection_1_Location = glGetUniformLocation(ourShader.Program, "projection_1");
glUniformMatrix4fv(projection_1_Location, 1, GL_FALSE, glm::value_ptr(projection_1));
int view_1_Location = glGetUniformLocation(ourShader.Program, "view_1");
glUniformMatrix4fv(view_1_Location, 1, GL_FALSE, glm::value_ptr(view_1));
// 第一个参数在 vs.txt 中,第二个在主函数中
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glBindVertexArray(0); // 解绑定 VAO
/* 交换缓冲 */
glfwSwapBuffers(window_1);
}
/* 释放资源 */
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate(); // 结束
return 0;
}
void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mode)
{
if( key == GLFW_KEY_ESCAPE && action == GLFW_PRESS )
{
glfwSetWindowShouldClose(window, GL_TRUE);
cout << "按下了关闭键 esc = "<< key << endl;
}
if( key >= 0 && key <= 1024 )
{
if( action == GLFW_PRESS )
keys[key] = true; // true 代表按下了键
else if( action == GLFW_RELEASE )
keys[key] = false;
}
}
void Key_Movement()
{
if( keys[GLFW_KEY_Q]) // 向前
camera.ProcessKeyboard(FORWARD, deltaTime);
if( keys[GLFW_KEY_E] ) // 向后
camera.ProcessKeyboard(BACKWARD, deltaTime);
if( keys[GLFW_KEY_A] ) // 向左
camera.ProcessKeyboard(LEFT, deltaTime);
if( keys[GLFW_KEY_D] ) // 向右
camera.ProcessKeyboard(RIGHT, deltaTime);
if( keys[GLFW_KEY_W] ) // 向上
camera.ProcessKeyboard(UPWARD, deltaTime);
if( keys[GLFW_KEY_S] ) // 向下
camera.ProcessKeyboard(DOWNWARD, deltaTime);
}
● 运行结果:
[1] 《Learn OpenGL——摄像机》
链接: https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/.
[2] 《Learn OpenGL——坐标系统》
链接: https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/.
[3] 《Understanding glm::lookAt()》
链接: https://stackoverflow.com/questions/21830340/understanding-glmlookat.
[4] 《归一化函数normalize详解》
链接: https://blog.csdn.net/qq_36930777/article/details/78301433.
上一篇文章链接:【OpenGL学习笔记⑥】——3D变换【旋转的正方体 ⭐实现地月系统⭐ 旋转+平移+缩放】.
下一篇文章链接:【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】.
OpenGL总学习目录: https://blog.csdn.net/Wang_Dou_Dou_/article/details/121240714.
⭐️ ⭐️