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

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应用程序创建灵活、功能丰富的相机系统。

http://www.xdnf.cn/news/10522.html

相关文章:

  • 机器学习算法:逻辑回归
  • 操作系统复习
  • 方法重写与方法重载详解
  • CSS之动画(奔跑的熊、两面反转盒子、3D导航栏、旋转木马)
  • 谷歌CEO皮查伊眼中的“下一代平台“与未来图景
  • 基于FPGA的VGA显示文字和动态数字基础例程,进而动态显示数据,类似温湿度等
  • Pyomo中线性规划接口的使用
  • 为什么ping显示connect:network is unreachable,如何排查网络不通问题?
  • LearnOpenGL-笔记-其十三
  • py爬虫的话,selenium是不是能完全取代requests?
  • NodeJS全栈WEB3面试题——P2智能合约与 Solidity
  • 单调栈(打卡)
  • 【C++/Linux】TinyWebServer前置知识之IP协议详解
  • 【iOS】YYModel源码解析
  • 告别printf!嵌入式系统高效日志记录方案
  • 如何评估 RAG 的分块Chunking策略
  • 【沉浸式求职学习day52】【初识Mybaits】
  • 风控研发大数据学习路线
  • Java生态中的NLP框架
  • 【C语言】C语言经典小游戏:贪吃蛇(上)
  • Vortex GPGPU的github流程跑通与功能模块波形探索(四)
  • 解决:install via Git URL失败的问题
  • 【LLM vs Agent】从语言模型到智能体,人工智能迈出的关键一步
  • Java中对象哈希值的解析
  • 力扣HOT100之多维动态规划:64. 最小路径和
  • Langchian - 自定义提示词模板 提取结构化的数据
  • bismark OT CTOT OB CTOB 以及mapping后的bam文件中的XG,XR列的含义
  • 用go从零构建写一个RPC(4)--gonet网络框架重构+聚集发包
  • 【知识点】第3章:基本数据类型
  • Linux之进程间通信