Qt配置OpenGL相机踩的坑

发布于:2025-03-13 ⋅ 阅读:(12) ⋅ 点赞:(0)

项目根据LearnOpenGL配置Qt的相机,更新view矩阵和project矩阵的位移向量变得很大,我设置的明明相机位置是(0,0,3),理想的位移向量刚好是相反数(0,0,-3),对应的view矩阵位置向量可以变成(0,0,1200)…离模型非常远矩阵模型也看不见:

#include "UI/RobotView.h"
#include <QtCore/QtGlobal>
#include <QtCore/QFile>
#include <QtCore/QDebug>
#include <QtGui/QMouseEvent>
#include <QtGui/QWheelEvent>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLBuffer>
#include <QtGui/QOpenGLVertexArrayObject>
#include <QtGui/QMatrix4x4>
#include <QtGui/QVector3D>
#include <QtWidgets/QOpenGLWidget>
#include <QElapsedTimer>
#include <QtMath>

#include <urdf_model/model.h>
#include <urdf_parser/urdf_parser.h>

RobotView::RobotView(QWidget *parent) : QOpenGLWidget(parent),
                                        VBO(QOpenGLBuffer::VertexBuffer),
                                        model_(nullptr),
                                        firstMouse(true),
                                        cameraZoom(45.0f),
                                        cameraYaw(-90.0f),
                                        cameraPitch(0.0f),
                                        cameraPosition(0.0f, 0.0f, 3.0f),
                                        worldUp(0.0f, 1.0f, 0.0f),
                                        cameraFront(0.0f, 0.0f, -1.0f), //*
                                        cameraUp(0.0f, 1.0f, 0.0f),
                                        mouseSensitivity(0.1f)
{
    setFocusPolicy(Qt::StrongFocus);

    frameCount = 0;
    fps = 0.0f;

    fpsTimer = new QTimer(this);
    connect(fpsTimer, &QTimer::timeout, this, [this]()
            {
                fps = frameCount;
                frameCount = 0;
                emit sendFPS(fps); // 发送帧率信号
            });
    fpsTimer->start(1000);
    updateTimer = new QTimer(this);
    connect(updateTimer, &QTimer::timeout, this, [this]()
            { update(); });
    updateTimer->start(16); // 60 FPS
    model_ = new RobotModel();
    viewMatrix.lookAt(cameraPosition, cameraPosition + cameraFront, cameraUp);
    projectionMatrix.perspective(cameraZoom, float(width()) / float(height()), 0.01f, 200.0f);
    if (!model_->loadFromURDF(":/assets/instrument_sim/urdf/instrument_sim.urdf"))
    {
        qCritical() << "Failed to load URDF file";
        delete model_;
    }
}

RobotView::~RobotView()
{
    delete fpsTimer;
    delete updateTimer;
    delete model_;
    fpsTimer = nullptr;
    updateTimer = nullptr;
    model_ = nullptr;
    cleanupGL();
}

void RobotView::initializeGL()
{
    initializeOpenGLFunctions();

    VAO.create();
    VBO.create();

    VAO.bind();
    VBO.bind();
    if (!initShaders())
    {
        qCritical() << "Failed to initialize shaders";
        return;
    }

    VAO.release();
    VBO.release();

    glEnable(GL_DEPTH_TEST);
}

void RobotView::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

void RobotView::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    shaderProgram.bind();
    projectionMatrix.perspective(cameraZoom, float(width()) / float(height()), 0.01f, 200.0f);
    viewMatrix.lookAt(cameraPosition, cameraPosition + cameraFront, cameraUp);

    // 将矩阵传递给着色器
    shaderProgram.setUniformValue("lightPos", lightPos);
    shaderProgram.setUniformValue("viewPos", cameraPosition);
    shaderProgram.setUniformValue("objectColor", objectColor);
    shaderProgram.setUniformValue("lightColor", lightColor);
    shaderProgram.setUniformValue("projectionMatrix", projectionMatrix);
    shaderProgram.setUniformValue("viewMatrix", viewMatrix);

    VAO.bind();

    // // Draw robot model
    auto &link = model_->getLinks()[1];
    if (link.visual)
    {
        VBO.bind();

        VBO.allocate(link.visual->getVertices(), link.visual->getVerticesSize() * sizeof(float));
        modelMatrix.setToIdentity();
        shaderProgram.setUniformValue("modelMatrix", modelMatrix);
        // Draw triangles
        int positionAttribute = shaderProgram.attributeLocation("aPos");
        shaderProgram.enableAttributeArray(positionAttribute);
        shaderProgram.setAttributeBuffer(positionAttribute, GL_FLOAT, 0, 3, 6 * sizeof(GLfloat));

        // 设置顶点属性
        int normalAttribute = shaderProgram.attributeLocation("aNormal");
        shaderProgram.enableAttributeArray(normalAttribute);
        shaderProgram.setAttributeBuffer(normalAttribute, GL_FLOAT, 3 * sizeof(GLfloat), 3, 6 * sizeof(GLfloat));
        glDrawArrays(GL_TRIANGLES, 0, link.visual->getVerticesSize() / 6);

        VBO.release();
    }
    VAO.release();
    shaderProgram.release();

    frameCount++;
}

bool RobotView::initShaders()
{
    // Load vertex shader
    QFile vertShaderFile(":/assets/shaders/phongShader.vert");

    if (!vertShaderFile.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qCritical() << "Failed to open vertex shader file:" << vertShaderFile.fileName();
        doneCurrent();
        return false;
    }
    QString vertShaderSource = vertShaderFile.readAll();
    vertShaderFile.close();

    // Load fragment shader
    QFile fragShaderFile(":/assets/shaders/phongShader.frag");

    if (!fragShaderFile.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qCritical() << "Failed to open fragment shader file:" << fragShaderFile.fileName();
        return false;
    }
    QString fragShaderSource = fragShaderFile.readAll();
    fragShaderFile.close();

    // Compile shaders
    if (!shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertShaderSource))
    {
        qCritical() << "Failed to compile vertex shader:" << shaderProgram.log();
        return false;
    }

    if (!shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShaderSource))
    {
        qCritical() << "Failed to compile fragment shader:" << shaderProgram.log();
        return false;
    }

    // Link shader program
    if (!shaderProgram.link())
    {
        qCritical() << "Failed to link shader program:" << shaderProgram.log();
        return false;
    }

    return true;
}

void RobotView::mouseMoveEvent(QMouseEvent *event)
{
    // update Front, Right and Up Vectors using the updated Euler angles
    
    if (event->buttons() & Qt::LeftButton)
    {
    	float xPos = event->x();
    	float yPos = event->y();
        if (firstMouse)
        {
            lastMousePosX = xPos;
            lastMousePosY = yPos;
            firstMouse = false;
        }
        float xoffset = xPos - lastMousePosX;
        float yoffset = lastMousePosY - yPos;

		lastMousePosX = xPos;
	    lastMousePosY = yPos;
	    
        xoffset *= mouseSensitivity;
        yoffset *= mouseSensitivity;

        cameraYaw += xoffset;
        cameraPitch += yoffset;

        // make sure that when pitch is out of bounds, screen doesn't get flipped
        if (cameraPitch > 89.0f)
            cameraPitch = 89.0f;
        if (cameraPitch < -89.0f)
            cameraPitch = -89.0f;
        QVector3D front;
        front.setX(cos(qDegreesToRadians(cameraYaw)) * cos(qDegreesToRadians(cameraPitch)));
        front.setY(sin(qDegreesToRadians(cameraPitch)));
        front.setZ(sin(qDegreesToRadians(cameraYaw)) * cos(qDegreesToRadians(cameraPitch)));
        cameraFront = front.normalized();
    }
    
}


void RobotView::cleanupGL()
{
    makeCurrent();
    VAO.destroy();
    VBO.destroy();
    shaderProgram.removeAllShaders();
    doneCurrent();
}


解决办法:

  1. 在每一次使用lookat和perspective函数前都将矩阵置为identity,根据手册,这两个函数api和glm不一样,会一直连乘之前的矩阵,所以调用这个函数api,先得吧矩阵view和project变为单位阵,防止一直连乘跑飞
    在这里插入图片描述

在这里插入图片描述

projectionMatrix.setToIdentity();
projectionMatrix.perspective(cameraZoom, float(width()) / float(height()), 0.01f, 200.0f);
viewMatrix.setToIdentity();
viewMatrix.lookAt(cameraPosition, cameraPosition + cameraFront, cameraUp);

加了以后好一点了,但是鼠标拖拽有时候移动到其他位置再拖动,模型会突然跳一下,分析了一下原因是因为鼠标的位置没有及时更新:

  1. 在构造函数加上鼠标的跟踪,修改把获取位置更新上一时刻位置放在if判断外面:

void RobotView::mouseMoveEvent(QMouseEvent *event)
{
    // update Front, Right and Up Vectors using the updated Euler angles
    float xPos = event->x();
    float yPos = event->y();
    if (event->buttons() & Qt::LeftButton)
    {
        if (firstMouse)
        {
            lastMousePosX = xPos;
            lastMousePosY = yPos;
            firstMouse = false;
        }
        float xoffset = xPos - lastMousePosX;
        float yoffset = lastMousePosY - yPos;

        xoffset *= mouseSensitivity;
        yoffset *= mouseSensitivity;

        cameraYaw += xoffset;
        cameraPitch += yoffset;

        // make sure that when pitch is out of bounds, screen doesn't get flipped
        if (cameraPitch > 89.0f)
            cameraPitch = 89.0f;
        if (cameraPitch < -89.0f)
            cameraPitch = -89.0f;
        QVector3D front;
        front.setX(cos(qDegreesToRadians(cameraYaw)) * cos(qDegreesToRadians(cameraPitch)));
        front.setY(sin(qDegreesToRadians(cameraPitch)));
        front.setZ(sin(qDegreesToRadians(cameraYaw)) * cos(qDegreesToRadians(cameraPitch)));
        cameraFront = front.normalized();
        // viewMatrix.setToIdentity();
        // viewMatrix.lookAt(cameraPosition, cameraPosition + cameraFront, cameraUp);
    }
    lastMousePosX = xPos;
    lastMousePosY = yPos;
}

但是发现还是有问题,貌似mouseMove事件需要按键按下才触发,鼠标的位置没有得到及时更新,经过查资料,推测可能是没有开启鼠标的跟踪,在构造函数加入下面的语句就可以了

setMouseTracking(true);

调bug还是看看用户手册的好