You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用QOpenGLWidget实现OpenGL2.1管线时触发段错误

OpenGL 2.1 管线渲染问题排查

我正在尝试实现一个基于QOpenGLWidget的兼容OpenGL2.1管线最小可行示例,目标是在视图空间中创建一个平行于xy平面的平面(采用正交投影矩阵),并在片段着色器中使用顶点着色器的颜色输出变量。运行代码时,调用glDrawElements会触发段错误;改用glDrawArrays虽无段错误,但没有任何渲染结果。我并非OpenGL专家,对其配置流程不够熟悉,希望有人能指出问题所在。


CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(OpenGLExample)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

find_package(Qt6 REQUIRED COMPONENTS Widgets OpenGLWidgets)
set(CMAKE_AUTOMOC ON)

add_executable(MainApp main.cpp)
target_link_libraries(MainApp PRIVATE Qt6::Widgets Qt6::OpenGLWidgets)

main.cpp

#include <QApplication>
#include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <QVector3D>
#include <QOpenGLContext>

namespace {
    const QString vertexShader = ( "#version 120\n"
                                   "attribute vec3 pos;\n"
                                   "varying vec3 color;\n"
                                   "uniform mat4 mvp_mat;\n"
                                   "void main(){\n"
                                   "    gl_Position = mvp_mat * vec4(pos, 1);\n"
                                   "    color = (vec3(pos.x, pos.y, 0) + 1) / 2;\n"
                                   "}" );
    const QString fragmentShader = ( "#version 120\n"
                                     "varying vec3 color;\n"
                                     "void main() {\n"
                                     "    gl_FragColor = vec4(color, 1);\n"
                                     "}" );
}

class OpenGLTestWidget: public QOpenGLWidget {
    Q_OBJECT

public:
    OpenGLTestWidget(QWidget* parent = nullptr)
        : QOpenGLWidget(parent)
        , vertexData({QVector3D(-0.5f, -0.5f, -0.5f), 
                      QVector3D(0.5f, -0.5f, -0.5f), 
                      QVector3D(0.5f, 0.5f, -0.5f), 
                      QVector3D(-0.5f, 0.5f, -0.5f)})
    {
        modelMatrix.setToIdentity();
        viewMatrix.setToIdentity();
        projectionMatrix.setToIdentity();
        projectionMatrix.ortho(-1.0, 1.0, -1.0, 1.0, 0, 1);
    }

    virtual ~OpenGLTestWidget() {
        cleanup();
    }
protected:
    void initializeGL() override {
        vao.create(); // added in edit
        vao.bind(); // added in edit

        vbo.create();
        vbo.bind();
        vbo.allocate(vertexData.data(), sizeof(QVector3D) * vertexData.size());

        program = new QOpenGLShaderProgram(this);
        program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader);
        program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader);
        program->link();
        program->bind();

        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLTestWidget::cleanup);
    }

    void paintGL() override {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        const QMatrix4x4 mpvMatrix = projectionMatrix * viewMatrix * modelMatrix;
        program->setUniformValue("mpv_mat", mpvMatrix);

        const int vertexLocation = program->attributeLocation("pos");
        program->enableAttributeArray(vertexLocation);
        program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, sizeof(QVector3D));

        // glDrawElements(GL_TRIANGLE_FAN, vertexData.size(), GL_UNSIGNED_SHORT, nullptr); // <-- ERROR
        glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData.size()); // <-- no segfault but nothing renders
    }

private:
    QOpenGLVertexArrayObject vao; // added in edit
    QOpenGLBuffer vbo;
    QOpenGLShaderProgram* program;
    std::array<QVector3D, 4> vertexData;
    QMatrix4x4 modelMatrix;
    QMatrix4x4 viewMatrix;
    QMatrix4x4 projectionMatrix;

    void cleanup(){
        makeCurrent();
        delete program;
        vbo.destroy();
        vao.destroy(); // added in edit
        doneCurrent();
        disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLTestWidget::cleanup);
    }
};

#include "main.moc"

int main(int argc, char* argv[]) {
    QApplication a(argc, argv);
    OpenGLTestWidget test;
    test.show();
    return a.exec();
}

问题排查与修复方案

1. glDrawElements段错误原因

你没有创建并绑定索引缓冲对象(EBO),glDrawElements需要从索引缓冲区读取顶点索引数据,而代码中没有初始化任何索引数据,直接调用会导致访问非法内存,触发段错误。如果要使用glDrawElements,需要添加EBO并填充索引数据,示例代码:

// 在initializeGL中添加EBO初始化
QOpenGLBuffer ebo(QOpenGLBuffer::IndexBuffer);
ebo.create();
ebo.bind();
// 定义四边形的索引(TRIANGLE_FAN对应4个顶点的索引是0,1,2,3)
unsigned short indices[] = {0,1,2,3};
ebo.allocate(indices, sizeof(indices));

2. glDrawArrays无渲染结果的核心问题

问题一:矩阵名称拼写错误

顶点着色器中uniform变量名为mvp_mat,但你在paintGL中设置的是mpv_mat,变量名不匹配导致MVP矩阵没有正确传递给着色器,顶点位置计算错误。修正为:

program->setUniformValue("mvp_mat", mpvMatrix);

问题二:深度裁剪范围冲突

你的顶点z坐标是-0.5f,但正交投影的近平面设置为0,OpenGL会裁剪近平面之前的顶点,导致所有顶点被裁掉。调整投影矩阵的近平面范围:

projectionMatrix.ortho(-1.0, 1.0, -1.0, 1.0, -1, 1);

问题三:VAO绑定与属性配置时机

OpenGL 2.1中VAO会记录顶点属性的启用状态和VBO绑定关系,建议将属性配置移到initializeGL中,避免每次paintGL重复设置:

// 在initializeGL的program->bind()之后添加
const int vertexLocation = program->attributeLocation("pos");
program->enableAttributeArray(vertexLocation);
program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, sizeof(QVector3D));

修复后的关键代码片段

构造函数中的投影矩阵修正

projectionMatrix.ortho(-1.0, 1.0, -1.0, 1.0, -1, 1);

修正后的paintGL

void paintGL() override {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    program->bind();
    vao.bind(); // 绑定VAO,自动恢复顶点属性配置

    const QMatrix4x4 mvpMatrix = projectionMatrix * viewMatrix * modelMatrix;
    program->setUniformValue("mvp_mat", mvpMatrix);

    glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData.size());
}

内容的提问来源于stack exchange,提问作者RobinW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 01:50:48