首页 > 解决方案 > 如何使用 qopenglwidget 中的线将缓冲区中的一些顶点渲染为点,其余的顶点?

问题描述

我有一组顶点和法线存储在缓冲区中。我想将大部分顶点显示为点,但我想为剩余的几个顶点画线。这些都存储在一个向量中,点部分在前面,我知道缓冲区的位置,直到它们使用点显示。我也知道每个绘图任务的元素数量。我只使用一个 vao 和一个缓冲区对象来完成这项任务。

我使用以下代码初始化 GLWidget:

void GLWidget::initializeGL()
{
    connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::cleanup);

    initializeOpenGLFunctions();
    glClearColor(0, 0, 0, m_transparent ? 0 : 1);

    m_program = new QOpenGLShaderProgram;
    m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_core ? vertexShaderSourceCore : vertexShaderSource);
    m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, m_core ? fragmentShaderSourceCore : fragmentShaderSource);
    m_program->bindAttributeLocation("vertex", 0);
    m_program->bindAttributeLocation("normal", 1);
    m_program->link();

    m_program->bind();
    m_projMatrixLoc = m_program->uniformLocation("projMatrix");
    m_mvMatrixLoc = m_program->uniformLocation("mvMatrix");
    m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
    m_lightPosLoc = m_program->uniformLocation("lightPos");
    m_vao.create();
    QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
    m_obj.create();
    setupBuffer();
    setupVertexAttribs();
    m_camera.setToIdentity();

    QVector3D camPos = QVector3D(0.0, 0.0, 15.0);
    m_camera.translate(-camPos);

    QVector3D camTarget = QVector3D(0.0, 0.0, 0.0);
    QVector3D camDirection = QVector3D(camPos - camTarget).normalized();
    QVector3D worldUp = QVector3D(0.0, 1.0, 0.0);
    QVector3D camRight = QVector3D::crossProduct(worldUp, camDirection).normalized();
    QVector3D camUp = QVector3D::crossProduct(camDirection, camRight);

    m_camera.lookAt(camPos, camTarget, camUp);
    // Light position is fixed.
    m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 200));

    m_program->release();
}

函数 setupBuffer() setupVertexAtrribs() 执行与它们的名称相同的任务。顶点布局在缓冲区中,顶点的 xyz 位置后跟其关联法线的 xyz。它们的实现如下

void GLWidget::setupBuffer()
{
    m_obj.bind();
    m_obj.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat));
}

void GLWidget::setupVertexAttribs()
{
    m_obj.bind();
    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
    f->glEnableVertexAttribArray(0);
    f->glEnableVertexAttribArray(1);
    f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), reinterpret_cast<void *>(0));
    f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
    m_obj.release();
}

现在,QVector 顶点是传递给 opengl 的缓冲区。这个向量中的最后几个条目是需要使用 GL_LINES 绘制的顶点。

我的paintGL() 函数看起来像这样:

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glEnable(GL_POINT_SIZE);
    glEnable(GL_LINE_WIDTH);
    glPointSize(2);
    glLineWidth(10);

    m_world.setToIdentity();
    m_world.rotate(180.0f - (m_xRot / 16.0f), 1, 0, 0);
    m_world.rotate(m_yRot / 16.0f, 0, 1, 0);
    m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
    m_world.scale(m_dispScale);

    QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
    m_program->bind();
    m_program->setUniformValue(m_projMatrixLoc, m_proj);
    m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
    QMatrix3x3 normalMatrix = m_world.normalMatrix();
    m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);

    glDrawArrays(GL_POINTS, 0, vertices.size() - camVertices.size());
    // Draw camera frustums
    glDrawArrays(GL_LINES, vertices.size() - camVertices.size(), camVertices.size());
    //glDrawElements(GL_POINTS, vecIndices.size(), GL_UNSIGNED_INT, 0);

    m_program->release();
}

QVector camVertices 是另一个包含需要使用线条绘制的点的向量。camVertices 中的数据在渲染之前附加到向量“Vertices”的末尾。如上面的代码所示,我调用了 glDrawArrays 函数两次——第一次,从缓冲区的 0 索引开始,第二次,从上一次调用结束的位置开始显示剩余的点。

问题是这些点显示得很好。然而,第二次调用仅显示点,但不绘制任何线。

这是显示输出的屏幕截图的链接 - https://drive.google.com/open?id=1i7CjO1qkBALw78KKYGvBteydhfAWh3wh

图片显示了一个显示的示例,其中在顶部外围看到的亮绿色点(许多点的框)是要用线绘制的点。但是,我只看到点而不是任何线。

标签: c++qtopenglqt5qopenglwidget

解决方案


我做了一个简单的测试,我可以使用以下代码绘制点和线:

        glDrawArrays(GL_POINTS, 0, verticesCount() - 10);
        glDrawArrays(GL_LINES, 10, 10);

除了变量之外,这与您的并没有太大不同。我也在使用 1 VAO。所以绝对可以像我们预期的那样在点之后画线。

您可以尝试相同的方法吗(使用整数而不是变量)

你能在你的顶点上显示调试信息吗?

你能上传一个最小的可编译示例吗?


推荐阅读