Инициализировать QGLWidget с помощью glClearColor

У меня есть QMainWindow с QGLWidget внутри. Я хочу, чтобы виджет отображал «чистый» цвет по моему выбору вместо черного экрана по умолчанию.

void MyQGLWidget::initializeGL(void) {
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
}

void MyQGLWidget::paintGL(void) {       
     qDebug("Painting grey-ness");
     glClear(GL_COLOR_BUFFER_BIT);

     // ... Do more stuff, but only after a certain event has happened ...
}

Это работает, и я заметил, что метод вызывается 4 раза во время запуска. Однако я хочу закрасить его пустым только один раз в методе paintGL(), потому что этот метод вызывается очень часто после запуска, и на самом деле происходит небольшая, но значительная потеря производительности, если буферы очищаются при каждом вызове.

Поэтому я изменил код на это:

void MyQGLWidget::paintGL(void) {
    static bool screenOnBlank = false;
    if (!screenOnBlank) {
         qDebug("Painting grey-ness");
         glClear(GL_COLOR_BUFFER_BIT);
         screenOnBlank = true;
     }

    // ... Do more stuff, but only after a certain event has happened ...
}

Теперь очистка делается только один раз, но у меня остается черный экран, а не серый экран glClearColor. Почему это? Почему QGLWidget снова окрашивает мой экран в черный цвет? И, что более важно, как я могу убедиться, что экран имеет мой собственный цвет по умолчанию, не перекрашивая его пустым на каждом временном шаге?


person Yellow    schedule 25.06.2013    source источник


Ответы (1)


Короткий ответ:

На большинстве платформ состояние вашего резервного буфера не определено после выполнения замены буфера. Вы можете найти более подробную информацию здесь. . Следовательно, вы не можете полагаться на то, что поведение вашего буфера останется таким, каким вы его оставили до операций подкачки. Затем, чтобы убедиться, что ваша программа является кросс-платформенной, у вас нет другого выбора, кроме как вызывать glClear() при каждом рисовании.

На практике:

Возможно, ваша платформа/конфигурация гарантирует, что ваш буфер не изменится, или не гарантирует этого, но на практике это все еще имеет место. Если вы знаете, что находитесь в этих случаях после экспериментов (см. ниже), но ваш экран по-прежнему становится черным, это означает, что каким-то образом в вашем коде вы сделали что-то «неправильное», что заставляет Qt явно вызывать glClear() со своим собственным glClearColor().

Вы можете использовать этот код, чтобы увидеть, остается ли буфер неизменным после swapBuffers(). На самом деле это так в моей конфигурации: Qt 4.8 на 64-битном Linux:

// -------- main.cpp --------

#include <QApplication>
#include "GLWidget.h"

int main(int argc, char ** argv)
{
    QApplication app(argc, argv);
    GLWidget * w = new GLWidget();
    w->show();
    return app.exec();
}


// -------- GLWidget.h --------

#include <QGLWidget>
#include <QTimer>


class GLWidget: public QGLWidget
{
    Q_OBJECT

public:
    GLWidget() : i(0), N(60)
    {
        timer.setInterval(16);
        connect(&timer, SIGNAL(timeout()), 
              this, SLOT(animationLoop()));
        timer.start();
    }


protected:
     void initializeGL()
     {
         glClearColor(1.0, 0.0, 0.0, 1.0);
     }

     void resizeGL(int w, int h)
     {
         glViewport(0, 0, (GLint)w, (GLint)h);
     }

     void paintGL()
     {
         bool test = false;
         if(i<N)
         {
             if(test)
                 glClearColor(1.0, 0.0, 0.0, 1.0);
             glClear(GL_COLOR_BUFFER_BIT);
         }
         else
         {
             if(test)
             {
                 glClearColor(0.0, 1.0, 0.0, 1.0);
                 glClear(GL_COLOR_BUFFER_BIT);
             }
         }
     }

private slots:
    void animationLoop() 
    {
         i++;
         if(i>2*N) i=0;
         updateGL();
    }

private:
    int i, N;
    QTimer timer;
};
  1. если test == true, то я всегда вызываю glClearColor(), за которым следует glClear(), но чередуя красный цвет и зеленый цвет каждую секунду. Я действительно вижу, как цвет переключается между красным и зеленым.

  2. если test == false, то glClearColor() я вызываю только раз и навсегда в initializeGL(). Затем в paintGL я попеременно вызываю glClear() или не вызываю его. Экран остается красным, т.е. никогда не становится черным (и не отображает единорога), даже если glClear() не вызывается.

Следовательно, что касается вашей проблемы:

  1. Либо ваша конфигурация отличается от моей (реализация swapBuffers предоставляется Qt и различается в зависимости от базовой оконной системы)

  2. Или ваш код не работает.

Простой способ проверить: скомпилируйте мой код и посмотрите, воспроизводит ли он проблему. Если да, то вы находитесь в случае 1., и вы ничего не можете с этим поделать (можно считать багом Qt, скорее несоответствием, так как в документации нигде не указано правильное поведение). В противном случае вы находитесь в случае 2., и тогда вы должны предоставить больше своего кода, чтобы мы могли определить, в чем проблема.

person Boris Dalstein    schedule 25.06.2013
comment
Хорошее предложение, но, к сожалению, неверное. Я пробовал, но это не имеет значения. - person Yellow; 25.06.2013
comment
А что, если вы попробуете setAutofillBackground(false) в конструкторе вашего QGLWidget? Если это не сработает, у вас нет другого выбора, кроме как очищать буфер каждые paintGL. Обратите внимание, что падение производительности не должно быть проблемой, это то, что делает любая высокооптимизированная игра. ;-) - person Boris Dalstein; 25.06.2013
comment
Боюсь, это тоже не работает. Я знаю, что даже в оптимизированных играх вызывается функция glClear, но у меня в фоновом режиме выполняются тяжелые симуляции, поэтому любое небольшое снижение накладных расходов того стоит. - person Yellow; 25.06.2013
comment
И я думаю, более философский вопрос Почему это не работает? остается в любом случае. ;-) - person Yellow; 25.06.2013
comment
@Jelle: я думаю, что OpenGL не гарантирует, что задний буфер останется неизменным после вызова swapBuffer(). Следовательно, даже если это могло бы работать в вашей конфигурации, вы не можете полагаться на такое поведение. Следовательно, ответ таков: либо Qt сам выполняет очистку черным цветом, либо просто ваша конфигурация не сохраняет предыдущие пиксели. - person Boris Dalstein; 25.06.2013
comment
Наконец, если ваша симуляция выполняется на ЦП, эта операция не требует затрат, поскольку она выполняется асинхронно в ГП, на самом деле эта команда просто передает несколько битов в ГП и позволяет ему выполнять свою работу. В любом случае, не думали ли вы реже вызывать paintGL? Для меня нет смысла не очищать, если вы рисуете что-то после. Выполняете ли вы операции в paintGL, которые не связаны с GPU? - person Boris Dalstein; 25.06.2013
comment
Спасибо за предложения! Я думаю, вы правы: я ошибался насчет разницы в производительности. Были небольшие изменения, но результат был не очень последовательным. Но, как сказано, основной вопрос остается. Я также пробовал следующее: glClear(GL_COLOR_BUFFER_BIT); swapBuffers(); glClear(GL_COLOR_BUFFER_BIT); чтобы оба буфера были серыми, но безрезультатно. Мне просто кажется странным, что Qt помещает экран в пустой (т.е. черный) цвет даже после того, как первый paintGL() уже был вызван. - person Yellow; 26.06.2013
comment
@Jelle Поскольку я все еще был очень удивлен, что вы получаете такое поведение, я внимательно проверил документ и пришел к выводу, что нигде не указано, что ваши буферы не будут стерты Qt, поэтому вы не можете полагаться на это поведения и должен вызывать glClear(). Тем не менее, я действительно просмотрел исходный код Qt, и у вас не должно быть такого поведения. Поэтому я закодировал тест, чтобы увидеть, как он ведет себя для меня, и я не могу воспроизвести вашу проблему. Посмотрите мое редактирование и выполните то, что написано в моем последнем абзаце, чтобы сузить основную проблему. - person Boris Dalstein; 26.06.2013
comment
@Jelle Смотрите мой отредактированный пост. Я даю фактический окончательный ответ на ваш вопрос. Я имею в виду, даже философский. - person Boris Dalstein; 27.06.2013