【问题标题】:Need help doing simple rendering with Qt5 Qml + OpenGL需要帮助使用 Qt5 Qml + OpenGL 进行简单渲染
【发布时间】:2014-07-22 18:10:26
【问题描述】:

我需要使它的一部分 Qml 视图被一些非 Qt OpenGL 渲染“接管”,并且我在获取纹理以正确显示时遇到问题,所以我想我只需画一条线并得到它在继续编写更复杂的代码之前工作。

对于那些不熟悉 Qt5 的人,整个窗口都是使用 OpenGL 绘制的,我使用 QQuickWindow::beforeRendering() 信号连接到 Qt 的 OpenGL 绘制机制,这意味着我的绘制代码在每次重绘(每次垂直同步)。

我采用了 Squircle 示例代码 (http://qt-project.org/doc/qt-5/qtquick-scenegraph-openglunderqml-example.html) 并对其稍作修改,使其可以在屏幕的指定部分(而不是整个屏幕)进行绘制,并且运行良好。然后,我只修改了 renderer::paint() 函数,最初绘制了三条绿线,2 秒后改为绘制一条蓝线:

void CtRenderer::paint()
{
static int n = 0;

if (n == 0)
{
    glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

    glEnable(GL_SCISSOR_TEST);
    glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);

    // glClearColor(1, 0, 0, 1);
    // glClear(GL_COLOR_BUFFER_BIT);

    glLineWidth(10);
    glColor4f(0.0, 1.0, 0.0, 1);
    glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 1);        glVertex3f(1, 0.5, 1);
    glVertex3f(0.0, 0.0, 1);        glVertex3f(0.5, 0.5, 1);
    glVertex3f(0.0, 0.0, 1);        glVertex3f(0.5, 1, 1);
    glEnd();
}
else if (n == 120)
{
    glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

    glEnable(GL_SCISSOR_TEST);
    glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);

    // glClearColor(1, 0, 0, 1);
    // glClear(GL_COLOR_BUFFER_BIT);

    glLineWidth(10);
    glColor4f(0.0, 0.0, 1.0, 1);
    glBegin(GL_LINES);
    glVertex3f(0.0, 0.0, 1);        glVertex3f(0.4, 0.8, 1);
    glEnd();

}
n++;

return;

}`

我得到的是连续闪烁的三条灰色线,并且永远不会变成一条线。经过一些在线研究,我认为也许我不应该使用 glBegin()/glEnd() 所以我更改了代码:

void CtRenderer::paint()
{
static bool bOnce = true;

if (bOnce)
{
    glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

    glEnable(GL_SCISSOR_TEST);
    glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

    float vertices[] = {-0.5f, -0.5f, 0.5f, 0.5f};

    glLineWidth(10);
    glColor4f(0.0, 1.0, 0.0, 1);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, GL_FLOAT, 0, vertices);

    glDrawArrays(GL_LINES, 0, 2);

    glDisableClientState(GL_VERTEX_ARRAY);

    bOnce = false;
}

return;
}

这仍然给了我一条闪烁的灰线。

当我在 Qt 之外的一个简单的 GLUT 应用程序中尝试这段代码时,它工作得很好,所以它似乎是 OpenGL 和 Qt5 Qml 之间的一些交互。接下来我可以尝试什么?

附言我在 Linux Ubuntu 机器上使用 Qt 5.3 版

p.p.s.为了响应一些 cmets,我将代码更新为如下所示:

void dumpGlErrors(int iLine)
{
for (;;)
{
    GLenum err = glGetError();
    if (err == GL_NO_ERROR)
        return;
    std::cout << "GL error " << err << " detected in line " << iLine << std::endl;
}
}

void CtRenderer::paint()
{
glPushMatrix();

glLoadIdentity();

glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

glEnable(GL_SCISSOR_TEST);
glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

dumpGlErrors(__LINE__);

glClearColor(1, 0, 1, 1);       // Magenta
glClear(GL_COLOR_BUFFER_BIT);

bool bDepth = glIsEnabled(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);

bool bLighting = glIsEnabled(GL_LIGHTING);
glDisable(GL_LIGHTING);

bool bTexture = glIsEnabled(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_2D);

dumpGlErrors(__LINE__);

float vertices[] = { -0.5f, -0.5f, 0.1f,  0.5f, 0.5f, 0.1f,  0.5f, 0.5f, 1.0f,  0.5f, -0.5f, 1.0f };
float colors[] = { 0.0f, 1.0f, 0.0f, 1.0f,  1.0f, 0.0f, 0.0f, 1.0f,  0.0f, 0.0f, 1.0f, 1.0f,  1.0f, 1.0f, 0.0f, 1.0f };

glLineWidth(10);
glColor4f(0.0, 0.0, 1.0, 1);

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);

glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, colors);

dumpGlErrors(__LINE__);

glDrawArrays(GL_LINES, 0, 4);       // 4, not 2.

dumpGlErrors(__LINE__);

// glFlush();

glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

if (bTexture)
    glEnable(GL_TEXTURE_2D);

if (bLighting)
    glEnable(GL_LIGHTING);

if (bDepth)
    glEnable(GL_DEPTH_TEST);

glPopMatrix();

dumpGlErrors(__LINE__);

// In case somebody else calls glClear()
glClearColor(0, 1, 1, 1);       // Cyan
}

现在我得到了一个漂亮的洋红色背景,我看到我的线条闪烁一次,然后它就消失了,我只剩下洋红色了。

p.p.p.s.我尝试使用 VBO 类型函数:

class Point
{
public:
float m_vertex[3];
float m_color[4];
};

void SquircleRenderer::drawBuffer()
{
QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());

if (!m_bBufInit)
{
    std::cout << "SquircleRenderer::drawBuffer()" << std::endl;
    // Adding these two lines doesn't change anything
    /*
    glFuncs.initializeOpenGLFunctions();
    glFuncs.glUseProgram(0);
    */

    // Create a new VBO
    glFuncs.glGenBuffers(1, &m_buf);

    // Make the new VBO active
    glFuncs.glBindBuffer(GL_ARRAY_BUFFER, m_buf);

    static const Point points[4] = {
        { { -0.5f, -0.5f, 0.1f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
        { {  0.5f,  0.5f, 0.1f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
        { {  0.5f,  0.5f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } },
        { {  0.5f, -0.5f, 1.0f }, { 1.0f, 1.0f, 0.0f, 1.0f } }
    };

    // Upload vertex data to the video device
    glFuncs.glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(Point), points, GL_STATIC_DRAW);

    dumpGlErrors(__LINE__);

    m_bBufInit = true;
}

glPushMatrix();

glLoadIdentity();

glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

glEnable(GL_SCISSOR_TEST);
glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);

glClearColor(1, 1, 0, 1);       // Yellow
glClear(GL_COLOR_BUFFER_BIT);

glFuncs.glBindBuffer(GL_ARRAY_BUFFER, m_buf);

dumpGlErrors(__LINE__);

/*
 * "If a non-zero named buffer object is bound to the GL_ARRAY_BUFFER target
 *  (see glBindBuffer) while a vertex array is
 *  specified, pointer is treated as a byte offset into the buffer object's data store"
 */

glLineWidth(10);
glColor4f(0.0, 0.0, 1.0, 1);

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(Point), 0);

glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, sizeof(Point), (void*)offsetof(Point, m_color));

dumpGlErrors(__LINE__);

glDrawArrays(GL_LINES, 0, 4);       // 4, not 2.

dumpGlErrors(__LINE__);

glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

// In case somebody else calls glClear()
glClearColor(0, 1, 1, 1);       // Cyan

glPopMatrix();

dumpGlErrors(__LINE__);
}

与我所做的所有其他努力一样,我得到了一个黄色背景,并且我的线条出现在我假设线条消失之前的 1/60 秒,我所拥有的只是我的黄色背景。

【问题讨论】:

  • 您可能缺少 Squircle 代码中的 glClear... 部分。
  • 如果我添加“glClearColor(0, 1, 0, 1);”和“glClear(GL_COLOR_BUFFER_BIT);”在 glScissor() 之后,除了闪烁的灰线之外,我还得到一个闪烁的绿色背景。奇怪的是,即使 glColor 的 alpha 为 1,绿色背景也是半透明的。
  • 它看起来是半透明的,因为你的眼睛跟不上。您的问题是由于您在有两个缓冲区(正面和背面)时所做的单一绘画。第一次绘画调用更新后台缓冲区,然后交换缓冲区,然后您调用绘画,但它什么也不做,因为您的 if 没有通过。缓冲区再次被交换,所以黑色缓冲区。然后重新交换,绿色+线。然后再次交换,依此类推。如果您将 b0unce 设置为 n int 等于 0 并且您的 if 检查低于 2,它应该可以工作。
  • 关于颜色,认为自己很幸运有一条白线(不是灰色,再一次,只是你的眼睛跟不上)。我认为glColor4f 不适用于glDrawArrays。试试glColorPointer。它作为glVertexPointer 工作,但我从未使用过它,固定管道对我来说是一个奇迹。
  • 感谢您的建议,我尝试将它们全部实现,如您所见(我编辑了我的主要帖子)结果有所不同但仍然不太正确。

标签: qt opengl qml qt5


【解决方案1】:

我找到了答案:我必须告诉 Qt 我正在使用旧的固定函数管道。在进行任何绘图之前调用一个函数就可以了:

QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
glFuncs.glUseProgram(0);

【讨论】:

  • "检查你使用的程序是0(默认固定管道)"。我确实要求你检查一下。我应该更清楚要调用什么函数。不要使用固定管道 -> []
  • 回顾讨论,我现在看到了!当时我不知道管道是什么,所以我无法对这些信息做太多事情。甚至我最终找到的答案也是偶然发现的......我发现了一些有效的代码,它在顶部有这个神奇的函数调用,我尝试了它并且它有效。但是你对我帮助很大,我很高兴给你赏金!
【解决方案2】:

使用 Qt Quick,您可以通过三种方式混合 OpenGL:

  1. 您可以(整个)QML 下绘制(参见http://qt-project.org/doc/qt-5/qtquick-scenegraph-openglunderqml-example.html
  2. 您可以在(整个)QML 上绘制
  3. 您可以提供在 QML 元素上绘制的 OpenGL 纹理(请参阅http://qt-project.org/doc/qt-5/qsgsimpletexturenode.html

或者,您可以构建一个 QWidget 应用程序并在一个 QWidget 窗口中使用 OpenGL,在另一个(QML 小部件窗口)中使用 QML。

【讨论】:

  • Squircle 代码(我的绘画所基于)使用 OpenGL Under QML 样式。我想在我的 OpenGL 上绘制 Qt 项目(如标签),所以#2 对我不利,最终我将有一个相当复杂的 OpenGL 渲染,我宁愿不转换为纹理并返回到 Open GL(GPU 和 CPU 之间交换过多)。所以我的问题不是我应该使用哪种混合OpenGL的方法,而是我怎样才能让这个特定的方法起作用。
  • 确实,我现在看到您特别提到您从 Squircle 下图示例开始。
  • 你有#2的例子(画在qml上)吗?我没有找到太多。我对此很感兴趣,因为我有一个 SceneGraph(也基于 Squircle 示例),然后是一个 Qt3D RenderSettings,其 ClearBuffer 正在擦除我的 SceneGraph,我不知道如何同时显示或放置我的场景图在 Qt3D 的场景图上。
  • 回复我之前的评论:我最初尝试这个时犯了一个错误,但事实证明它就像将连接从beforeRendering事件更改为afterRendering一样简单
【解决方案3】:
void SquircleRenderer::paint()
{
    if (!m_program) {
        m_program = new QOpenGLShaderProgram();
        m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
                                           "attribute highp vec4 aVertices;"
                                           "attribute highp vec4 aColors;"
                                           "varying highp vec4 vColors;"
                                           "void main() {"
                                           "    gl_Position = aVertices;"
                                           "    vColors= aColors;"
                                           "}");
        m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                           "varying highp vec4 vColors;"
                                           "void main() {"
                                           "    gl_FragColor = vColors;"
                                           "}");

        m_program->bindAttributeLocation("aVertices", 0);
        m_program->bindAttributeLocation("aColors", 1);
        m_program->link();

    }

    m_program->bind();

    m_program->enableAttributeArray(0);
    m_program->enableAttributeArray(1);

    float vertices[] = {
        -1, -1, //Diag bottom left to top right
        1, 1,
        -1, 1, //Diag top left to bottom right
        1, -1,
        -1, 0, //Horizontal line
        1, 0
    };
    float colors[] = {
        1, 1, 0, 1, 
        1, 0, 1, 1,
        0, 1, 1, 1,
        1, 0, 0, 1,
        0, 0, 1, 1,
        0, 1, 0, 1
    };
    m_program->setAttributeArray(0, GL_FLOAT, vertices, 2); //3rd to 0, 4th to 1 by default
    m_program->setAttributeArray(1, GL_FLOAT, colors, 4); 

    glViewport(0, 0, m_viewportSize.width(), m_viewportSize.height());

    glDisable(GL_DEPTH_TEST);

    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    //Here I draw 3 lines, reduce to 2 instead of 6 to draw only one.
    //Change second param to choose which line to draw
    glDrawArrays(GL_LINES, 0, 6);

    m_program->disableAttributeArray(0);
    m_program->disableAttributeArray(1);
    m_program->release();
}

我的结论是将旧版 OpenGL 留在历史书中。即使它是用于像这样的虚拟着色器,它也为您省去了管理任何矩阵的麻烦,并且您知道发生了什么。

PS : 该代码未经测试,只是对 Squircle 代码的调整。让我知道是否有问题,但我宁愿从这段代码继续故障排除会话。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-16
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 2021-05-10
    • 2021-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多