使用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
相关产品推荐
相关产品推荐

