QT+OpenGL 摄像机

QT+OpenGL 摄像机

本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主

OpenGL本身没有摄像机的定义,但是我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生一种我们在移动的感觉。

摄像机原理

方向向量(Direction Vector)并不是最好的名字,因为它实际上指向从它到目标向量的相反方向

生成view矩阵的大致步骤

// 摄像机位置
QVector3D cameraPos = QVector3D(0.0, 0.0, 2.0);
// 摄像机方向
QVector3D cameraTarget = QVector3D(0.0, 0.0, 0.0);
QVector3D cameraDirection = QVector3D(cameraPos - cameraTarget);
cameraDirection.normalize();

// 右轴
QVector3D up = QVector3D(0.0, 1.0, 0.0);
QVector3D cameraRight = QVector3D::crossProduct(up, cameraDirection);
cameraRight.normalize();
// 上轴
QVector3D cameraUp = QVector3D::crossProduct(cameraDirection, cameraRight);
cameraRight.normalize();

使用QT的矩阵

QMatrix4x4 view;
view.lookAt(cameraPos, cameraTarget, QVector3D(0.0, 1.0, 0.0));

l o o k A t = [ R x R y R z 0 U x U y U z 0 D x D y D z 0 0 0 0 1 ] ∗ [ 1 0 0 − P x 0 1 0 − P y 0 0 1 − P z 0 0 0 1 ] lookAt =\left[\begin{matrix} Rx&Ry&Rz&0 \\ Ux&Uy&Uz&0 \\ Dx&Dy&Dz&0 \\ 0&0&0&1 \\ \end{matrix}\right] * \left[\begin{matrix} 1&0&0&-Px \\ 0&1&0&-Py \\ 0&0&1&-Pz \\ 0&0&0&1 \\ \end{matrix}\right] lookAt= RxUxDx0RyUyDy0RzUzDz00001 100001000010PxPyPz1

其中R是右向量,U是上向量,D是方向向量P是摄像机位置向量

const float radius = 10.0;
float time = m_time.elapsed()/1000.0;
float camX = sin(time) *radius;
float camZ = cos(time) *radius;

view.lookAt(QVector3D(camX, 0.0, camZ), QVector3D(0.0, 0.0, 0.0), QVector3D(0.0, 1.0, 0.0));

通过上面的改造我们能实现一个沿着某个轴的旋转。

摄像机自由移动

摄像机移动

如果我们总是盯着原点,不太礼貌且枯燥,我们需要向前看。

cameraFront = QVector3D(0.0, 0.0, -1.0);
view.lookAt(cameraPos, cameraPos + cameraFront, QVector3D(0.0, 1.0, 0.0));
 switch (event->key())
 {
     case Qt::Key_W:
         cameraPos += cameraSpeed * cameraFront;
         break;
     case Qt::Key_A:
         cameraPos -= cameraSpeed * cameraRight;
         break;
     case Qt::Key_S:
         cameraPos -= cameraSpeed * cameraFront;
         break;
     case Qt::Key_D:
         cameraPos += cameraSpeed * cameraRight;
         break;
 }

摄像机旋转

为了能够改变视角,我们需要根据鼠标的输入改变canmeraFront向量

欧拉角: 俯仰角(Pitch), 偏航角(Yaw), 滚转角(Roll);
在这里插入图片描述

主要代码:(无滚转角)

void TurboOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    static float yaw = -90;
    static float pitch = 0;
    static QPoint lastPos(width()/2, height()/2);
    auto curPos = event->pos();
    auto deltaPos = curPos - lastPos;
    lastPos = curPos;
    float sensitivity = 0.1f;
    deltaPos *= sensitivity;
    yaw += deltaPos.x();
    pitch -= deltaPos.y();
    float pi = 3.1415926;
    if(pitch > 89.0f) pitch = 89.0f;
    if(pitch < -89.0f) pitch = -89.0f;
    cameraFront.setX(cos(yaw*pi/180) * cos(pitch * pi/ 180));
    cameraFront.setY(sin(pitch*pi/180));
    cameraFront.setZ((yaw*pi/180) * cos(pitch*pi/180));
    cameraFront.normalize();
    update();
}

摄像机缩放

视野定义了可以看到场景中的多大范围。当视野变小时候,场景投影出来的空间就会减小,产生放大的感觉。我们会使用鼠标滚轮来放大物体。

QMatrix4x4 projection;
projection.perspective(fov, float(width() / height()), 0.1, 100);
shader_program_.setUniformValue("projection", projection);

void TurboOpenGLWidget::wheelEvent(QWheelEvent *event)
{
    if(fov >= 1.0 && fov <= 75.0)
        fov -= event->angleDelta().y()/120;
    if(fov <=1.0 ) fov = 1.0;
    if(fov >=75.0 ) fov = 75.0;
    update();
}

Camera封装

上述代码看看就可以,我们在实际使用的时候是不会这么写代码的,我们需要将摄像机封装成一个摄像机类,这里提供该类的基础版本的设计。

camera.h:

#ifndef QTOPENGL_CAMERA_H
#define QTOPENGL_CAMERA_H

#include<QMatrix4x4>
#include <vector>
#include <QOpenGLShaderProgram>

#define PI 3.141592653589793638
// 移动方向枚举量. 是一种抽象,以避开特定于窗口系统的输入方法
// 我们这里是WSAD
enum Camera_Movement {
    emFORWARD,
    emBACKWARD,
    emLEFT,
    emRIGHT
};

// 默认值
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;

// 一个抽象的camera类,用于处理输入并计算相应的Euler角度、向量和矩阵,以便在OpenGL中使用
class Camera
{
public:
    // constructor with vectors
    explicit Camera(QVector3D position = QVector3D(0.0f, 0.0f, 0.0f), QVector3D up = QVector3D(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH);
    // constructor with scalar values
    Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch);

    // returns the view matrix calculated using Euler Angles and the LookAt Matrix
    QMatrix4x4 getViewMatrix();

    // 处理从任何类似键盘的输入系统接收的输入。接受摄像机定义枚举形式的输入参数(从窗口系统中提取)
    void processKeyboard(Camera_Movement direction, float deltaTime);

    // 处理从鼠标输入系统接收的输入。需要x和y方向上的偏移值。
    void processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true);

    // 处理从鼠标滚轮事件接收的输入。仅需要在垂直车轮轴上输入
    void processMouseScroll(float yoffset);

    void setPosition(const QVector3D &position);
    QVector3D getPosition();

    void setFront(const QVector3D &front);
    QVector3D getFront();

    void setUp(const QVector3D &up);
    QVector3D getUp();

    void setRight(const QVector3D &right);
    QVector3D getRight();

    void setWorldUp(const QVector3D &worldUp);
    QVector3D getWorldUp();

    void setYaw(const float &yaw);
    float getYaw() const;

    void setPitch(const float &pitch);
    float getPitch() const;

    void setMovementSpeed(const float &movementSpeed);
    float getMovementSpeed() const;

    void setMouseSensitivity(const float &mouseSensitivity);
    float getMouseSensitivity() const;

    void setZoom(const float &zoom);
    float getZoom() const;

private:
    // 根据相机的(更新的)Euler角度计算前矢量
    void updateCameraVectors();

private:
    // 摄像机位置
    QVector3D m_position;

    // 前后移动值
    QVector3D m_front;

    // 上下移动值
    QVector3D m_up;

    // 左右移动值
    QVector3D m_right;

    QVector3D m_worldUp;

    // euler Angles
    float m_yaw;
    float m_pitch;
    // camera options
    float m_movementSpeed;
    float m_mouseSensitivity;
    float m_zoom;

};

#endif //QTOPENGL_CAMERA_H

camera.cpp:

#include "camera.h"

Camera::Camera(QVector3D position, QVector3D up, float yaw, float pitch)
        : m_front(QVector3D(0.0f, 0.0f, -1.0f)), m_movementSpeed(SPEED), m_mouseSensitivity(SENSITIVITY), m_zoom(ZOOM)
{
    m_position = position;
    m_worldUp = up;
    m_yaw = yaw;
    m_pitch = pitch;
    updateCameraVectors();
}

Camera::Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch)
        : m_front(QVector3D(0.0f, 0.0f, -1.0f)), m_movementSpeed(SPEED), m_mouseSensitivity(SENSITIVITY), m_zoom(ZOOM)
{
    m_position = QVector3D(posX, posY, posZ);
    m_worldUp = QVector3D(upX, upY, upZ);
    m_yaw = yaw;
    m_pitch = pitch;
    updateCameraVectors();
}

QMatrix4x4 Camera::getViewMatrix()
{
    QMatrix4x4 theMatrix;
    theMatrix.lookAt(m_position, m_position + m_front, m_up);
    return theMatrix;
}

void Camera::processKeyboard(Camera_Movement direction, float deltaTime)
{
    float velocity = m_movementSpeed * deltaTime;
    if (direction == emFORWARD)
        m_position += m_front * velocity;
    if (direction == emBACKWARD)
        m_position -= m_front * velocity;
    if (direction == emLEFT)
        m_position -= m_right * velocity;
    if (direction == emRIGHT)
        m_position += m_right * velocity;
}

void Camera::processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch)
{
    xoffset *= m_mouseSensitivity;
    yoffset *= m_mouseSensitivity;

    m_yaw   += xoffset;
    m_pitch += yoffset;

    // 确保当投球超出边界时,屏幕不会翻转
    if (constrainPitch)
    {
        if (m_pitch > 89.0f)
            m_pitch = 89.0f;
        if (m_pitch < -89.0f)
            m_pitch = -89.0f;
    }

    // 使用更新的Euler角度更新前、右和上矢量
    updateCameraVectors();
}

void Camera::processMouseScroll(float yoffset)
{
    m_zoom -= (float)yoffset;
    if (m_zoom < 1.0f)
        m_zoom = 1.0f;
    if (m_zoom > 75.0f)
        m_zoom = 75.0f;
}

void Camera::updateCameraVectors()
{
    // calculate the new Front vector
    QVector3D front;
    front.setX(cos(m_yaw*PI/180.0) * cos(m_pitch*PI/180.0));
    front.setY( sin(m_pitch*PI/180.0));
    front.setZ(sin(m_yaw*PI/180.0) * cos(m_pitch*PI/180.0));
    front.normalize();
    m_front = front;
    // also re-calculate the Right and Up vector
    m_right = QVector3D::crossProduct(m_front, m_worldUp);
    // 标准化向量,因为向上或向下看得越多,向量的长度就越接近0,这会导致移动速度变慢。
    m_right.normalize();
    m_up = QVector3D::crossProduct(m_right, m_front);
    m_up.normalize();
}

QVector3D Camera::getPosition()
{
    return m_position;
}

void Camera::setPosition(const QVector3D &position)
{
    m_position = position;
}

void Camera::setFront(const QVector3D &front)
{
    m_front = front;
}

QVector3D Camera::getFront()
{
    return m_front;
}

void Camera::setUp(const QVector3D &up)
{
    m_up = up;
}

QVector3D Camera::getUp()
{
    return m_up;
}

void Camera::setRight(const QVector3D &right)
{
    m_right = right;
}

QVector3D Camera::getRight()
{
    return m_right;
}

void Camera::setWorldUp(const QVector3D &worldUp)
{
    m_worldUp = worldUp;
}

QVector3D Camera::getWorldUp()
{
    return m_worldUp;
}

void Camera::setYaw(const float &yaw)
{
    m_yaw = yaw;
}

float Camera::getYaw() const
{
    return m_yaw;
}

void Camera::setPitch(const float &pitch)
{
    m_pitch = pitch;
}

float Camera::getPitch() const
{
    return m_pitch;
}

void Camera::setMovementSpeed(const float &movementSpeed)
{
    m_movementSpeed = movementSpeed;
}

float Camera::getMovementSpeed() const
{
    return m_movementSpeed;
}

void Camera::setMouseSensitivity(const float &mouseSensitivity)
{
    m_mouseSensitivity = mouseSensitivity;
}

float Camera::getMouseSensitivity() const
{
    return m_mouseSensitivity;
}

void Camera::setZoom(const float &zoom)
{
    m_zoom = zoom;
}

float Camera::getZoom() const
{
    return m_zoom;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

turbolove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值