Qt 5.5 和 OpenGL:尝试调用 paintGL() 时引发读取访问冲突异常

Posted

技术标签:

【中文标题】Qt 5.5 和 OpenGL:尝试调用 paintGL() 时引发读取访问冲突异常【英文标题】:Qt 5.5 and OpenGL: read access violation exception raised when trying to call paintGL() 【发布时间】:2015-08-29 15:11:21 【问题描述】:

我正在编写一个简单的应用程序,它使用 Qt 5.5 和 OpenGL 在 QtOpenGLWidget 中绘制一些非常基本的几何图形。最初,大部分顶点数据初始化和渲染代码都取自一个旨在绘制单个三角形的教程。这个版本工作正常,但我自己尝试修改它以促进 4 个顶点形成一个正方形,但没有成功。程序编译没有错误,但在执行时甚至没有将ui绘制到屏幕上就崩溃了,并出现以下错误:

error: Exception at 0x53fe35, code: 0xc0000005: read access violation at: 0x0, flags=0x0 (first chance)

我尽可能地使用 Qt 的 OpenGL 包装器。

我在this 帖子中找到的第一个潜在(和可能)解决方案;有问题的问题具有相同的错误签名,并且对问题原因的描述,即错误启用或配置 VAO 对我来说似乎也很重要,考虑到向 VBO 添加另一个顶点并且我可能无能为力正确重新配置 glVertexAttribPointer。但是,我无法在我的代码中找到问题。

GLWidget(相关的 OpenGL 小部件)声明:

class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions 
    Q_OBJECT

    void prepareVertexBuffers();

    void prepareVertexShader();
    void prepareFragmentShader();
    void prepareShaderProgram();

    void applyShaderPrograms();

    QOpenGLBuffer *vertBuffer;
    QOpenGLBuffer *elementBuffer;
    QOpenGLVertexArrayObject *vao;

    QOpenGLShader *vertShader;
    QOpenGLShader *fragShader;
    QOpenGLShaderProgram *shaderProgram;

public:
    GLWidget(QWidget *parent = 0);

    void initializeGL();
    void resizeGL();
    void paintGL();

signals:
    void shaderCompiled(const QString compilationLog);

public slots:
    void updateFragmentShader(QString newsource);
;

prepareVertexBuffers() 函数——这里我定义了 4 个顶点,并将它们排列成 2 个三角形,形成一个正方形。

void GLWidget::prepareVertexBuffers() 

    static const GLfloat vertData[] = 
       -1.0f, 1.0f, 0.0f,
       -1.0f, -1.0f, 0.0f,
       1.0f,  -1.0f, 0.0f,
       1.0f,  1.0f, 0.0f,
    ;

    static const GLushort elementData[] = 1, 2, 3, 2, 3, 4;

    vao = new QOpenGLVertexArrayObject(this);
    vao->create();
    vao->bind();

    vertBuffer = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    vertBuffer->create();
    vertBuffer->allocate(vertData, 4 * 3 * sizeof(GLfloat) );
    vertBuffer->setUsagePattern( QOpenGLBuffer::StaticDraw );
    vertBuffer->bind();

    elementBuffer = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
    elementBuffer->create();
    elementBuffer->allocate(elementData, 6 * sizeof(GLushort) );
    elementBuffer->setUsagePattern( QOpenGLBuffer::StaticRead );
    elementBuffer->bind();

paintGL(),我怀疑问题出在:

void GLWidget::paintGL() 
    vao->bind();
    vertBuffer->bind();
    elementBuffer->bind();

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, (void*)0 );
    glEnableVertexAttribArray(0);

    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glDrawArrays(GL_TRIANGLES, 0, 4);
    glDisableVertexAttribArray(0);
    elementBuffer->release();
    vertBuffer->release();
    vao->release();

很明显,我几乎没有使用 OpenGL 的经验,因此,如果能提供有关如何正确构建我的代码和一般编码实践的线索,我将不胜感激。

【问题讨论】:

你的代码中有很多奇怪的东西:你的元素索引应该是从0开始的,元素缓冲区的使用提示StaticRead没有意义,你甚至不是完全使用该元素数组,您使用具有4个顶点计数的DrawArrays,它应该只生成一个具有前3个顶点的三角形,并忽略第4个,因为它不能构建一个只有一个顶点的三角形。但是,这些错误都不会引发崩溃。 你在哪里打电话prepareVertexBuffers?如果它在没有 OpenGL 上下文可用和绑定的情况下发生,它将什么也不做。关于 Qt,使用 OpenGL 函数的唯一有效位置是在 …GL 方法(initializeGLpaintGLresizeGL)或手动使用 makeCurrentdoneCurrent 时。 【参考方案1】:

感谢 derhass 的澄清。我盲目地假设 glDrawArrays 神奇地使用当前绑定的元素缓冲区进行绘制——显然事实并非如此。我换行了

glDrawArrays(GL_TRIANGLES, 0, 4);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);

并且程序在启动时不再崩溃。

此外,我确保在根据文档为缓冲区分配内存之前绑定()缓冲区。

【讨论】:

但这仍然没有意义。 glDrawArrays 调用可能不是您想要的,但它不应该在这个星座中崩溃。 @derhass:星座? @MaxB:这仍然不对,您在 elementBuffer 的位置传递的东西应该是该缓冲区地址空间的整数偏移量 - 在其内存中找到第一个元素的位置。您似乎正在传递一个指向封装该缓冲区的对象的指针(API 允许这样做,因为它需要一个GLvoid*);这是允许的,但这样做毫无意义。 @Andon:我查看了opengl.org/sdk/docs/man/html/glDrawElements.xhtml 的文档,我很困惑——最后一个参数是 GLvoid * 索引——这是指向内存的实际指针还是内部“整数”openGL 指针?是这样,我应该传递 elementBuffer 的 bufferID() 的返回值吗? @MaxB:当非零值绑定到GL_ELEMENT_ARRAY(这是elementBuffer->bind (...) 为您所做的;->release (...) 绑定0),该值在此函数末尾传递的内容被解释为绑定数组内存(服务器端/驱动程序管理的内存)的偏移量。如果 0 已绑定,则它是指向客户端(您的应用程序)内存的指针。 这都归功于缓冲区对象是 OpenGL API 中事后才想到的 - 当引入顶点缓冲区时,它们修改了现有 API 函数的行为。 为地址传递 0,很有可能。 @Andon:感谢您花时间解释这一点,我把它弄得太复杂了。我想我现在明白了 - glDrawElements() 中的最后一个参数只是从开始读取当前绑定的元素数组的位置的偏移量。

以上是关于Qt 5.5 和 OpenGL:尝试调用 paintGL() 时引发读取访问冲突异常的主要内容,如果未能解决你的问题,请参考以下文章

Qt 5.5 和 OpenGL:检索设备信息

如何在qt中执行opengl

使用 OpenGL 和 Qt 进行科学可视化

将纹理添加到 QT OpenGL 场景图

在 Qt 中创建原始 GL 上下文?

qt 3d 和opengl哪个好