当前位置: 首页 > news >正文

Qt OpenGL 相机实现

在Qt中使用OpenGL实现相机功能主要涉及视图矩阵(view matrix)的操作,包括相机位置、观察方向和上向量等概念。下面我将介绍如何在Qt中实现一个基本的3D相机。

基本概念

OpenGL相机本质上是通过视图矩阵(view matrix)来实现的,它定义了从世界空间到观察空间的变换。视图矩阵可以通过以下参数构建:

  1. 相机位置(camera position)

  2. 目标位置(target position)

  3. 上向量(up vector)

实现步骤

1. 包含必要的头文件

cpp

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QMatrix4x4>
#include <QVector3D>
#include <QKeyEvent>

2. 定义相机类

Camera.h

#ifndef CAMERA_H
#define CAMERA_H#include <QVector3D>
#include <QMatrix4x4>
#include <QQuaternion>class Camera
{
public:Camera();void setPosition(const QVector3D &position);void setTarget(const QVector3D &target);void setUpVector(const QVector3D &up);QMatrix4x4 getViewMatrix() const;QVector3D getPosition() const { return m_position; }void moveForward(float distance);void moveRight(float distance);void moveUp(float distance);void rotate(float yaw, float pitch);private:void updateVectors();QVector3D m_position;QVector3D m_target;QVector3D m_up;QVector3D m_right;float m_yaw;float m_pitch;
};#endif // CAMERA_H

3. 实现相机类

Camera.cpp

#include "camera.h"
#include <QtMath>Camera::Camera() :m_position(0.0f, 0.0f, 3.0f),m_target(0.0f, 0.0f, -1.0f),m_up(0.0f, 1.0f, 0.0f),m_yaw(-90.0f),m_pitch(0.0f)
{updateVectors();
}void Camera::setPosition(const QVector3D &position)
{m_position = position;updateVectors();
}void Camera::setTarget(const QVector3D &target)
{m_target = target;updateVectors();
}void Camera::setUpVector(const QVector3D &up)
{m_up = up;updateVectors();
}QMatrix4x4 Camera::getViewMatrix() const
{QMatrix4x4 view;view.lookAt(m_position, m_position + m_target, m_up);return view;
}void Camera::moveForward(float distance)
{m_position += m_target * distance;
}void Camera::moveRight(float distance)
{m_position += m_right * distance;
}void Camera::moveUp(float distance)
{m_position += m_up * distance;
}void Camera::rotate(float yaw, float pitch)
{m_yaw += yaw;m_pitch += pitch;// 限制俯仰角,防止万向节死锁if (m_pitch > 89.0f)m_pitch = 89.0f;if (m_pitch < -89.0f)m_pitch = -89.0f;updateVectors();
}void Camera::updateVectors()
{// 计算新的前向量QVector3D front;front.setX(cos(qDegreesToRadians(m_yaw)) * cos(qDegreesToRadians(m_pitch)));front.setY(sin(qDegreesToRadians(m_pitch)));front.setZ(sin(qDegreesToRadians(m_yaw)) * cos(qDegreesToRadians(m_pitch)));m_target = front.normalized();// 重新计算右向量和上向量m_right = QVector3D::crossProduct(m_target, QVector3D(0.0f, 1.0f, 0.0f)).normalized();m_up = QVector3D::crossProduct(m_right, m_target).normalized();
}

4. 在OpenGLWidget中使用相机

OpenGLWidget.h

#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMatrix4x4>
#include <QVector3D>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#include "camera.h"class OpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{Q_OBJECTpublic:explicit OpenGLWidget(QWidget *parent = nullptr);~OpenGLWidget();protected:void initializeGL() override;void resizeGL(int w, int h) override;void paintGL() override;void keyPressEvent(QKeyEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void wheelEvent(QWheelEvent *event) override;private:void initShaders();void initCube(float width);QOpenGLShaderProgram m_program;QOpenGLVertexArrayObject m_vao;QOpenGLBuffer m_vbo;Camera m_camera;QMatrix4x4 m_projection;QPoint m_lastMousePos;bool m_firstMouse = true;
};#endif // OPENGLWIDGET_H

5. 实现OpenGLWidget

OpenGLWidget.cpp

#include "openglwidget.h"
#include <QDebug>OpenGLWidget::OpenGLWidget(QWidget *parent) :QOpenGLWidget(parent),m_lastMousePos(QPoint(width()/2, height()/2))
{setFocusPolicy(Qt::StrongFocus);setMouseTracking(true);
}OpenGLWidget::~OpenGLWidget()
{m_vao.destroy();m_vbo.destroy();
}void OpenGLWidget::initializeGL()
{initializeOpenGLFunctions();glClearColor(0.2f, 0.3f, 0.3f, 1.0f);initShaders();initCube(1.0f);glEnable(GL_DEPTH_TEST);
}void OpenGLWidget::resizeGL(int w, int h)
{glViewport(0, 0, w, h);m_projection.setToIdentity();m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
}void OpenGLWidget::paintGL()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);m_program.bind();// 设置模型、视图和投影矩阵QMatrix4x4 model;model.setToIdentity();model.translate(0.0f, 0.0f, 0.0f);m_program.setUniformValue("model", model);m_program.setUniformValue("view", m_camera.getViewMatrix());m_program.setUniformValue("projection", m_projection);// 绘制立方体m_vao.bind();glDrawArrays(GL_TRIANGLES, 0, 36);m_vao.release();m_program.release();
}void OpenGLWidget::initShaders()
{if (!m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex.glsl"))qDebug() << "Vertex shader error:" << m_program.log();if (!m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment.glsl"))qDebug() << "Fragment shader error:" << m_program.log();if (!m_program.link())qDebug() << "Shader program link error:" << m_program.log();
}void OpenGLWidget::initCube(float width)
{float halfWidth = width / 2.0f;QVector<QVector3D> vertices;// 前面vertices << QVector3D(-halfWidth, -halfWidth, halfWidth);vertices << QVector3D(halfWidth, -halfWidth, halfWidth);vertices << QVector3D(halfWidth, halfWidth, halfWidth);vertices << QVector3D(halfWidth, halfWidth, halfWidth);vertices << QVector3D(-halfWidth, halfWidth, halfWidth);vertices << QVector3D(-halfWidth, -halfWidth, halfWidth);// 后面vertices << QVector3D(-halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(halfWidth, halfWidth, -halfWidth);vertices << QVector3D(halfWidth, halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, -halfWidth, -halfWidth);// 左面vertices << QVector3D(-halfWidth, halfWidth, halfWidth);vertices << QVector3D(-halfWidth, halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, -halfWidth, halfWidth);vertices << QVector3D(-halfWidth, halfWidth, halfWidth);// 右面vertices << QVector3D(halfWidth, halfWidth, halfWidth);vertices << QVector3D(halfWidth, halfWidth, -halfWidth);vertices << QVector3D(halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(halfWidth, -halfWidth, halfWidth);vertices << QVector3D(halfWidth, halfWidth, halfWidth);// 上面vertices << QVector3D(-halfWidth, halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, halfWidth, halfWidth);vertices << QVector3D(halfWidth, halfWidth, halfWidth);vertices << QVector3D(halfWidth, halfWidth, halfWidth);vertices << QVector3D(halfWidth, halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, halfWidth, -halfWidth);// 下面vertices << QVector3D(-halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, -halfWidth, halfWidth);vertices << QVector3D(halfWidth, -halfWidth, halfWidth);vertices << QVector3D(halfWidth, -halfWidth, halfWidth);vertices << QVector3D(halfWidth, -halfWidth, -halfWidth);vertices << QVector3D(-halfWidth, -halfWidth, -halfWidth);m_vao.create();m_vao.bind();m_vbo.create();m_vbo.bind();m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(QVector3D));m_program.bind();m_program.enableAttributeArray(0);m_program.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(QVector3D));m_vao.release();m_vbo.release();
}void OpenGLWidget::keyPressEvent(QKeyEvent *event)
{float cameraSpeed = 0.05f;switch(event->key()) {case Qt::Key_W:m_camera.moveForward(cameraSpeed);break;case Qt::Key_S:m_camera.moveForward(-cameraSpeed);break;case Qt::Key_A:m_camera.moveRight(-cameraSpeed);break;case Qt::Key_D:m_camera.moveRight(cameraSpeed);break;case Qt::Key_Space:m_camera.moveUp(cameraSpeed);break;case Qt::Key_Shift:m_camera.moveUp(-cameraSpeed);break;}update();
}void OpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{if (event->buttons() & Qt::RightButton) {if (m_firstMouse) {m_lastMousePos = event->pos();m_firstMouse = false;}QPoint delta = event->pos() - m_lastMousePos;m_lastMousePos = event->pos();float sensitivity = 0.1f;m_camera.rotate(delta.x() * sensitivity, -delta.y() * sensitivity);update();}
}void OpenGLWidget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::RightButton) {m_lastMousePos = event->pos();}
}void OpenGLWidget::wheelEvent(QWheelEvent *event)
{QPoint numDegrees = event->angleDelta() / 8;if (!numDegrees.isNull()) {float zoom = numDegrees.y() / 15.0f;m_camera.moveForward(zoom);}event->accept();update();
}

6.着色器代码

vertex.glsl

#version 330 corelayout (location = 0) in vec3 aPos;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);
}

fragment.glsl

#version 330 coreout vec4 FragColor;void main()
{FragColor = vec4(0.8, 0.3, 0.02, 1.0);
}
注意:
1)vertex.glsl和fragment.glsl需要添加QT工程的资源文件中。比如:shaders.qrc
<RCC><qresource prefix="/shaders"><file>shaders/vertex.glsl</file><file>shaders/fragment.glsl</file></qresource>
</RCC>

2)交互功能

移动控制:W: 向前移动  S: 向后移动   A: 向左移动  D: 向右移动 Space: 向上移动 Shift: 向下移动
视角控制:按住鼠标右键并移动鼠标可以旋转视角。鼠标滚轮可以缩放视图。

 7.主窗口使用

#include <QApplication>
#include "openglwidget.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);QSurfaceFormat format;format.setVersion(3, 3);format.setProfile(QSurfaceFormat::CoreProfile);format.setDepthBufferSize(24);QSurfaceFormat::setDefaultFormat(format);OpenGLWidget w;w.resize(800, 600);w.setWindowTitle("Qt OpenGL Camera Example");w.show();return a.exec();
}

高级功能扩展

1. 添加FPS相机

cpp

class FPSCamera : public Camera {
public:void update(float deltaTime);void setMovementSpeed(float speed) { m_movementSpeed = speed; }void setMouseSensitivity(float sensitivity) { m_mouseSensitivity = sensitivity; }private:float m_movementSpeed = 2.5f;float m_mouseSensitivity = 0.1f;
};

2. 添加鼠标滚轮缩放

cpp

void OpenGLWidget::wheelEvent(QWheelEvent *event) {QPoint numDegrees = event->angleDelta() / 8;if (!numDegrees.isNull()) {float zoom = numDegrees.y() / 15.0f;m_camera.moveForward(zoom);}event->accept();update();
}

3. 添加弧球相机(Arcball Camera)

cpp

class ArcballCamera : public Camera {
public:void rotate(float angleX, float angleY);void zoom(float distance);void pan(float x, float y);private:float m_radius = 5.0f;QVector3D m_center;
};

总结

在Qt中实现OpenGL相机主要涉及:

  1. 创建相机类管理视图矩阵

  2. 处理键盘和鼠标输入来控制相机

  3. 在渲染时应用视图和投影矩阵

  4. 根据需求扩展相机功能(FPS、弧球等)

通过这种方式,你可以为Qt OpenGL应用程序创建灵活、功能丰富的相机系统。

相关文章:

  • 无他相机:专业摄影,触手可及
  • 排序算法C语言实现
  • flutter开发安卓APP适配不同尺寸的手机屏幕
  • FreeBSD 14.3 候选版本附带 Docker 镜像和关键修复
  • java28
  • SystemVerilog—new函数的使用和误区
  • 数据结构之堆:解析与应用
  • 数据结构哈希表总结
  • 高阶数据结构——并查集
  • HealthBench医疗AI评估基准:技术路径与核心价值深度分析(上)
  • 光伏功率预测 | BiLSTM多变量单步光伏功率预测(Matlab完整源码和数据)
  • React 核心概念与生态系统
  • Transformer 是未来的技术吗?
  • arc3.2语言sort的时候报错:(sort < `(2 9 3 7 5 1)) 需要写成这种:(sort > (pair (list 3 2)))
  • 【Linux系列】Gunicorn 进程架构解析:主进程与工作进程
  • DAY 43 复习日
  • 网络安全:网页密码防护与记住密码功能的安全
  • 常见ADB指令
  • CLion调试无法触发断点
  • CppCon 2014 学习:Gamgee: A C++14 library for genomic data processing and analysis
  • c 做网站/服务营销策划方案
  • 武汉网站制作 费用/外贸推广有哪些好的方式
  • 浪起网站建设/芜湖网络营销公司
  • 购物网站建设流程/互动营销经典案例
  • 政府网站建设 讲话/广告投放平台系统
  • 我要啦免费统计怎么做网站/网上教育培训机构