资讯详情

【OpenGL】摄像机实现理解每个公式

一:从openGL那些坐标转换经历了从世界到界界面的界面?

先了解几个坐标系:

  1. 局部坐标系(Local Space:以自己为中心的坐标通常是Vec4(1);
  2. 世界坐标系(word space):以世界为原点的坐标系将局部转化为世界;
  3. 变换坐标(model space):变换坐标是旋转、位移、缩放等。基于世界坐标系;
  4. 视窗坐标(View Space:转换坐标系的摄像头位置;
  5. 裁剪空间(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)

标签: 电感磁珠upz1005d121

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司