Qt3D改变观察视角例程(二)

发布时间 2023-11-03 11:05:33作者: 兜尼完

本例依旧是改变3D视角。不同的是这个是视野位置不变而只改变观察方向。相当于一个人站在原地不动,旋转脑袋看周围的东西。测试的条件是VS2017和Qt5.9。主要的知识点就是欧拉角的计算。下面是效果图:

头文件:

class QOpenGLTexture;
class QOpenGLBuffer;

class MvOpenGLWidget : public QOpenGLWidget, private QOpenGLFunctions
{
    Q_OBJECT

private:
    struct VertexData
    {
        QVector3D pos;
        QVector3D normal;
        QVector3D color;
    };

public:
    MvOpenGLWidget(QWidget* parent = 0);

private:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int h) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    QVector3D calcDirVec() const;

private:
    QOpenGLShaderProgram* program;
    QOpenGLBuffer* vertexBuff;
    QOpenGLBuffer* indexBuff;
    /* 下面2个是相机方向,都为0时相机朝向X轴正方向 */
    float yaw; /* 绕Y轴旋转 */
    float pitch; /* 绕X轴旋转 */
    /* 下面1个是鼠标上次点击的位置 */
    QPoint lastPos;
};

CPP文件:

MvOpenGLWidget::MvOpenGLWidget(QWidget* parent) :
    QOpenGLWidget(parent), lastPos(0xDEADBEEFi32, 0xDEADBEEFi32)
{
    yaw = -90.0f;
    pitch = 0;

    QSurfaceFormat surface;
    surface.setSamples(4);
    setFormat(surface);
}

void MvOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);

    program = new QOpenGLShaderProgram(this);
    const char* vsrc = u8R"(
        #version 330 core
        layout (location = 0) in vec3 aPos; // 空间坐标
        layout (location = 1) in vec3 aNormal; // 法线向量
        layout (location = 2) in vec3 aColor; // 顶点颜色
        uniform mat4 modelMatrix;
        uniform mat4 mvpMatrix;
        out vec3 oNormal;
        out vec3 oFragPos;
        out vec3 oColor;

        void main()
        {
            oFragPos = vec3(modelMatrix * vec4(aPos, 1.0));
            oNormal =  mat3(transpose(inverse(modelMatrix))) * aNormal;
            oColor = aColor;
            gl_Position = mvpMatrix * vec4(aPos, 1.0);
        })";
    program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc);
    const char* fsrc = u8R"(
        #version 330 core
        uniform vec3 lightPos; 
        uniform vec3 viewPos; 
        uniform vec3 lightColor;
        in vec3 oNormal;
        in vec3 oFragPos;
        in vec3 oColor;

        void main()
        {
            // 环境光照
            float ambientStrength = 0.4;
            vec3 ambient = ambientStrength * lightColor;
            // 漫反射光照 
            vec3 norm = normalize(oNormal);
            vec3 lightDir = normalize(lightPos - oFragPos);
            float diff = max(dot(norm, lightDir), 0.0);
            vec3 diffuse = diff * lightColor;
            // 镜面光照
            float specularStrength = 1.0;
            vec3 viewDir = normalize(viewPos - oFragPos);
            vec3 reflectDir = reflect(-lightDir, norm);
            float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
            vec3 specular = specularStrength * spec * lightColor;
            // 把纹理中的颜色和光照参数相乘
            gl_FragColor = vec4((ambient + diffuse + specular) * oColor, 1.0);
        })";
    program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc);

    program->link();
    program->bind();

    VertexData vertices[] =
    {
        { QVector3D(-0.433f, 0, -0.25f), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(1.0f, 0, 0) },
        { QVector3D(0, 0, 0.5f), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(0, 1.0f, 0) },
        { QVector3D(0, 0.707f, 0), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(0, 0, 1.0f) },

        { QVector3D(0, 0, 0.5f), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(1.0f, 1.0f, 0) },
        { QVector3D(0.433f, 0, -0.25f), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(1.0f, 0, 1.0f) },
        { QVector3D(0, 0.707f, 0), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(0, 1.0f, 1.0f) },

        { QVector3D(0.433f, 0, -0.25f), QVector3D(0, 0.333f, -0.943f), QVector3D(0, 0, 1.0f) },
        { QVector3D(-0.433f, 0, -0.25f), QVector3D(0, 0.333f, -0.943f), QVector3D(0, 1.0f, 0) },
        { QVector3D(0, 0.707f, 0), QVector3D(0, 0.333f, -0.943f), QVector3D(1.0f, 0, 0) },

        { QVector3D(0, 0, 0.5f), QVector3D(0, -1.0f, 0), QVector3D(0, 1.0f, 1.0f) },
        { QVector3D(-0.433f, 0, -0.25f), QVector3D(0, -1.0f, 0), QVector3D(1.0f, 1.0f, 0) },
        { QVector3D(0.433f, 0, -0.25f), QVector3D(0, -1.0f, 0), QVector3D(1.0f, 0, 1.0f) },

        /* 后面6个点绘制水平的地面 */
        { QVector3D(-10.0f, -0.2f, -10.0f), QVector3D(0, 1.0f, 0), QVector3D(0, 1.0f, 1.0f) },
        { QVector3D(-10.0f, -0.2f, 10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 1.0f, 0) },
        { QVector3D(10.0f, -0.2f, -10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 0, 1.0f) },

        { QVector3D(10.0f, -0.2f, -10.0f), QVector3D(0, 1.0f, 0), QVector3D(0, 1.0f, 1.0f) },
        { QVector3D(-10.0f, -0.2f, 10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 1.0f, 0) },
        { QVector3D(10.0f, -0.2f, 10.0f), QVector3D(0, 1.0f, 0), QVector3D(1.0f, 0, 1.0f) },
    };
    GLushort indices[] =
    {
        0, 1, 2, 3, 4, 5,
        6, 7, 8, 9, 10, 11,
        12, 13, 14, 15, 16, 17,
    };
    vertexBuff = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    vertexBuff->create();
    vertexBuff->bind();
    vertexBuff->allocate(vertices, 18 * sizeof(VertexData));
    indexBuff = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
    indexBuff->create();
    indexBuff->bind();
    indexBuff->allocate(indices, 18 * sizeof(float));

    int offset = 0;
    int vertexLocation = program->attributeLocation("aPos");
    program->enableAttributeArray(vertexLocation);
    program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
    offset += sizeof(QVector3D);
    int normalLocation = program->attributeLocation("aNormal");
    program->enableAttributeArray(normalLocation);
    program->setAttributeBuffer(normalLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
    offset += sizeof(QVector3D);
    int colorLocation = program->attributeLocation("aColor");
    program->enableAttributeArray(colorLocation);
    program->setAttributeBuffer(colorLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
}

#define LIGHT_COLOR QVector3D(0.9f, 1.0f, 2.0f) /* 这里的颜色值可以大于1不知道为什么 */
#define LIGHT_POS   QVector3D(2.5f, 5.8f, 2.0f)
#define VIEW_CENTER QVector3D(0.0f, 0.25f, 0.0f)

void MvOpenGLWidget::paintGL()
{
    QMatrix4x4 projection;
    projection.perspective(45.0f, 1, 0.1f, 100.0f);
    QMatrix4x4 viewMatrix;
    QVector3D eyeDir = calcDirVec();
    QVector3D eyePos = VIEW_CENTER - 10 * eyeDir;
    viewMatrix.lookAt(eyePos, VIEW_CENTER, QVector3D(0, 1, 0));
    QMatrix4x4 modelMatrix;
    modelMatrix.translate(0, -0.8, 0); // 平移
    modelMatrix.rotate(0, 0, 1); // 旋转
    modelMatrix.scale(4.0); // 缩放
    QMatrix4x4 mvpMatrix = projection * viewMatrix * modelMatrix;

    program->setUniformValue("lightColor", LIGHT_COLOR);
    program->setUniformValue("lightPos", LIGHT_POS);
    program->setUniformValue("viewPos", eyePos);
    program->setUniformValue("mvpMatrix", mvpMatrix);
    program->setUniformValue("modelMatrix", modelMatrix);
    glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_SHORT, 0);
}

#undef LIGHT_COLOR
#undef LIGHT_POS
#undef EYE_CENTER

void MvOpenGLWidget::resizeGL(int w, int h)
{
}

void MvOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
    lastPos = event->pos();
}

void MvOpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    lastPos = QPoint(0xDEADBEEFi32, 0xDEADBEEFi32);
}

void MvOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    float xoffset = event->pos().x() - lastPos.x();
    float yoffset = lastPos.y() - event->pos().y();
    lastPos = event->pos();

    float sensitivity = 0.25f;
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw += xoffset;
    pitch += yoffset;
    pitch = std::max(-89.0f, std::min(89.0f, pitch));

    update();
}

QVector3D MvOpenGLWidget::calcDirVec() const
{
    QVector3D front;
    front.setX(cosf(qDegreesToRadians(yaw)) * cosf(qDegreesToRadians(pitch)));
    front.setY(sinf(qDegreesToRadians(pitch)));
    front.setZ(sinf(qDegreesToRadians(yaw)) * cosf(qDegreesToRadians(pitch)));
    return front.normalized();
}