首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Qt6中使用QOpenGLWidget绘制三角形?

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

Stack Overflow用户
提问于 2022-08-09 15:44:48
回答 1查看 185关注 0票数 2

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

这里接受的答案给出了关于如何设置一个示例的非常详细的解释,但是随着对这个答案状态的大量评论(几年后),有部分示例被废弃或不再是最佳实践。

现在有人能解释如何在Qt6+中不使用glBegin/glEnd和不使用GLU来实现这一点吗?

我最终需要能够围绕OpenGL上下文构建一个图形用户界面,OpenGL能够将3D模型呈现为一个线框,而不需要映射到它上的任何类型的着色器或纹理。

我试着在立方体示例工作。我能够添加GUI元素,但是它们呈现在OpenGL窗口的顶部,而不是在上面或周围,我不知道如何修改代码来解决这个问题。我能够从文件中输入三维几何图形,并将其绘制成图形,但它将示例中的cube.png纹理映射到我所绘制的任何东西上,而且我还无法让它呈现一个线框而不是纹理。

编辑4:我想我现在就把这个问题解决了。参考这条线,我了解到除了中心小部件之外,还可以添加其他小部件,只是不是普通的小部件,它们必须是停靠小部件(据我所知)。我已经更新了下面的代码,以反映这个图像,这是一个‘工作’解决方案,我在这里提出的问题。非常感谢用户‘新QOpenGLWidget’的所有帮助!

​

main.cpp

代码语言:javascript
复制
#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
    MainWindow window;
    window.setFixedSize(600,800);
    window.show();
#else
    QLabel note("OpenGL Support required");
    note.show();
#endif
    return app.exec();
}

mainwindow.h -新添加

代码语言:javascript
复制
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "vizglwidget.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:


private:
    VizGlWidget *glWidget; // pointer to vizglwidget
    QPushButton *loadButton;
    void setupGui();
};

#endif // MAINWINDOW_H

mainwindow.cpp -新加入

代码语言:javascript
复制
#include "mainwindow.h"

#include <QGroupBox>
#include <QGridLayout>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    setWindowTitle("cube");
    setupGui();
}

MainWindow::~MainWindow()
{
}

void MainWindow::setupGui()
{
    // Try docking widgets with GL as central widget
    glWidget = new VizGlWidget();
    setCentralWidget(glWidget);

    setStatusBar(new QStatusBar(this));

    QDockWidget* dock1 = new QDockWidget;
    this->addDockWidget(Qt::TopDockWidgetArea, dock1);
    dock1->setMinimumSize(800,200);

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

vizglwidget.h -原为

代码语言:javascript
复制
#ifndef VIZGLWIDGET_H
#define VIZGLWIDGET_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 VizGlWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    using QOpenGLWidget::QOpenGLWidget;
    ~VizGlWidget();

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;
};

#endif // VIZGLWIDGET_H

vizglwidget.cpp -以前的mainwidget.cpp

代码语言:javascript
复制
#include "vizglwidget.h"

#include <QMouseEvent>

#include <cmath>

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

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

void VizGlWidget::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 VizGlWidget::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 VizGlWidget::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 VizGlWidget::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 VizGlWidget::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 VizGlWidget::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 VizGlWidget::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);
}

geometryengine.h

代码语言:javascript
复制
#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

geometryengine.cpp

代码语言:javascript
复制
#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);
}

storedgeometry.h

代码语言:javascript
复制
#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

storedgeometry.cpp

代码语言:javascript
复制
#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;

fshader.glsl

代码语言:javascript
复制
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif

// From example:
//uniform sampler2D texture;
//varying vec2 v_texcoord;

void main()
{
    // Set fragment color from texture
    //original: gl_FragColor = texture2D(texture, v_texcoord);

    // Set fragment color to fixed color
    gl_FragColor = vec4(1.0f,0.0f,0.0f,1.0f);
}

vshader.glsl

代码语言:javascript
复制
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif

uniform mat4 mvp_matrix;

attribute vec4 a_position;
attribute vec2 a_texcoord;

varying vec2 v_texcoord;

void main()
{
    // Calculate vertex position in screen space
    gl_Position = mvp_matrix * a_position;

    // Pass texture coordinate to fragment shader
    // Value will be automatically interpolated to fragments inside polygon faces
    v_texcoord = a_texcoord;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-09 17:54:26

对于GUI,不要对它们使用QOpenGLWidget。如果这样做,它将自动在OpenGL上呈现GUI,因为QOpenGLWidget强制OpenGL窗口出现在整个屏幕上。要解决这个问题,需要添加一个扩展QMainWindow的包装类,将MainWidget和GUI放在一起。

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

代码语言:javascript
复制
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

关于线框的更多澄清:

去掉纹理,用统一的颜色(如红色)替换它:

代码语言:javascript
复制
gl_FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73294606

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档