我们自己的摄像机类(在OpenGL中造一个摄像机)
程序员文章站
2022-03-25 22:25:02
...
在学图形学时,我们都是通过把场景中的所有物体向z轴负方向移动来模拟出摄像头的感觉,但那样的话我们模拟摄像机移动时其实也只能移动物体,当然我们还可以用glLookAt改变对应参数来模拟摄像机移动会更真实一些。
但我们如果要应用到着色器中只用glLookAt很不方便,比如我们经常要计算镜面反射光就需要视线方向,而且OpenGL本身没有摄像机的概念,但我们可以通过摄像机向后移动来模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动。所以我们自己构造一个摄像机,并通过键盘和鼠标输入让我们能够在3D场景中*移动,就像我的世界中的操作一样。
几点重要的说明:
(1)鼠标移动伴随的摄像机角度的移动:
这些角度对于计算鼠标移动对应的摄像机角度的变化很关键。也是其原理。在程序中用的就是同名变量,注意理解。
(2)在主程序中的使用:
摄像机对象的函数看最后的类结合使用理解。
初始化:
// camera定义摄像机对象
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
bool firstMouse = true;
//我们跟踪两个全局变量来计算出deltaTime值:为了在不同性能的计算机上运行速度一样
float deltaTime = 0.0f; // 当前帧与上一帧的时间差
float lastFrame = 0.0f; // 上一帧的时间
//第一步是计算鼠标自上一帧的偏移量。我们必须先在程序中储存上一帧的鼠标位置,我们把它的初始值设置为屏幕的中心:
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
//判断是否是第一次鼠标事件
bool firstMouse=true;
//滚轮控制观察范围
float fov=45.0;
glfw提供的用于交互的回调函数:
glfwSetCursorPosCallback(window, mouse_callback);//鼠标
glfwSetScrollCallback(window, scroll_callback);//滚轮
//我们要告诉GLFW,它应该隐藏光标,并捕捉(Capture)它。
//捕捉光标表示的是,如果焦点在你的程序上
//(译注:即表示你正在操作这个程序,Windows中拥有焦点的程序标题栏通常是有颜色的那个,而失去焦点的程序标题栏则是灰色的)
//光标应该停留在窗口中(除非程序失去焦点或者退出)。
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
也就是说在调用glfwSetInputMode函数之后,无论我们怎么去移动鼠标,光标都不会显示了,它也不会离开窗口。
上面鼠标、滚轮时间的回调函数:mouse_callback、scroll_callback
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; //别忘了OpenGL是左下角为原点
lastX = xpos;
lastY = ypos;
//我们只需要得到鼠标的位置,剩下的摄像机位置交给ProcessMouseMovement函数
camera.ProcessMouseMovement(xoffset, yoffset);
}
当滚动鼠标滚轮的时候,yoffset值代表我们竖直滚动的大小,ProcessMouseScroll根据yoffset值改变投影时平截头体的角度范围(也就是下面的camera.Zoom)来模拟缩放。
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
render loop 渲染循环中:
投影矩阵和观察矩阵的计算:
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
键盘的输入processInput(window);
void processInput(GLFWwindow *window)
{
//按esc退出程序
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
//按w、s、a、d可以分别前后左右移动摄像机位置
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
}
摄像机类
#ifndef CAMERA_H
#define CAMERA_H
//#define STB_IMAGE_IMPLEMENTATION//用glm库的必备(如果主程序中已经定义就不用再在这里重复了)
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
//用在ProcessKeyboard函数中实现键盘控制摄像机前后左右的移动
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
//YAW、PITCH对应前面图的角度
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
//我们把偏移量乘以了sensitivity(灵敏度)值。
//如果我们忽略这个值,鼠标移动就会太大了;你可以自己实验一下,找到适合自己的灵敏度值。
const float ZOOM = 45.0f;
//上面已经提到过ZOOM——投影时平截头体的角度范围fovy
// An abstract camera class that processes input and calculates the corresponding Euler Angles, Vectors and Matrices for use in OpenGL
class Camera
{
public:
//这些变量在很多地方都有重要的应用注意体会
// Camera Attributes
glm::vec3 Position;//位置
glm::vec3 Front;//朝向
glm::vec3 Up;//头向上的位置
glm::vec3 Right;
glm::vec3 WorldUp;
// Euler Angles
float Yaw;
float Pitch;
// Camera options
float MovementSpeed;//移动速度
float MouseSensitivity;//灵敏度
float Zoom;//fovy
//下面是两种构造方式
// Constructor with vectors
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
{
Position = position;
WorldUp = up;
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// Constructor with scalar values
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
{
Position = glm::vec3(posX, posY, posZ);
WorldUp = glm::vec3(upX, upY, upZ);
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
glm::mat4 GetViewMatrix()
{//计算观察矩阵
return glm::lookAt(Position, Position + Front, Up);
}
// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
void ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
//为了让不同性能的电脑移动速度相同
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD)
Position += Front * velocity;
if (direction == BACKWARD)
Position -= Front * velocity;
if (direction == LEFT)
Position -= Right * velocity;
if (direction == RIGHT)
Position += Right * velocity;
}
// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
{
//注意我们把偏移量乘以了sensitivity(灵敏度)值。
//如果我们忽略这个值,鼠标移动就会太大了;
//你可以自己实验一下,找到适合自己的灵敏度值。
xoffset *= MouseSensitivity;
yoffset *= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
// Make sure that when pitch is out of bounds, screen doesn't get flipped
//对于俯仰角,要让用户不能看向高于89度的地方(在90度时视角会发生逆转,所以我们把89度作为极限),同样也不允许小于-89度。
//这样能够保证用户只能看到天空或脚下,但是不能超越这个限制。
//我们可以在值超过限制的时候将其改为极限值来实现:
if (constrainPitch)
{
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
}
// Update Front, Right and Up Vectors using the updated Euler angles
updateCameraVectors();
}
// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
void ProcessMouseScroll(float yoffset)
{//滚轮实现视野的缩放(前面提到过了)
if (Zoom >= 1.0f && Zoom <= 45.0f)
Zoom -= yoffset;
if (Zoom <= 1.0f)
Zoom = 1.0f;
if (Zoom >= 45.0f)
Zoom = 45.0f;
}
//最后写一个用来对方向变换后更新摄像机对象中内容的函数。
private:
// Calculates the front vector from the Camera's (updated) Euler Angles
void updateCameraVectors()
{
// Calculate the new Front vector
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));
Front = glm::normalize(front);
// Also re-calculate the Right and Up vector
Right = glm::normalize(glm::cross(Front, WorldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
Up = glm::normalize(glm::cross(Right, Front));
}
};
#endif
上一篇: 基于注解的IOC配置
下一篇: python面试,日更