一:从openGL那些坐标转换经历了从世界到界界面的界面?
先了解几个坐标系:
- 局部坐标系(Local Space:以自己为中心的坐标通常是Vec4(1);
- 世界坐标系(word space):以世界为原点的坐标系将局部转化为世界;
- 变换坐标(model space):变换坐标是旋转、位移、缩放等。基于世界坐标系;
- 视窗坐标(View Space:转换坐标系的摄像头位置;
- 裁剪空间(clip space):切割屏幕外的坐标需要定义一个投影矩阵,一个正视投影,另一个透视投影。
正视投影:
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
透视投影:
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
以上变化非常复杂,本文只理解,请点击:OpenGL Projection Matrix (songho.ca)
最后组合在一起是我们看到的屏幕坐标。

理解矩阵相乘的含义!
粘贴链接:矩阵相乘的几何意义 - 知乎 (zhihu.com)
说白了,矩阵的乘法可以在一系列变换后呈现到我们的屏幕上,但这种乘法是有序的 从右到左读取上述公式是矩阵变换的顺序。
引用管方图,我们需要做的是根据原点坐标(视点、焦点)、摄像头坐标,世界向量可以找到摄像头坐标系变换矩阵步骤如下:
首选相机位置与原点连接的首选(z)向量:(从摄像机到原点的方向)
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);
我们找到了相机的轴向量,然后我们问第二个,我们首先定义了上向量代表zy在平面上,找出x轴的向量,我们将上向量定为:
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
根据向量叉乘,我们可以找到垂直于两个向量的向量(x)(右手坐标系);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));
我们还要求剩下的向量(y):
glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);
这样,我们就找出了视口矩阵
其中RR是右向量,UU是上向量,DD是方向向量PP是摄像机的位置向量。
四:移动相机
在opengl中设置LookAt摄像机矩阵函数即可获得
view = glm::lookAt(cameraPos, cameraFront, cameraUp);
移动只是同时向前移动焦点和位置:
view = glm::lookAt(cameraPos, cameraPos cameraFront, cameraUp);
根据opengl改变相机输入的按键回调函数。
五、相机旋转
在相机旋转方面,我们需要引入欧拉角。
欧拉角(Euler Angle)是可以表示3D伦哈德是空间中任何旋转的三个值·欧拉(Leonhard Euler)18世纪提出的欧拉角有三种:俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll),下图显示了它们的含义:
其中,我们使用较少的第三种。一般来说,会有空战游戏,其他游戏主要是第一和第二种。欧拉角定义旋转。我们首先需要知道矩阵旋转的变换公式。
通过旋转pitch、yaw、roll将相机移动到指定位置eye,对应的视变矩阵是
对相机进行pitch和yaw角度的旋转后,我们需要重新计算相机的forward向量,以及side向量用于完成相机的前后左右移动。这两个向量都是在世界坐标系下给定的。我们可以计算出相机坐标系下的点旋转后在世界坐标系下的值。
原始可以通过矩阵R计算forward=(0,0,-1,0)向量变换后的向量,计算结果为上述矩阵R第三列求反的结果,表示为:
Front.x = -cos(glm::radians(Pitch))*sin(glm::radians(Yaw)); Front.y = sin(glm::radians(Pitch)); Front.z = -cos(glm::radians(Pitch))*cos(glm::radians(Yaw)); Front = glm::normalize(Front);
最后,鼠标移动值可以结合回调函数获得。
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; lastX = xpos; lastY = ypos; float sensitivity = 0.05; xoffset *= sensitivity; yoffset *= sensitivity; yaw = xoffset; pitch = yoffset; if(pitch > 89.0f) pitch = 89.0f; if(pitch < -89.0f) pitch = -89.0f; 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)); cameraFront = glm::normalize(front); }
附录:摄像机类(转载官方脚本)
#ifndef CAMERA_H #define CAMERA_H #include <glad/glad.h> #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <vector> // Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;
// 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;
// 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)
{
xoffset *= MouseSensitivity;
yoffset *= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
// make sure that when pitch is out of bounds, screen doesn't get flipped
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)
{
Zoom -= (float)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
参考资料:从零开始学FPS摄像机 - 腾讯云开发者社区-腾讯云 (tencent.com)
摄像机 - LearnOpenGL CN (learnopengl-cn.github.io)