【OpenGL学习笔记⑦】——键盘控制镜头的平移【3D正方体 透视投影 观察矩阵 对LookAt的理解】


🍻 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.1 摄影机的位置

  ●摄像机位置简单来说就是世界空间中一个指向摄像机位置的向量。我们把摄像机位置设置为如下,图在 1.4 中

glm::vec3 cameraPos = glm::vec3(0.0f, 1.0f, -5.0f);

  ◆ :z 轴是指向屏幕内部的(在小编自己探索所设定的左手坐标系,详见 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 ⭐实现地月系统⭐ 旋转+平移+缩放】,如果我们希望摄像机向后移动,我们就沿着 z 轴的负方向移动。

1.2 摄影机的朝向向量

  ● 摄像机的方向:指的是摄像机指向哪个方向。现在我们让摄像机指向场景原点:(0, 0, 0)。

glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);


1.3 摄影机的正视向量

  ● 小编研究很久也不太懂这个向量,姑且叫它 “在世界坐标系里的正视向量” 吧。【在几乎所有正常情况下,一般设为(0,1,0),即正视效果】

glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); 


1.4 摄影机世界的 Z、X、Y 轴向量

  ● 在摄影机的视角里面,我们把摄影机作为 “新的坐标原点”,建立如下的 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()是叉乘函数【这里遵循右手原则】。还有就是,用叉乘这个想法确实妙,如果你能和我一样感受到上面三个公式的漂亮,那 🤝


1.5 LookAt 矩阵

  ● 现在我们虽然设置了摄影机的位置和三个坐标向量,那接下来,我们要定义出一个 “转换矩阵”(在官网一般叫这个为 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);

在这里插入图片描述



三、键盘互动(让摄影机随键盘移动)

3.1 前后左右上下移动的实现

  ● 要让摄像机移动,那首先我们必须设置一个摄像机系统,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.2 在顶点着色器里的处理

  ● 在顶点着色器中,我们要将 “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 类

  ● 在上面我们已经理清楚了摄影机的各种原理,接下来可以直接将其进行封装,成为一个类,方便我们后面调用。我们将其命名为 “Camera.h”,设置为头文件

#include <iostream>
using namespace std;
#include <glew.h>
#include <glfw3.h>
#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 <iostream>
using namespace std;
#define GLEW_STATIC	
#include"Shader.h"
#include"Camera.h"
#include<glew.h>					// 注:这一部分要根据个人情况进行设定
#include<glfw3.h>
#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.


⭐️ ⭐️

  • 18
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一支王同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值