【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

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签