[OpenGL]摄像机

文章目录[x]
  1. 1:自定义摄像机类
  2. 1.1:摄像机基类
  3. 1.2:固定摄像机
  4. 1.3:自由摄像机
  5. 1.4:FPS摄像机
  6. 2:Usage
  7. 2.1:建立测试类

自定义摄像机类


摄像机基类


头文件

#pragma once

#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "Window/Window.h"

class BaseCamera
{
public:
    BaseCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up, GLfloat fov, GLfloat nearClip, GLfloat farClip);
    virtual ~BaseCamera();

    virtual void Update(float deltaTime) {}

    inline void Set(glm::vec3 eye, glm::vec3 at, glm::vec3 up) { m_Eye = eye; m_At = at; m_Up = up; }
    inline void SetEye(glm::vec3 eye) { m_Eye = eye; }
    inline void SetAt(glm::vec3 at) { m_At = at; }
    inline void SetUp(glm::vec3 up) { m_Up = up; }
    inline glm::vec3 GetEye() const { return m_Eye; }
    inline glm::vec3 GetAt() const { return m_At; }
    inline glm::vec3 GetUp() const { return m_Up; }

    inline void SetFov(GLfloat fov) { m_Fov = fov; }

    inline virtual glm::mat4 GetViewMatrix() { return glm::lookAt(m_Eye, m_At, m_Up); }
    inline virtual glm::mat4 GetProjectionMatrix() { return glm::perspective(glm::radians(m_Fov), SCREEN_WIDTH / (float)SCREEN_HEIGHT, m_NearClip, m_FarClip); }

protected:
    glm::vec3 m_Eye;
    glm::vec3 m_At;
    glm::vec3 m_Up;
    GLfloat m_Fov;
    GLfloat m_NearClip, m_FarClip;
};

源文件

#include "BaseCamera.h"

BaseCamera::BaseCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up, GLfloat fov, GLfloat nearClip, GLfloat farClip)
    : m_Eye(eye), m_At(at), m_Up(up), m_Fov(fov), m_NearClip(nearClip), m_FarClip(farClip)
{
}

BaseCamera::~BaseCamera()
{
}

 

固定摄像机


定义:摄像机的位置和视角都无法移动

View Code
// .h
#pragma once

#include "BaseCamera.h"

class FixedCamera : public BaseCamera
{
public:
    FixedCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up);

};

//.cpp
#include "FixedCamera.h"

FixedCamera::FixedCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up) : BaseCamera(eye, at, up, 45.0f, 0.1f, 1000.0f)
{
}

 

自由摄像机


定义:摄像机的位置和视角能够自由的移动

View Code
// .h
#pragma once

#include "BaseCamera.h"

class FreeCamera : public BaseCamera
{
public:
    FreeCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up);
    FreeCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up, GLfloat fov, GLfloat nearClip, GLfloat farClip);

    virtual void Update(float deltaTime) override;

    inline virtual glm::mat4 GetViewMatrix() { return glm::lookAt(m_Eye, m_Eye + m_At, m_Up); }

protected:
    bool m_FirstMove;
    GLfloat m_LastX, m_LastY;
    GLfloat m_Yaw, m_Pitch;
    GLfloat m_Sensitivity, m_MoveSpeed, m_ZoomSpeed, m_ZoomLimit = 55.0f;

    void OnMouseMove(GLFWwindow* window, float xpos, float ypos);
    void OnMouseScroll(GLFWwindow* window, float xoffset, float yoffset);
};

// .cpp
#include "FreeCamera.h"
#include "input/Input.h"

FreeCamera::FreeCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up) : FreeCamera(eye, at, up, 45.0f, 0.1f, 1000.0f)
{

}

FreeCamera::FreeCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up, GLfloat fov, GLfloat nearClip, GLfloat farClip) :
    BaseCamera(eye, at, up, fov, nearClip, farClip),
    m_MoveSpeed(3.0f), m_ZoomSpeed(15), m_Sensitivity(0.12f), m_Yaw(-90.0), m_Pitch(0.0f)
{
    // 禁用并捕捉光标
    glfwSetInputMode(Window::GetInstance().GetGlfwWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    using namespace std::placeholders;
    Input::GetInstance().onMouseMove = std::bind(&FreeCamera::OnMouseMove, this, _1, _2, _3);
    Input::GetInstance().onMouseScroll = std::bind(&FreeCamera::OnMouseScroll, this, _1, _2, _3);

    m_Fov = fov;
    m_NearClip = nearClip;
    m_FarClip = farClip;

    m_FirstMove = true;
}

void FreeCamera::OnMouseMove(GLFWwindow* window, float xpos, float ypos)
{
    // 防止第一次鼠标垂直移动时造成的鼠标瞬移
    if (m_FirstMove)
    {
        m_LastX = xpos;
        m_LastY = ypos;
        m_FirstMove = false;
    }

    float xOffset = xpos - m_LastX;
    float yOffset = m_LastY - ypos;
    m_LastX = xpos;
    m_LastY = ypos;

    xOffset *= m_Sensitivity;
    yOffset *= m_Sensitivity;
    m_Yaw += xOffset;
    m_Pitch += yOffset;
    if (m_Pitch > 89.0f)
        m_Pitch = 89.0f;
    if (m_Pitch < -89.0f)
        m_Pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
    front.y = sin(glm::radians(m_Pitch));
    front.z = sin(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));

    m_At = glm::normalize(front);
}

void FreeCamera::OnMouseScroll(GLFWwindow* window, float xoffset, float yoffset)
{
    if (m_Fov >= 1.0f && m_Fov <= m_ZoomLimit)
        m_Fov -= yoffset * m_ZoomSpeed;
    if (m_Fov <= 1.0f)
        m_Fov = 1.0f;
    if (m_Fov >= m_ZoomLimit)
        m_Fov = m_ZoomLimit;
}

void FreeCamera::Update(float deltaTime)
{
    float velocity = m_MoveSpeed * deltaTime;
    if (Input::GetKey(KeyCode::W))
        m_Eye += m_At * velocity;
    if (Input::GetKey(KeyCode::S))
        m_Eye -= m_At * velocity;
    if (Input::GetKey(KeyCode::A))
        m_Eye -= glm::normalize(glm::cross(m_At, m_Up)) * velocity;
    if (Input::GetKey(KeyCode::D))
        m_Eye += glm::normalize(glm::cross(m_At, m_Up)) * velocity;
}

 

FPS摄像机


定义:FPS摄像机仅仅是在自由摄像机的基础上让其始终在XZ轴方向运动

View Code
// .h
#pragma once

#include "BaseCamera.h"

class FreeCamera : public BaseCamera
{
public:
    FreeCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up);
    FreeCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up, GLfloat fov, GLfloat nearClip, GLfloat farClip);

    virtual void Update(float deltaTime) override;

    inline virtual glm::mat4 GetViewMatrix() { return glm::lookAt(m_Eye, m_Eye + m_At, m_Up); }

protected:
    bool m_FirstMove;
    GLfloat m_LastX, m_LastY;
    GLfloat m_Yaw, m_Pitch;
    GLfloat m_Sensitivity, m_MoveSpeed, m_ZoomSpeed, m_ZoomLimit = 55.0f;

    void OnMouseMove(GLFWwindow* window, float xpos, float ypos);
    void OnMouseScroll(GLFWwindow* window, float xoffset, float yoffset);
};

// .cpp
#include "FPSCamera.h"
#include "input/Input.h"

FPSCamera::FPSCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up) : FPSCamera(eye, at, up, 45.0f, 0.1f, 1000.0f)
{

}

FPSCamera::FPSCamera(glm::vec3 eye, glm::vec3 at, glm::vec3 up, GLfloat fov, GLfloat nearClip, GLfloat farClip) :
    FreeCamera(eye, at, up, fov, nearClip, farClip)
{
    m_EyeHeight = 1.4f;
}

void FPSCamera::Update(float deltaTime)
{
    FreeCamera::Update(deltaTime);
    m_Eye.y = m_EyeHeight;
}

 

Usage


建立测试类


固定摄像机
class FixedCamera : public SubTestBed
{
protected:
    ::BaseCamera* m_Camera;
    glm::mat4 m_Model = glm::identity<glm::mat4>();
    std::vector<glm::vec3> m_CubePositions;
public:
    virtual void Setup() override
    {
#pragma region vertices
        float vertices[] = {
            -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
             0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

            -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
        };
#pragma endregion
        m_VA[0] = new VertexArray();
        m_VB[0] = new VertexBuffer(vertices, sizeof(vertices));
        VertexBufferLayout layout;
        layout.Push<float>(3);
        layout.Push<float>(2);
        m_VA[0]->AddBuffer(*m_VB[0], layout);
        m_Texture[0] = new Texture("Resources/Textures/container.jpg", GL_REPEAT, GL_REPEAT);
        m_Texture[1] = new Texture("Resources/Textures/awesomeface.png", GL_REPEAT, GL_REPEAT);

        if (!m_TestScene)
        {
            TestScene::CameraData data;
            data.eye = glm::vec3(0.0f, 3.0f, 3.0f);
            data.at = glm::vec3(0.0f, 3.0f, 0.0f);
            data.up = glm::vec3(0.0f, 1.0f, 0.0f);
            m_TestScene = new TestScene(TestScene::FIXED_CAMERA, data);
            m_Camera = (::FixedCamera*)m_TestScene->Camera();
        }

        m_TestScene->Setup();
        m_Shader[0] = new Shader("SandBox/06_Coordinate/PerspectiveMVPMatrix.shader");
        m_Shader[0]->Bind();
        m_Shader[0]->SetUniformMat4f("u_Projection", m_Camera->GetProjectionMatrix());

        m_VB[0]->UnBind();
        m_VA[0]->UnBind();
        m_Texture[0]->UnBind();
        m_Texture[1]->UnBind();

        m_CubePositions.push_back(glm::vec3(0.0f, 2.6f, 0.0f));
        m_CubePositions.push_back(glm::vec3(2.0f, 7.6f, -15.0f));
        m_CubePositions.push_back(glm::vec3(-1.5f, 0.4f, -2.5f));
        m_CubePositions.push_back(glm::vec3(-3.8f, 0.6f, -12.3f));
        m_CubePositions.push_back(glm::vec3(2.4f, 2.2f, -3.5f));
        m_CubePositions.push_back(glm::vec3(-1.7f, 5.6f, -7.5f));
        m_CubePositions.push_back(glm::vec3(1.3f, 0.6f, -2.5f));
        m_CubePositions.push_back(glm::vec3(1.5f, 4.6f, -2.5f));
        m_CubePositions.push_back(glm::vec3(1.5f, 2.8f, -1.5f));
        m_CubePositions.push_back(glm::vec3(-1.3f, 3.6f, -1.5f));
    }


    virtual void Render() override
    {
        m_TestScene->Render();

        m_Texture[0]->Bind(0);
        m_Texture[1]->Bind(1);

        for (unsigned int i = 0; i < m_CubePositions.size(); i++)
        {
            this->m_Renderer.Draw(m_VA[0], 36, m_Shader[0]);
            m_Shader[0]->SetUniform1i("u_Texture0", 0);
            m_Shader[0]->SetUniform1i("u_Texture1", 1);
            m_Model = glm::identity<glm::mat4>();
            m_Model = glm::translate(m_Model, m_CubePositions[i]);
            m_Model = glm::rotate(m_Model, glm::radians(20 * (i + 1) * (float)(glfwGetTime())), glm::vec3(0.6f, 1, 0.3f));

            // 圆的参数方程
            // x = a + r * cosθ;
            // y = b + r * sinθ;
            float radius = 16.0f;
            float camX = sin((float)glfwGetTime()) * radius;
            float camZ = cos((float)glfwGetTime()) * radius;
            m_Camera->SetEye(glm::vec3(camX, m_Camera->GetEye().y, camZ));

            m_Shader[0]->SetUniformMat4f("u_Model", m_Model);
            m_Shader[0]->SetUniformMat4f("u_View", m_Camera->GetViewMatrix());
        }
    }

};

 

自由摄像机
class FreeCamera : public FixedCamera
{
protected:
    glm::mat4 m_ViewMatrix;
public:
    virtual void Setup() override
    {
        FixedCamera::Setup();
        TestScene::CameraData data;
        m_TestScene->ReInitCameraData(TestScene::FREE_CAMERA, data);
        m_Camera = (::FreeCamera*)m_TestScene->Camera();
    }

    virtual void Update(float deltaTime) override
    {
        m_TestScene->Update(deltaTime);
    }

    virtual void Render() override
    {
        m_TestScene->Render();

        m_Texture[0]->Bind(0);
        m_Texture[1]->Bind(1);
        m_Shader[0]->Bind();
        m_Shader[0]->SetUniform1i("u_Texture0", 0);
        m_Shader[0]->SetUniform1i("u_Texture1", 1);
        m_Shader[0]->SetUniformMat4f("u_Projection", m_Camera->GetProjectionMatrix());
        m_ViewMatrix = m_Camera->GetViewMatrix();
        m_Shader[0]->SetUniformMat4f("u_View", m_ViewMatrix);

        for (unsigned int i = 0; i < m_CubePositions.size(); i++)
        {
            m_Model = glm::identity<glm::mat4>();
            m_Model = glm::translate(m_Model, m_CubePositions[i]);
            m_Model = glm::rotate(m_Model, glm::radians(20 * (i + 1) * (float)(glfwGetTime())), glm::vec3(0.6f, 1, 0.3f));
            m_Shader[0]->SetUniformMat4f("u_Model", m_Model);

            this->m_Renderer.Draw(m_VA[0], 36, m_Shader[0]);
        }
    }

};

自由摄像机

 

练习题1: 看看你是否能够修改摄像机类,使得其能够变成一个真正的FPS摄像机(也就是说不能够随意飞行);你只能够呆在xz平面上
class FPSCamera : public FreeCamera
{
public:
    virtual void Setup() override
    {
        FreeCamera::Setup();
        TestScene::CameraData data;
        m_TestScene->ReInitCameraData(TestScene::FPS_CAMERA, data);
        m_Camera = (::FPSCamera*)m_TestScene->Camera();
    }
};

 

FPS摄像机

 

练习题2: 试着创建你自己的LookAt函数,其中你需要手动创建一个我们在一开始讨论的观察矩阵。用你的函数实现来替换GLM的LookAt函数
class CustomLookAt : public FreeCamera
{
public:
    virtual void Render() override
    {
        m_TestScene->Render();

        m_Texture[0]->Bind(0);
        m_Texture[1]->Bind(1);
        m_Shader[0]->Bind();
        m_Shader[0]->SetUniform1i("u_Texture0", 0);
        m_Shader[0]->SetUniform1i("u_Texture1", 1);
        m_Shader[0]->SetUniformMat4f("u_Projection", m_Camera->GetProjectionMatrix());
        m_ViewMatrix = GetViewMatrix(m_Camera->GetEye(), m_Camera->GetEye() + m_Camera->GetAt(), m_Camera->GetUp());
        m_Shader[0]->SetUniformMat4f("u_View", m_ViewMatrix);

        for (unsigned int i = 0; i < m_CubePositions.size(); i++)
        {
            m_Model = glm::identity<glm::mat4>();
            m_Model = glm::translate(m_Model, m_CubePositions[i]);
            m_Model = glm::rotate(m_Model, glm::radians(20 * (i + 1) * (float)(glfwGetTime())), glm::vec3(0.6f, 1, 0.3f));
            m_Shader[0]->SetUniformMat4f("u_Model", m_Model);

            this->m_Renderer.Draw(m_VA[0], 36, m_Shader[0]);
        }
    }

private:
    glm::mat4 GetViewMatrix(glm::vec3 eye, glm::vec3 at, glm::vec3 up)
    {
        // 自己的LookAt矩阵
        // | Rx Ry Rz 0 |   | 1 0 0 -Px |
        // | Ux Uy Uz 0 | * | 0 1 0 -Py |
        // | Dx Dy Dz 0 |   | 0 0 1 -Pz |
        // | 0  0  0  1 |   | 0 0 0  0  |
        // R = 摄像机右轴    U = 摄像机上轴   D = 摄像机前轴   P = 摄像机位置
        glm::vec3 zAxis = glm::normalize(eye - at); // 老问题,OpenGL用的右手坐标系,靠近人眼这边的Z是正轴
        glm::vec3 xAxis = glm::normalize(glm::cross(glm::normalize(up), zAxis));
        glm::vec3 yAxis = glm::cross(zAxis, xAxis);

        glm::mat4 translation = glm::identity<glm::mat4>();
        translation[3][0] = -eye.x;
        translation[3][1] = -eye.y;
        translation[3][2] = -eye.z;

        glm::mat4 rotation = glm::identity<glm::mat4>();
        rotation[0][0] = xAxis.x;
        rotation[1][0] = xAxis.y;
        rotation[2][0] = xAxis.z;
        rotation[0][1] = yAxis.x;
        rotation[1][1] = yAxis.y;
        rotation[2][1] = yAxis.z;
        rotation[0][2] = zAxis.x;
        rotation[1][2] = zAxis.y;
        rotation[2][2] = zAxis.z;

        return rotation * translation;
    }
};

 

自定义View矩阵

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像