QT+OpenGL 摄像机

news2024/11/18 0:44:35

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/338816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux内核启动(2,0.11版本)内核启动前的苦力活与内核启动

内核启动前的工作 在上一章的内容中&#xff0c;我们跳转到了setup.s的代码部分&#xff0c;这章我们先讲一讲setup做了什么吧 entry start start:! ok, the read went well so we get current cursor position and save it for ! posterity.mov ax,#INITSEG ! this is done …

Flowable进阶学习(十)定时器、ServiceTask服务任务、ScriptTask脚本任务

文章目录一、定时器1. 流程定义定时激活2. 流程实例定时挂起3. 定时任务执行过程ServiceTask 服务任务委托表达式表达式类中字段ScriptTask 脚本任务JS TASK一、定时器 相关知识链接阅读&#xff1a;事件网关——定时器启动事件 1. 流程定义定时激活 可以通过activateProces…

材质笔记 - Simluate Solid Surface

光的行为 当光和物体相遇时&#xff0c;光会有三种行为&#xff1a;被物体反射、穿过物体&#xff08;物体是透明或半透明的&#xff09;或者被吸收。 高光反射和漫反射 高光反射&#xff08;Specular Reflection&#xff09;会在表面光滑且反光的物体上看到&#xff0c;比如镜…

SMART PLC时间间隔定时器应用(高速脉冲测频/测速)

高速脉冲计数测量频率,专栏有系列文章分析讲解,这里不再赘述(原理都是利用差分代替微分)。具体链接如下: 西门子SMART PLC高速脉冲计数采集编码器速度(RC滤波)_RXXW_Dor的博客-CSDN博客这篇文章主要讲解西门子 SMART PLC高速计数采集编码器脉冲信号计算速度,根据编码器脉…

鸢尾花数据集分类(PyTorch实现)

一、数据集介绍 Data Set Information: This is perhaps the best known database to be found in the pattern recognition literature. Fisher’s paper is a classic in the field and is referenced frequently to this day. (See Duda & Hart, for example.) The data…

[Android Studio]Android 数据存储-文件存储学习笔记-结合保存QQ账户与密码存储到指定文件中的演练

&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Android Debug&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Topic 发布安卓学习过程中遇到问题解决过程&#xff0c;希望我的解决方案可以对小伙伴们有帮助。 &#x1f4cb;笔记目…

戴尔游匣G16电脑U盘安装系统操作教程分享

戴尔游匣G16电脑U盘安装系统操作教程分享。有用户在使用戴尔游匣G16电脑的时候遇到了系统问题&#xff0c;比如电脑蓝屏、自动关机重启、驱动不兼容等问题。遇到这些问题如果无法进行彻底解决&#xff0c;我们可以通过U盘重新安装系统的方法来解决&#xff0c;因为这些问题一般…

I.MX6ULL内核开发7:led字符设备驱动实验

目录 一、led字符设备驱动实验 二、驱动模块初始化 三、虚拟地址读写 四、自定义led的file_operation接口 五、拷贝数据 六、register_chrdev函数 七、 __register_chrdev函数 八、编译执行 一、led字符设备驱动实验 驱动模块内核模块(.ko)驱动接口(file_operations) …

Mysql 增删改查(一) —— 查询(条件查询where、分页limits、排序order by)

查询 select 可以认为是四个基本操作中使用最为频繁的操作&#xff0c;然而数据量比较大的时候&#xff0c;我们不可能查询所有内容&#xff0c;我们一般会搭配其他语句进行查询&#xff1a; 假如要查询某一个字段的内容&#xff0c;可以使用 where假如要查询前几条记录&#…

STM32----搭建Arduino开发环境

搭建Arduino开发环境前言一、Arduino软件1.软件下载2.软件安装3.软件操作二、Cortex官方内核三、烧录下载四、其他第三方内核1.Libmaple内核2.Steve改进的LibMaple 内核3.STMicroelectronics(ST)公司编写的内核总结前言 本章介绍搭建STM32搭建Arduino开发环境&#xff0c;包括…

leetcode470 用Rand7()实现Rand10()

力扣470 第一步&#xff1a;根据Rand7()函数制作一个可以随机等概率生成0和1的函数rand_0and1 调用Rand7()函数&#xff0c;随机等概率生成1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#xff0c;7 这时我们设置&#xff1a;生成1&#xff0c;2&a…

“深度学习”学习日记。卷积神经网络--用CNN的实现MINIST识别任务

2023.2.11 通过已经实现的卷积层和池化层&#xff0c;搭建CNN去实现MNIST数据集的识别任务&#xff1b; 一&#xff0c;简单CNN的网络构成&#xff1a; 代码需要在有网络的情况下运行&#xff0c;因为会下载MINIST数据集&#xff0c;运行后会生成params.pkl保留训练权重&…

【吉先生的Java全栈之路】

吉士先生Java全栈学习路线&#x1f9e1;第一阶段Java基础: 在第一阶段:我们要认真听讲,因为基础很重要!基础很重要!基础很重要!!! 重要的事情说三遍。在这里我们先学JavaSE路线&#xff1b;学完之后我们要去学第一个可视化组件编程《GUI》&#xff1b;然后写个《贪吃蛇》游戏耍…

微搭低代码从入门到精通05-变量定义

我们上一篇对应用编辑器有了一个整体的介绍。要想零基础开发小程序&#xff0c;就得从各种概念开始学起。 如果你是零基础学习开发&#xff0c;无论学习哪一门语言&#xff0c;第一个需要掌握的知识点就是变量。 那么什么是变量&#xff1f;变量其实就是存放数据的一个容器&a…

专题 | 防抖和节流

一 防抖&#xff1a;单位时间内&#xff0c;频繁触发事件&#xff0c;只执行最后一次 场景&#xff1a;搜索框搜索输入&#xff08;利用定时器&#xff0c;每次触发先清掉以前的定时器&#xff0c;从新开始&#xff09; 节流&#xff1a;单位时间内&#xff0c;频繁触发事件&…

Yii2模板:自定义头部脚部文件,去掉头部脚部文件

一、yii安装完成之后&#xff0c;运行结果如下图二、如何自定义头部脚部文件呢0、默认展示1、在类里定义&#xff0c;在整个类中生效2、在方法中定义&#xff0c;在当前方法中生效3、home模板介绍三、去掉头部脚部文件1、控制 $layout 的值2、把action中的render改为renderPart…

前端对于深拷贝和浅拷贝的应用和思考

浅拷贝 浅拷贝 &#xff1a; 浅拷贝是指对基本类型的值拷贝&#xff0c;以及对对象类型的地址拷贝。它是将数据中所有的数据引用下来&#xff0c;依旧指向同一个存放地址&#xff0c;拷贝之后的数据修改之后&#xff0c;也会影响到原数据的中的对象数据。最简单直接的浅拷贝就…

java ssm集装箱码头TOS系统调度模块的设计与实现

由于历史和经济体制的原因&#xff0c;国内码头物流企业依然保持大而全的经营模式。企业自己建码头、场地、经营集装箱运输车辆。不过近几年来随着经济改革的进一步深入和竞争的激烈&#xff0c;一些大型的码头物流企业逐步打破以前的经营模式&#xff0c;其中最明显的特征就是…

利用机器学习(mediapipe)进行人脸468点的3D坐标检测--视频实时检测

上期文章,我们分享了人脸468点的3D坐标检测的图片检测代码实现过程,我们我们介绍一下如何在实时视频中,进行人脸468点的坐标检测。 import cv2 import mediapipe as mp mp_drawing = mp.solutions.drawing_utils mp_face_mesh = mp.solutions.face_mesh face_mesh = mp_fac…

ubuntu 驱动更新后导致无法进入界面

**问题描述&#xff1a; **安装新ubuntu系统后未禁止驱动更新导致无法进入登录界面。 解决办法&#xff1a; 首先在进入BIOS中&#xff0c;修改设置以进行命令行操作&#xff0c;然后卸载已有的系统驱动&#xff0c;最后安装新的驱动即可。 开机按F11进入启动菜单栏&#xf…