繁体   English   中英

如何使用 QOpenGLWidget 在 Qt6 中绘制三角形?

[英]How to draw triangles in Qt6 using QOpenGLWidget?

引用一个 7 岁的问题: 如何在 QOpenGLWidget 中渲染三角形?

此处接受的答案对如何设置示例进行了非常详细的解释,但是由于对该答案 state (几年后)的大量评论,示例中的某些部分已被弃用或不再是最佳实践。

任何人都可以在 Qt6+ 中解释如何在不使用 glBegin/glEnd 且不使用 GLU 的情况下执行此操作吗?

我最终需要能够围绕 OpenGL 上下文构建 GUI,OpenGL 能够将 3D 模型渲染为线框,无需任何类型的着色器或纹理映射到它。

我尝试从多维数据集示例开始工作。 我能够添加 GUI 元素,但它们呈现在 OpenGL window 之上,而不是在它之上或周围,我不确定如何更改代码来解决这个问题。 我能够从文件中输入 3D 几何图形并将其获取到 plot ,但是它将示例中的 cube.png 纹理映射到任何东西上质地。

编辑以添加示例图像和 C++ 代码:

试图达到: 在此处输入图像描述

Edit2:添加 GLPolygonMode 后(反映在下面的代码中) 在此处输入图像描述

主文件

#include <QApplication>
#include <QLabel>
#include <QSurfaceFormat>

#ifndef QT_NO_OPENGL
#include "mainwidget.h"
#endif
#include "geometryengine.h"
#include "storedGeometry.h"

extern "C" {
    // this fortran function is called by cpp
    void rk_viz_f90(const char *geoname, int str_len=0); // length is optional, default 0, pass by value

    // this cpp function is called by fortran
    void send_facet(float in[][3])
    {
        gUseGeom.addFacet(GeometryEngine::facetData(QVector3D(in[0][0],in[0][1],in[0][2]),QVector3D(in[1][0],in[1][1],in[1][2]),QVector3D(in[2][0],in[2][1],in[2][2])));
    }
}

int main(int argc, char *argv[])
{    
    QApplication app(argc, argv);

    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    QSurfaceFormat::setDefaultFormat(format);

    app.setApplicationName("cube");
    app.setApplicationVersion("0.1");


    // Call Fortran Rk_Viz Lib version
    std::string geofile = "C:\\TEMP\\qt\\demo_send_arrays\\sphere_6in_PW.RawRkViz.bin";
    printf("C++ filename %s\n",geofile.c_str());
    const char * geoname = geofile.c_str();
    rk_viz_f90(geoname,geofile.size());

#ifndef QT_NO_OPENGL
    MainWidget widget;
    widget.setFixedSize(600,800);
    widget.show();
#else
    QLabel note("OpenGL Support required");
    note.show();
#endif
    return app.exec();
}

mainwidget.h

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include "geometryengine.h"
#include "storedGeometry.h"

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QMatrix4x4>
#include <QQuaternion>
#include <QVector2D>
#include <QBasicTimer>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QPushButton>

class GeometryEngine;

class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    using QOpenGLWidget::QOpenGLWidget;
    MainWidget(QWidget *parent = nullptr);
    ~MainWidget();

protected:
    void mousePressEvent(QMouseEvent *e) override;
    void mouseReleaseEvent(QMouseEvent *e) override;
    void timerEvent(QTimerEvent *e) override;

    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;

    void initShaders();
    void initTextures();

private:
    std::vector<GeometryEngine::facetData> *pUseGeom = nullptr;

    QBasicTimer timer;
    QOpenGLShaderProgram program;
    GeometryEngine *geometries = nullptr;

    QOpenGLTexture *texture = nullptr;

    QMatrix4x4 projection;

    QVector2D mousePressPosition;
    QVector3D rotationAxis;
    qreal angularSpeed = 0;
    QQuaternion rotation;

    QPushButton *loadButton;
};

#endif // MAINWIDGET_H

mainwidget.cpp

#include "mainwidget.h"

#include <QMouseEvent>
#include <QGridLayout>

#include <cmath>

MainWidget::MainWidget(QWidget *parent)
{
    // Setup gui by using layout(s), which can be nested
    QGridLayout *layout = new QGridLayout;

    loadButton = new QPushButton(QString("Load Bin File..."),this);
    layout->addWidget(loadButton,0,0,1,1,Qt::AlignHCenter);

}

MainWidget::~MainWidget()
{
    // Make sure the context is current when deleting the texture
    // and the buffers.
    makeCurrent();
    delete texture;
    delete geometries;
    doneCurrent();
}

void MainWidget::mousePressEvent(QMouseEvent *e)
{
    // Save mouse press position
    mousePressPosition = QVector2D(e->position());
}

void MainWidget::mouseReleaseEvent(QMouseEvent *e)
{
    // Mouse release position - mouse press position
    QVector2D diff = QVector2D(e->position()) - mousePressPosition;

    // Rotation axis is perpendicular to the mouse position difference
    // vector
    QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();

    // Accelerate angular speed relative to the length of the mouse sweep
    qreal acc = diff.length() / 100.0;

    // Calculate new rotation axis as weighted sum
    rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();

    // Increase angular speed
    angularSpeed += acc;
}

void MainWidget::timerEvent(QTimerEvent *)
{
    // Decrease angular speed (friction)
    angularSpeed *= 0.99;

    // Stop rotation when speed goes below threshold
    if (angularSpeed < 0.01) {
        angularSpeed = 0.0;
    } else {
        // Update rotation
        rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;

        // Request an update
        update();
    }
}

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

    glClearColor(0, 0, 0, 1);

    initShaders();
    initTextures();

    // Enable depth buffer
    glEnable(GL_DEPTH_TEST);

    // Enable back face culling
    //glEnable(GL_CULL_FACE);

    geometries = new GeometryEngine();
    // Use QBasicTimer because its faster than QTimer
    timer.start(12, this);
}

void MainWidget::initShaders()
{
    // Compile vertex shader
    if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl"))
        close();

    // Compile fragment shader
    if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fshader.glsl"))
        close();

    // Link shader pipeline
    if (!program.link())
        close();

    // Bind shader pipeline for use
    if (!program.bind())
        close();
}

void MainWidget::initTextures()
{
    // Load cube.png image
    texture = new QOpenGLTexture(QImage(":/cube.png").mirrored());

    // Set nearest filtering mode for texture minification
    texture->setMinificationFilter(QOpenGLTexture::Nearest);

    // Set bilinear filtering mode for texture magnification
    texture->setMagnificationFilter(QOpenGLTexture::Linear);

    // Wrap texture coordinates by repeating
    // f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2)
    texture->setWrapMode(QOpenGLTexture::Repeat);
}

void MainWidget::resizeGL(int w, int h)
{
    // Calculate aspect ratio
    qreal aspect = qreal(w) / qreal(h ? h : 1);

    // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
    //const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
    const qreal zNear = 0.1, zFar = 10.0, fov = 30.0;

    // Reset projection
    projection.setToIdentity();

    // Set perspective projection
    projection.perspective(fov, aspect, zNear, zFar);
}

void MainWidget::paintGL()
{
    // Clear color and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    texture->bind();

    // Calculate model view transformation
    QMatrix4x4 matrix;
    matrix.translate(0.0, 0.0, -1);
    matrix.rotate(rotation);

    // Set modelview-projection matrix
    program.setUniformValue("mvp_matrix", projection * matrix);

    // Use texture unit 0 which contains cube.png
    program.setUniformValue("texture", 0);

    // Draw cube geometry
    geometries->drawCubeGeometry(&program);
}

几何引擎.h

#ifndef GEOMETRYENGINE_H
#define GEOMETRYENGINE_H

#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>

class GeometryEngine : protected QOpenGLFunctions
{
public:
    struct facetData
    {
        QVector3D v1;
        QVector3D v2;
        QVector3D v3;

        facetData() {

        };
        facetData(QVector3D iv1, QVector3D iv2, QVector3D iv3) {
            v1 = iv1;
            v2 = iv2;
            v3 = iv3;
        };
        ~facetData() {
            v1.~QVector3D();
            v2.~QVector3D();
            v3.~QVector3D();
        };
    };

    GeometryEngine();
    virtual ~GeometryEngine();

    void drawCubeGeometry(QOpenGLShaderProgram *program);

private:
    void initCubeGeometry();

    QOpenGLBuffer arrayBuf;
    QOpenGLBuffer indexBuf;
};

#endif // GEOMETRYENGINE_H

几何引擎.cpp

#include "geometryengine.h"
#include "storedGeometry.h"

#include <QVector2D>
#include <QVector3D>
#include <algorithm>

GeometryEngine::GeometryEngine()
    : indexBuf(QOpenGLBuffer::IndexBuffer)
{
    initializeOpenGLFunctions();

    // Generate 2 VBOs
    arrayBuf.create();
    indexBuf.create();

    // Initializes cube geometry and transfers it to VBOs
    initCubeGeometry();
}

GeometryEngine::~GeometryEngine()
{
    arrayBuf.destroy();
    indexBuf.destroy();
}

void GeometryEngine::initCubeGeometry()
{
    // Get a copy of the geometry to reference here
    std::vector<GeometryEngine::facetData> tGeom = gUseGeom.getGeom();
    // Convert vector to array
    GeometryEngine::facetData* aGeom = tGeom.data();

    // Get a copy of the generated indices to reference here
    std::vector<GLushort> tInd = gUseGeom.getGenIndices();
    // Convert vector to array
    GLushort* aInd = tInd.data();

    // Transfer vertex data to VBO 0
    arrayBuf.bind();
    arrayBuf.allocate(aGeom, tGeom.size() * sizeof(GeometryEngine::facetData));

    // Transfer index data to VBO 1
    indexBuf.bind();
    indexBuf.allocate(aInd, tInd.size() * sizeof(GLushort));
}

void GeometryEngine::drawCubeGeometry(QOpenGLShaderProgram *program)
{
    // Tell OpenGL which VBOs to use
    arrayBuf.bind();
    indexBuf.bind();

    // Tell OpenGL programmable pipeline how to locate vertex position data
    int vertexLocation = program->attributeLocation("a_position");
    program->enableAttributeArray(vertexLocation);
    // setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
    program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3);

    // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
    int texcoordLocation = program->attributeLocation("a_texcoord");
    program->enableAttributeArray(texcoordLocation);
    // original: program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData));
    program->setAttributeBuffer(texcoordLocation, GL_FLOAT, 0, 3);

    // Draw cube geometry using indices from VBO 1
    glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
    glDrawElements(GL_TRIANGLES, gUseGeom.gSize() * 3, GL_UNSIGNED_SHORT, nullptr);
}

存储几何.h

#ifndef STOREDGEOMETRY_H
#define STOREDGEOMETRY_H

#include "geometryengine.h"

class storedGeometry
{
private:
    std::vector<GeometryEngine::facetData> useGeom;
    std::vector<std::vector<GLushort>> useInd;
    std::vector<GLushort> genInd;

public:
    // Constructor/Destructor
    storedGeometry();
    ~storedGeometry();

    // Setters
    void setGeom(std::vector<GeometryEngine::facetData> inGeom);
    void addFacet(GeometryEngine::facetData inFacet);
    void setIndices(std::vector<std::vector<GLushort>> inInd);
    void addIndices(std::vector<GLushort> inInd);

    // Getters
    std::vector<GeometryEngine::facetData> getGeom();
    GeometryEngine::facetData getFacet(int pos);
    int gSize();
    int iSize();
    std::vector<std::vector<GLushort>> getUseIndices();
    std::vector<GLushort> getGenIndices();
    std::vector<GLushort> getInd(int pos);
};

extern storedGeometry gUseGeom;

#endif // STOREDGEOMETRY_H

存储几何.cpp

#include "storedGeometry.h"

// Constructor
storedGeometry::storedGeometry()
{
    std::vector<GeometryEngine::facetData> useGeom;
    std::vector<GLushort> useInd;
    std::vector<GLushort> genInd;
}

// Destructor
storedGeometry::~storedGeometry()
{
    useGeom.clear();
    useInd.clear();
    genInd.clear();
}

// Setters
void storedGeometry::setGeom(std::vector<GeometryEngine::facetData> inGeom) {
    useGeom = inGeom;
}

void storedGeometry::addFacet(GeometryEngine::facetData inFacet) {
    useGeom.push_back(inFacet);

    // also want to generate indices to go with this at the same time
    // can take in indices from rkviz, but are not useful for this purpose
    if (genInd.empty()) {
        // case 1 - currently no indices, add 0, 1, 2
        genInd.push_back(0);
        genInd.push_back(1);
        genInd.push_back(2);
    } else {
        // case 2 - already has indices, add n+1, n+1, n+2, n+3, n+3, where n is previous entry
        GLushort tInd = genInd[genInd.size()-1];
        genInd.push_back(tInd+1);
        genInd.push_back(tInd+2);
        genInd.push_back(tInd+3);
    }
}

void storedGeometry::setIndices(std::vector<std::vector<GLushort>> inInd) {
    useInd = inInd;
}

void storedGeometry::addIndices(std::vector<GLushort> inInd) {
    useInd.push_back(inInd);
}

// Getters
std::vector<GeometryEngine::facetData> storedGeometry::getGeom() {
    return useGeom;
}

GeometryEngine::facetData storedGeometry::getFacet(int pos) {
    if (pos <= useGeom.size()) {
        return useGeom[pos];
    } else {
        return useGeom[useGeom.size()];
    }
}

int storedGeometry::gSize() {
    return useGeom.size();
}

int storedGeometry::iSize() {
    return useInd.size();
}

std::vector<std::vector<GLushort>> storedGeometry::getUseIndices() {
    return useInd;
}

std::vector<GLushort> storedGeometry::getGenIndices() {
    return genInd;
}

std::vector<GLushort> storedGeometry::getInd(int pos) {
    if (pos <= useInd.size()) {
        return useInd[pos];
    } else {
        return useInd[useInd.size()];
    }
}

storedGeometry gUseGeom;

对于 GUI,不要为它们使用QOpenGLWidget 如果你这样做,它会自动在 OpenGL 东西之上呈现 GUI。 为了解决这个问题,添加一个包装器 class 扩展 QMainWindow 以放置 MainWidget 和 GUI。

对于线框,请尝试在调用glDrawElements之前放置此代码:

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM