【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

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签