1

我正在尝试使用 Qt 框架(4.7.4)来演示滑动显示,其中新的像素数据被添加到屏幕的第一行,并且之前的像素在每次刷新时滚动到下方一个像素。

它每秒刷新 20 次,每次刷新时,都会在黑色背景上绘制随机的绿点(像素)。

问题是; 每次刷新都有非常明显的闪烁。我已经通过网络进行了研究并尽可能优化了我的代码。我尝试将光栅渲染与 QPainter(在 QWidget 上)和 QGraphicsScene(在 QGraphicsView 上)一起使用,甚至我尝试在 QGLWidget 上使用 OpenGL 渲染。但是,最后我仍然有同样的闪烁问题。

什么可能导致这种闪烁?我开始怀疑我的 LCD 显示器无法刷新显示以实现黑色到绿色的过渡。我还注意到,如果我选择灰色背景而不是黑色,则不会发生闪烁。

4

2 回答 2

1

你看到的效果纯粹是心理视觉的。这是人为缺陷,不是软件缺陷。我是认真的。您可以通过固定值来验证x- 您仍将在窗口上重新绘制整个像素图,不会有任何闪烁 - 因为本身没有闪烁。

当滚动速率与实时的流逝无关时,就会发生心理视觉闪烁。当偶尔更新之间的时间因 CPU 负载或系统计时器不准确而发生变化时,我们的视觉系统会集成两个图像,并且看起来好像整体亮度发生了变化。

您已经正确地注意到,当您通过将背景设置为灰色来降低图像的对比度时,感知到的闪烁会减少。这是一个额外的线索,表明效果是心理视觉的。

下面是一种防止这种影响的方法。注意滚动距离与时间的关系(这里:1ms = 1pixel)。

#include <QElapsedTimer>
#include <QPaintEvent>
#include <QBasicTimer>
#include <QApplication>
#include <QPainter>
#include <QPixmap>
#include <QWidget>
#include <QDebug>

static inline int rand(int range) { return (double(qrand()) * range) / RAND_MAX; }

class Widget : public QWidget
{
    float fps;
    qint64 lastTime;
    QPixmap pixmap;
    QBasicTimer timer;
    QElapsedTimer elapsed;
    void timerEvent(QTimerEvent * ev) {
        if (ev->timerId() == timer.timerId()) update();
    }
    void paintEvent(QPaintEvent * ev) {
        qint64 time = elapsed.elapsed();
        qint64 delta = time - lastTime;
        lastTime = time;
        if (delta > 0) {
            const float weight(0.05);
            fps = (1.0-weight)*fps + weight*(1E3/delta);

            if (pixmap.size() != size()) {
                pixmap = QPixmap(size());
                pixmap.fill(Qt::black);
            }
            int dy = qMin((int)delta, pixmap.height());
            pixmap.scroll(0, dy, pixmap.rect());
            QPainter pp(&pixmap);
            pp.fillRect(0, 0, pixmap.width(), dy, Qt::black);
            for(int i = 0; i < 30; ++i){
                int x = rand(pixmap.width());
                pp.fillRect(x, 0, 3, dy, Qt::green);
            }
        }
        QPainter p(this);
        p.drawPixmap(ev->rect(), pixmap, ev->rect());
        p.setPen(Qt::yellow);
        p.fillRect(0, 0, 100, 50, Qt::black);
        p.drawText(rect(), QString("FPS: %1").arg(fps, 0, 'f', 0));
    }

public:
    explicit Widget(QWidget *parent = 0) : QWidget(parent), fps(0), lastTime(0), pixmap(size())
    {
        timer.start(1000/60, this);
        elapsed.start();
        setAttribute(Qt::WA_OpaquePaintEvent);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}
于 2013-09-10T17:24:44.947 回答
-1

我建议您不要在原地滚动像素图,而是创建第二个像素图并使用drawPixmap()将除一行之外的所有内容从像素图 1 复制到像素图 2(带有滚动偏移)。然后继续在像素图 2 上绘制。帧之后,交换对两个像素图的引用,然后重新开始。

基本原理是从一个内存区域复制到另一个内存区域可以比就地修改一个内存区域更容易优化。

于 2012-04-16T11:55:23.843 回答