【OpenGL学习笔记⑨】——鼠标控制镜头 + 滚轮控制镜头缩放



文章目录

  • 零、 成果预览图
  • 一、对欧拉角的理解
  • 二、鼠标输入
  • 三、滚轮缩放
  • 四、完整代码
  • 五、参考附录:


Mouse

上一篇文章链接: 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】.
下一篇文章链接:
OpenGL总学习目录: https://blog.csdn.net/Wang_Dou_Dou_/article/details/121240714.


零、 成果预览图

【OpenGL学习笔记⑨】——鼠标控制镜头 + 滚轮控制镜头缩放_第1张图片

  ● 说明:实现 鼠标移动环绕正方体一周 + 鼠标滚轮缩放 两个效果功能。【注:移动鼠标时要和键盘移动一起配合使用才有以上效果。但小编还不够熟练,导致最后转过来时,正方体未正放】



一、对欧拉角的理解

  ● 欧拉角(Euler Angle)是可以表示 3D 空间中任何旋转的 3 个值,由莱昂哈德·欧拉(Leonhard Euler)在18世纪提出。一共有 3 种欧拉角:俯仰角(Pitch)偏航角(Yaw)滚转角(Roll),下面的图片展示了它们的含义:

【OpenGL学习笔记⑨】——鼠标控制镜头 + 滚轮控制镜头缩放_第2张图片
  ◆ 说明
    ① 俯仰角是描述我们如何往上或往下看的角,可以在第一张图中看到。
    ② 第二张图展示了偏航角,偏航角表示我们往左和往右看的程度。
    ③ 滚转角代表我们如何翻滚摄像机。


  ● 在我们的摄像机所拍摄的世界坐标系里,这 3 种欧拉角体现如下:

【OpenGL学习笔记⑨】——鼠标控制镜头 + 滚轮控制镜头缩放_第3张图片

  ◆ 说明
    ① 每个欧拉角都有一个值来表示,把三个角结合起来我们就能够计算 3D 空间中任何的旋转向量了。
    ② 如上图中所示,只要把三架纸飞机的 3 个欧拉角结合起来就是我们最终想要的旋转角度。

  ● 对于我们的摄像机系统来说,我们只关心俯仰角和偏航角,所以我们不会讨论滚转角。因为我们也不经常 “歪着脑袋” 看世界。



二、鼠标输入

  ● 偏航角和俯仰角是通过鼠标移动获得的,水平的移动影响偏航角,竖直的移动影响俯仰角。

  ● 它的原理就是,储存上一帧鼠标的位置,在当前帧中我们当前计算鼠标位置与上一帧的位置相差多少。如果水平/竖直差别越大那么俯仰角或偏航角就改变越大。

  ● 首先我们要告诉 GLFW,它应该隐藏光标,并捕捉(Capture)它。我们可以用一个简单地配置调用来完成:

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

  ◆ 函数说明
    ① 第一个参数:当前窗口
    ② 第二个参数:指示引入 GLFW 的光标机制
    ③ 第三个参数:指示隐藏光标


  ● 为了计算俯仰角和偏航角,我们需要让 GLFW 监听鼠标移动事件。(和键盘输入相似)我们会用一个回调函数来完成,函数的原型如下:

void mouse_callback(GLFWwindow* window, double xpos, double ypos);

  ◆ 函数说明
    ① 第一个参数:当前窗口
    ② 第二、三个参数:这里的 xpos 和 ypos 代表当前鼠标的位置。

  ● 当我们用 GLFW 注册了回调函数之后,鼠标一移动mouse_callback()函数就会被调用:

glfwSetCursorPosCallback(window, mouse_callback);

  ◆ 函数说明
    ① 第一个参数:当前窗口
    ② 第二个参数:当光标移动时,会自动调用的函数(这里写函数名即可)。


  ● 在处理摄像机的鼠标输入的时候,为最终获取方向向量,我们一般会进行如下工作:
    ① 计算鼠标距上一帧的偏移量。
    ② 把偏移量添加到摄像机的俯仰角和偏航角中。
    ③ 对偏航角和俯仰角进行最大和最小值的限制。
    ④ 计算方向向量。

  ● 第一步是计算鼠标自上一帧的偏移量。我们需在程序中储存上一帧的鼠标位置,我们把它的初始值设置为屏幕的中心(假设窗口的尺寸是 800x600 ):

float lastX = 400, lastY = 300;

  ● 然后在鼠标的回调函数中我们计算当前帧和上一帧鼠标位置的偏移量:

"主函数中的 mouse_callback() 中有以下内容:"
float xoffset = xpos - lastX;		// 计算 x 方向的偏移量
float yoffset = lastY - ypos; 		// 计算 y 方向的偏移量
lastX = xpos;						
lastY = ypos;

float sensitivity = 0.05f;			// 鼠标灵敏度的设置
xoffset *= sensitivity;
yoffset *= sensitivity;

--------------------------------------------------------------------
"Camera类中有以下内容:"
pitch += yoffset;				// 俯仰角加上 y 方向上的偏移量
yaw   += xoffset;				// 偏航角加上 x 方向上的偏移量

...
if(pitch > 89.0f)
  pitch =  89.0f;
if(pitch < -89.0f)
  pitch = -89.0f;
...

"以下代码在 Camera类中 updateCameraVectors() 函数中:"
glm::vec3 front;
front.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));
front.y = sin(glm::radians(pitch));
front.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
cameraFront = glm::normalize(front);
...

  ◆ 代码说明
    ① 两个if()条件语句是给摄像机添加的一些限制,这样摄像机就不会出现 360° 无死角翻转的效果,也就是不会出现 “倒立着看大地” 的情况。这个限制只关乎俯仰角。
    ② front是摄像机指向方向的向量。cos()sin()等计算是三角形函数的一些推导过程。我们可以不用去了解这些推导过程。


  ● 在运行代码时,你会发现在窗口第一次获取焦点的时候摄像机会突然跳一下。这个问题产生的原因是,在你的鼠标移动进窗口的那一刻,鼠标回调函数就会被调用,这时候的 xpos 和 ypos 会等于鼠标刚刚进入屏幕的那个位置。这通常是一个距离屏幕中心很远的地方,因而产生一个很大的偏移量,所以就会跳了。我们可以简单的使用一个 bool 变量检验我们是否是第一次获取鼠标输入,如果是,那么我们先把鼠标的初始位置更新为 xpos 和 ypos 值,这样就能解决这个问题。

  ● 最后的mouse_callback()函数如下:

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)		// firstMouse 是一个 bool 变量,一开始为初始化为 true
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; 

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);		
}


三、滚轮缩放

  ● 最后,我们希望通过鼠标滚轮的滑动来控制时角的放大与缩小,这就涉及到第 ⑦ 篇文章讲的投影矩阵。

  ● 在 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 。


  ● 这里,我们将其改为:【其中 fov 是视野 (Field of View) 的缩写,后面我们会改写为 camera.GetZoom() 】

"绘图循环中有以下代码:"
glm::mat4 projection_1 = glm::perspective(glm::radians(fov), (float)width/(float)height, 0.1f, 100.0f);

------------------------------------------------------------------------------------------------
"主函数中的 scroll_callback() 中有以下内容:"
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
  if(fov >= 1.0f && fov <= 45.0f)
    fov -= yoffset;
  if(fov <= 1.0f)
    fov = 1.0f;
  if(fov >= 45.0f)
    fov = 45.0f;
}

  ◆ 代码说明
    ① 当滚动鼠标滚轮的时候,yoffset 值代表我们竖直滚动的大小。
    ② 当 scroll_callback 函数被调用后,我们会改变全局变量 fov 变量的内容。
    ③ 因为 45.0f 是默认的视野值,我们将会把缩放级别(Zoom Level)限制在 1.0f 到 45.0f 。


  ● 当然,和键盘输入、鼠标输入一样,滚轮输入也要注册鼠标滚轮的回调函数

glfwSetScrollCallback(window, scroll_callback);

  ◆ 函数说明
    ① 第一个参数:当前窗口
    ② 第二个参数:当滚轮滑动时,会自动调用的函数(这里写函数名即可)。



四、完整代码

  ● 修改过后的 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->Zoom = 45.0f;
        this->Yaw = 90.0f;
        this->Pitch = 0.0f;
        this->MouseSensitivity = 0.2f;
        this->updateCameraVectors();		
    }
 
     /* 观察矩阵 */ 
    glm::mat4 GetViewMatrix()
    {
		return glm::lookAt(this->position, this->position + this->camera_Z_axis, this->camera_Y_axis);
    }

    /* 获取当前摄像机的视野(即观察空间的大小) */
    GLfloat GetZoom()
    {
        return this->Zoom;
    }

    /* 获取当前摄像机的位置 */
    glm::vec3 GetPosition()
    {
        return this->position;
    }

    /* 键盘控制的视角变化 */
    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;
    }

    /* 鼠标移动的视角变化 */
    void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
    {
        xoffset *= MouseSensitivity;
        yoffset *= MouseSensitivity;

        Yaw += xoffset;
        Pitch += yoffset;

        /* 防止出现 “倒立看大地” 的情况 */
        if (constrainPitch)
        {
            if (Pitch > 89.0f)
                Pitch = 89.0f;
            if (Pitch < -89.0f)
                Pitch = -89.0f;
        }
        this->updateCameraVectors();
    }

    /* 鼠标滚轮缩放 */
    void ProcessMouseScroll(float yoffset)
    {
        Zoom -= (float)yoffset;
        if (Zoom < 1.0f)
            Zoom = 1.0f;
        if (Zoom > 45.0f)
            Zoom = 45.0f;
    }


private:
    glm::vec3 position;				
	glm::vec3 camera_Z_axis;		
	glm::vec3 camera_X_axis;		
    glm::vec3 camera_Y_axis;		
	GLfloat movementSpeed;	

    float Yaw;              // 俯仰角
    float Pitch;            // 偏移角
    float MouseSensitivity; // 鼠标灵敏度
    float Zoom;             // 投影深度

    void updateCameraVectors()
    {
        /* 鼠标移动 */
        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));
        this->camera_Z_axis= front;

        /* 摄像机的坐标系 */
		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));	
    }
};

  ● 头文件 Shader.h 依旧沿用第③篇中的代码【OpenGL学习笔记③】——⭐着色器【GLSL Uniform 彩色三角形 变色正方形】⭐

  ● 投光物的顶点着色器 light_v.txt 和投光物的片元着色器 light_f.txt ,以及 shader_v.txtshader_f.txt,还有关于投光物 的封装类 Point_Light.h 都沿用第⑧篇中的代码【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】

  ● 主函数的代码如下

/* 引入相应的库 */
#include 
using namespace std;
#define GLEW_STATIC	
#include"Shader.h"
#include"Camera.h"
#include "Point_Light.h"
#include							// 根据自己的配置情况进行调整
#include						
#include"glm\glm.hpp"
#include"glm\gtc\matrix_transform.hpp"
#include"glm\gtc\type_ptr.hpp"
#include"glm/gtx/rotate_vector.hpp"

/* 顶点属性 */
float vertices_1[] = {
	// x、y、z 坐标				// color				// normal
	-0.5f, -0.5f, -0.5f,		1.0f, 0.0f, 0.0f,		0.0f, 0.0f, -1.0f,	// red
	 0.5f, -0.5f, -0.5f,		1.0f, 0.0f, 0.0f,		0.0f, 0.0f, -1.0f,
	 0.5f,  0.5f, -0.5f,		1.0f, 0.0f, 0.0f,		0.0f, 0.0f, -1.0f,
	 0.5f,  0.5f, -0.5f,		1.0f, 0.0f, 0.0f,		0.0f, 0.0f, -1.0f,
	-0.5f,  0.5f, -0.5f,		1.0f, 0.0f, 0.0f,		0.0f, 0.0f, -1.0f,
	-0.5f, -0.5f, -0.5f,		1.0f, 0.0f, 0.0f,		0.0f, 0.0f, -1.0f,

	-0.5f, -0.5f,  0.5f,		0.0f, 1.0f, 0.0f,		0.0f, 0.0f, 1.0f,	// green
	 0.5f, -0.5f,  0.5f,		0.0f, 1.0f, 0.0f,		0.0f, 0.0f, 1.0f,
	 0.5f,  0.5f,  0.5f,		0.0f, 1.0f, 0.0f,		0.0f, 0.0f, 1.0f,
	 0.5f,  0.5f,  0.5f,		0.0f, 1.0f, 0.0f,		0.0f, 0.0f, 1.0f,
	-0.5f,  0.5f,  0.5f,		0.0f, 1.0f, 0.0f,		0.0f, 0.0f, 1.0f,
	-0.5f, -0.5f,  0.5f,		0.0f, 1.0f, 0.0f,		0.0f, 0.0f, 1.0f,

	-0.5f,  0.5f,  0.5f,		0.0f, 0.0f, 1.0f,		-1.0f, 0.0f, 0.0f, 	// blue
	-0.5f,  0.5f, -0.5f,		0.0f, 0.0f, 1.0f,		-1.0f, 0.0f, 0.0f,
	-0.5f, -0.5f, -0.5f,		0.0f, 0.0f, 1.0f,		-1.0f, 0.0f, 0.0f,
	-0.5f, -0.5f, -0.5f,		0.0f, 0.0f, 1.0f,		-1.0f, 0.0f, 0.0f,
	-0.5f, -0.5f,  0.5f,		0.0f, 0.0f, 1.0f,		-1.0f, 0.0f, 0.0f,
	-0.5f,  0.5f,  0.5f,		0.0f, 0.0f, 1.0f,		-1.0f, 0.0f, 0.0f,

	 0.5f,  0.5f,  0.5f,		1.0f, 1.0f, 0.0f,		1.0f, 0.0f, 0.0f,	// yellow
	 0.5f,  0.5f, -0.5f,		1.0f, 1.0f, 0.0f,		1.0f, 0.0f, 0.0f,
	 0.5f, -0.5f, -0.5f,		1.0f, 1.0f, 0.0f,		1.0f, 0.0f, 0.0f,
	 0.5f, -0.5f, -0.5f,		1.0f, 1.0f, 0.0f,		1.0f, 0.0f, 0.0f,
	 0.5f, -0.5f,  0.5f,		1.0f, 1.0f, 0.0f,		1.0f, 0.0f, 0.0f,
	 0.5f,  0.5f,  0.5f,		1.0f, 1.0f, 0.0f,		1.0f, 0.0f, 0.0f,

	-0.5f, -0.5f, -0.5f,		1.0f, 0.0f, 1.0f,		0.0f, -1.0f, 0.0f, 	// purple
	 0.5f, -0.5f, -0.5f,		1.0f, 0.0f, 1.0f,		0.0f, -1.0f, 0.0f,
	 0.5f, -0.5f,  0.5f,		1.0f, 0.0f, 1.0f,		0.0f, -1.0f, 0.0f,
	 0.5f, -0.5f,  0.5f,		1.0f, 0.0f, 1.0f,		0.0f, -1.0f, 0.0f,
	-0.5f, -0.5f,  0.5f,		1.0f, 0.0f, 1.0f,		0.0f, -1.0f, 0.0f,
	-0.5f, -0.5f, -0.5f,		1.0f, 0.0f, 1.0f,		0.0f, -1.0f, 0.0f,

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

/* 窗口大小 */
const GLint WIDTH = 800, HEIGHT = 600;

/* 摄像机位置与方向的初始化 */
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));

/* 点光源位置的初始化 */
glm::vec3 lightPos = glm::vec3(0.0f, 0.0f, 1.0f);

/* 所有关于输入相应的交互函数 */
void Key_Movement();
void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);					
void square_Movement(GLfloat&, GLfloat&, GLfloat&);	

/* 所有关于输入相应的交互变量 */
bool keys[1024];						// 专门存储按过的键
GLfloat deltaTime = 0.0f;
GLfloat lastTime = 0.0f;
const double Shift_pix = 0.0005;		// 正方体移动速度
bool firstMouse = true;
GLfloat lastX = 0;
GLfloat lastY = 0;

int main()
{
	/* 初始化 glfw */
	glfwInit();
	glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);		// 缩放关闭

	/* 窗口捕获与处理 */
	GLFWwindow* window_1 = glfwCreateWindow(WIDTH, HEIGHT, "Learn OpenGL Light Test", nullptr, nullptr);
	int screenWidth_1, screenHeight_1;
	glfwGetFramebufferSize(window_1, &screenWidth_1, &screenHeight_1);
	glfwMakeContextCurrent(window_1);
	glfwSetInputMode(window_1, GLFW_CURSOR, GLFW_CURSOR_DISABLED);	// 鼠标捕获(且不显示)

	/* 回调函数 */
	glfwSetKeyCallback(window_1, KeyCallback);						// 键盘输入
	glfwSetCursorPosCallback(window_1, mouse_callback);				// 鼠标输入
	glfwSetScrollCallback(window_1, scroll_callback);				// 滚轮输入

	/* 初始化 glew + 光照生成 */
	glewInit();
	Point_Light my_light = Point_Light();

	/* 深度测试开启 */
	glEnable(GL_DEPTH_TEST);

	/* 将我们自己设置的着色器文本传进来 */
	Shader ourShader = Shader("shader_v.txt", "shader_f.txt");		// 相对路径
	Shader lightShader = Shader("light_v.txt", "light_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, 9 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(0);		// 通道 0 打开
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
	glEnableVertexAttribArray(1);		// 通道 1 打开
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
	glEnableVertexAttribArray(2);		// 通道 2 打开

	GLfloat up_down_move = 0.0f;		// 上下移动的变量
	GLfloat left_right_move = 0.0f;		// 左右移动的变量
	GLfloat front_back_move = 0.0f;		// 前后移动的变量

	/* draw loop 画图循环 */
	while (!glfwWindowShouldClose(window_1))
	{
		/* 时间获取 */
		GLfloat currentTime = glfwGetTime();
		deltaTime = currentTime - lastTime;
		lastTime = (float)currentTime;

		/* 视口 +  键鼠捕获 */
		glViewport(0, 0, screenWidth_1, screenHeight_1);
		glfwPollEvents();		// 获取键盘鼠标
		Key_Movement();			// 获取键盘
		square_Movement(up_down_move, left_right_move, front_back_move);		// 正方体移动

		/* 渲染 + 清除颜色缓冲 */
		glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		/* 光照绘制 */
		lightShader.Use();
		glm::mat4 my_transform = glm::mat4(1.0f);
		lightPos = glm::rotate(lightPos, glm::radians(0.1f), glm::vec3(0.0f, 1.0f, 0.0f));		// 旋转
		my_transform = glm::translate(my_transform, lightPos);									// 平移
		my_transform = glm::scale(my_transform, glm::vec3(0.1f, 0.1f, 0.1f));					// 缩放
		glm::mat4 my_projection = glm::perspective(glm::radians(45.0f), (float)screenWidth_1 / (float)screenHeight_1, 0.1f, 100.0f);
		glm::mat4 my_view = camera.GetViewMatrix();	// 求得观察矩阵

		int my_transform_Location = glGetUniformLocation(lightShader.Program, "transform_2");
		glUniformMatrix4fv(my_transform_Location, 1, GL_FALSE, glm::value_ptr(my_transform));
		int my_projection_Location = glGetUniformLocation(lightShader.Program, "projection_2");
		glUniformMatrix4fv(my_projection_Location, 1, GL_FALSE, glm::value_ptr(my_projection));
		int my_view_Location = glGetUniformLocation(lightShader.Program, "view_2");
		glUniformMatrix4fv(my_view_Location, 1, GL_FALSE, glm::value_ptr(my_view));
		my_light.Draw(lightShader);

		/* 正方体绘制 */
		my_transform = glm::mat4(1.0f);		// 初始化是必要的
		ourShader.Use();					// 调用着色器程序
		glBindVertexArray(VAO);				// 绑定 VAO
		my_transform = glm::translate(my_transform, glm::vec3(left_right_move, up_down_move, front_back_move));
		my_transform = glm::scale(my_transform, glm::vec3(0.5, 0.5, 0.5));
		my_projection = glm::perspective(glm::radians(camera.GetZoom()), (float)screenWidth_1 / (float)screenHeight_1, 0.1f, 100.0f);
		my_view = camera.GetViewMatrix();

		my_transform_Location = glGetUniformLocation(ourShader.Program, "transform_1");
		glUniformMatrix4fv(my_transform_Location, 1, GL_FALSE, glm::value_ptr(my_transform));

		my_projection_Location = glGetUniformLocation(ourShader.Program, "projection_1");
		glUniformMatrix4fv(my_projection_Location, 1, GL_FALSE, glm::value_ptr(my_projection));

		my_view_Location = glGetUniformLocation(ourShader.Program, "view_1");
		glUniformMatrix4fv(my_view_Location, 1, GL_FALSE, glm::value_ptr(my_view));

		int LightPos_Location = glGetUniformLocation(ourShader.Program, "LightPos");
		glUniform3f(LightPos_Location, lightPos.x, lightPos.y, lightPos.z);

		int CameraPos_Location = glGetUniformLocation(ourShader.Program, "CameraPos");
		glUniform3f(CameraPos_Location, camera.GetPosition().x, camera.GetPosition().y, camera.GetPosition().z);

		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);
	}
	if (key >= 0 && key <= 1024)
	{
		if (action == GLFW_PRESS)
			keys[key] = true;
		else if (action == GLFW_RELEASE)
			keys[key] = false;
	}
}

void Key_Movement()		// Camera 
{
	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);
}

void square_Movement(GLfloat& up_down_move, GLfloat& left_right_move, GLfloat& front_back_move)	 // Square
{

	if (keys[GLFW_KEY_UP])		// 向上
		up_down_move += Shift_pix;

	if (keys[GLFW_KEY_DOWN])	// 向下
		up_down_move -= Shift_pix;

	if (keys[GLFW_KEY_LEFT])	// 向左
		left_right_move += Shift_pix;

	if (keys[GLFW_KEY_RIGHT])	// 向右
		left_right_move -= Shift_pix;

	if (keys[GLFW_KEY_F])		// 向前(按 F 键)
		front_back_move += Shift_pix;

	if (keys[GLFW_KEY_B])		// 向后(按 B 键)
		front_back_move -= Shift_pix;
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
	if (firstMouse)
	{
		lastX = xpos;
		lastY = ypos;
		firstMouse = false;
		return;
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos;			// 注意这里是相反的

	lastX = xpos;
	lastY = ypos;

	camera.ProcessMouseMovement(xoffset, yoffset);
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	camera.ProcessMouseScroll(yoffset);
}

  ● 运行结果
【OpenGL学习笔记⑨】——鼠标控制镜头 + 滚轮控制镜头缩放_第4张图片

  ● 说明:实现 鼠标移动环绕正方体一周 + 鼠标滚轮缩放 两个效果功能。【注:移动鼠标时要和键盘移动一起配合使用才有以上效果。】



五、参考附录:

[1] 《Learn OpenGL——摄像机》
链接: https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/.

上一篇文章链接: 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】.

下一篇文章链接:

OpenGL总学习目录: https://blog.csdn.net/Wang_Dou_Dou_/article/details/121240714.


⭐️ ⭐️

你可能感兴趣的:(OpenGL学习笔记,opengl)