【OpenGL学习笔记⑨】——鼠标控制镜头 + 滚轮控制镜头缩放_opengl鼠标滚轮缩放-程序员宅基地

技术标签: OpenGL学习笔记  opengl  




Mouse

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


零、 成果预览图

在这里插入图片描述

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



一、对欧拉角的理解

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

在这里插入图片描述
  ◆ 说明
    ① 俯仰角是描述我们如何往上或往下看的角,可以在第一张图中看到。
    ② 第二张图展示了偏航角,偏航角表示我们往左和往右看的程度。
    ③ 滚转角代表我们如何翻滚摄像机。


  ● 在我们的摄像机所拍摄的世界坐标系里,这 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 <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->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 <iostream>
using namespace std;
#define GLEW_STATIC	
#include"Shader.h"
#include"Camera.h"
#include "Point_Light.h"
#include<glew.h>							// 根据自己的配置情况进行调整
#include<glfw3.h>						
#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);
}

  ● 运行结果
在这里插入图片描述

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



五、参考附录:

[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.


️ ️

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Wang_Dou_Dou_/article/details/121313659

智能推荐

JavaWeb笔记-程序员宅基地

文章浏览阅读620次。JavaWeb笔记备忘参考课程:B站狂神说JavaWebhttps://www.bilibili.com/video/BV12J411M7Sj1、Web基本概念web开发:web:网页静态webhtml,css提供给所有人看内容不会变化动态web淘宝等几乎所有网站内容会根据不同的人发生变化技术栈:Servlet/JSP,ASP,PHP在java中,动态web资源开发中的技术统称为JavaWeb;2、Servlet2.1、Servlet简介Servle_javaweb笔记

众所周知,B站并不是个学习网站-程序员宅基地

文章浏览阅读970次,点赞4次,收藏4次。立了一个Flag鸽鸽鸽鸽我喜立Flag,9月份说要做点视频,不知不觉已经鸽了俩月了。中间就零星时间学了一些剪辑方面的知识,工作太忙,视频一直没有实质进展。视频的灵魂其实是脚本,到现在还没写好。我还是稍微擅长点技术创作,但是纯技术的视频肯定没有市场,这个转变过程有点困难。这个Flag立的,感觉要吃点苦头,但我不后悔这个决定。隐约记得 N 多年的考研英语有一篇阅读理解,大致意思是:“有理想要喊出来” ,写这篇文章时,回头翻了翻已经落了一层灰的丁晓钟《考研英语历年真题超精解》,只在2000年的考研英语真题中找

在Hadoop上运行基于RMM中文分词算法的MapReduce程序-程序员宅基地

文章浏览阅读169次。为什么80%的码农都做不了架构师?>>> ..._基于中文分词算法的mapreduce

正确设置 MME类型-程序员宅基地

文章浏览阅读2.4k次。请求中的 response - header 中的content-type项是指�0�2�0�2服务器发送给客户端内容的MIME类型,如果 设置不对 那么浏览器怕是不能正常解析;const path = require(“path”);// 多用途Internet邮件扩展(MIME)类型const mimeType= {“.323”:“text/h323” ,“.3gp”:“video..._mme类型

关于EM算法_语音特征建模算法中常用期望最大化算法进行优化,一下关于em算法说法错误的是-程序员宅基地

文章浏览阅读143次。EM算法的简介1.什么是EM算法最大期望算法(Expectation-maximization algorithm,又译为期望最大化算法),是在概率模型中寻找参数最大似然估计或者最大后验估计的算法,其中概率模型依赖于无法观测的隐性变量。最大期望算法经过两个步骤交替进行计算,第一步是计算期望(E),利用对隐藏变量的现有估计值,计算其最大似然估计值;第二步是最大化(M),最大化在E步上求得的..._语音特征建模算法中常用期望最大化算法进行优化,一下关于em算法说法错误的是

Harbor 重启失败--已解决_error: for harbor-log cannot start service log: dr-程序员宅基地

文章浏览阅读1.4w次,点赞5次,收藏13次。harbor版本:v1.10.2遇到问题:使用 docker-compose down命令关闭harbor,然后使用./install.sh --with-chartmuseum命令重新安装带chart仓库的harbor,然后发现启动不了,报以下错误:[Step 5]: starting Harbor ...Creating network "harbor_harbor" with the default driverCreating network "harbor_harbor-chartmu_error: for harbor-log cannot start service log: driver failed programming ex

随便推点

System.Runtime.InteropServices.COMException (0x80004005): 无法创建目录或文件_system.runtime.interopservices.externalexception(0-程序员宅基地

文章浏览阅读1.8w次。自定义工具错误: "代码生成器 'ReportCodeGenerator' 失败。异常堆积 = CrystalDecisions.Shared.CrystalReportsException: 加载报表失败。 ---> System.Runtime.InteropServices.COMException (0x80004005): 无法创建目录或文件。 在 CrystalDecisions_system.runtime.interopservices.externalexception(0x80004005)

css 添加滚动条,div添加滚动条, 自定义滚动条_handsontable滚动条可以滚动-程序员宅基地

文章浏览阅读536次。.rows 的子元素高度必须高于 .rows 的高度, 才能出现滚动条.rows { height: 230px; overflow: hidden; overflow-y: auto;}.rows::-webkit-scrollbar-track-piece { background-color: rgba(0, 0, 0, 0); border-left: 1px solid rgba(0, 0, 0, 0);}.rows::-webkit-scrollbar { _handsontable滚动条可以滚动

优化器提示——性能调整手册和参考_oracle_pred arule-程序员宅基地

文章浏览阅读544次。Hint是Oracle数据库灵活性的体现。由于Hint具有最高的优先级,因此可以通过Hint使优化器根据用户的需要来生成指定的执行计划。Oracle的hint种类繁多,大致可以分为下面几类:优化方式和目标:如RULE、CHOOSE、FIRST_ROWS、ALL_ROWS等。访问路径:如INDEX、FULL、CLUSTER、INDEX_FFS等。查询转换:如MERGE、USE_CON_pred arule

“iPhone邮件应用程序无法同步最新电子邮件“ - 解决方案与源代码示例_苹果手机邮箱不更新邮件-程序员宅基地

文章浏览阅读679次。在邮件设置中,您可以找到"账户"选项,然后选择您的电子邮件账户。然而,有时候我们可能会遇到问题,即我们的iPhone邮件应用程序未能显示最新的电子邮件。在本篇文章中,我们将探讨这个问题的解决方案,并提供相应的源代码示例,帮助您解决此类问题。在邮件设置中,您可以检查您的电子邮件帐户的配置和同步设置。下面是一个简单的示例代码,演示如何使用Swift语言编写一个简单的邮件应用程序,并实现邮件的同步功能。下面是一些您可以尝试的解决方案,以确保您的iPhone邮件应用程序能够显示最新的电子邮件。_苹果手机邮箱不更新邮件

unity 一个拼图demo(七巧板)和一个切割demo_untiy开发七巧板-程序员宅基地

文章浏览阅读6.6k次。好久没更新博客了,那么今天就来装一下b吧。还是先上一波图,看看我们到底要做什么。前面2张是切割的的demo,后面的这个是拼图,但是我们看到材质,这里的解释一下2个demo用的都是一个网格构建代码,所以才会出现这种情况,正确的做法应该是把添加uv的代码做个判断一下,把这个变量放到这个方法参数列表中,同时把它变成一个对外的方法。这里顺便给大家提一下封装的思想,厉害的程序员他们其实大部分的时_untiy开发七巧板

SpringCloud微服务架构升级总结_springcloud 升级了annotation-程序员宅基地

文章浏览阅读263次。SpringCloud微服务架构升级总结wuli程序员2019-06-26 15:37:28一、背景1.1 应用系统的架构历史1.2 什么是微服务?起源:微服务的概念源于 2014 年 3 月 Martin Fowler 所写的一篇文章“Microservices”。文中内容提到:微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互..._springcloud 升级了annotation