【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

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文