QT多線程渲染Mandelbrot集

QT多線程渲染 Mandelbrot集

Mandelbrot集是人類有史以來做出的最奇異,最瑰麗的幾何圖形,曾被稱爲“上帝的指紋”。
這個點集均出自公式:Zn+1=Zn2+CZ_{n+1} = Z_n^2 +C,對於非線性迭代公式Zn+1=Zn2+CZ_{n+1} = Z_n^2 +C
所有使得無限迭代後的結果能保持有限數值的複數C的集合,構成Mandelbrot集.

在這裏插入圖片描述

公式: Zn+1=Zn2+CZ_{n+1} = Z_n^2 +C

複數 Z = a + bi

  • 式中 Zn+1Z_{n+1} 是複數 Z = a + bi 的第n+1次迭代,
  • C是確定該點在複平面中位置的複數值.
  • z的初始值爲0.迭代將一直進行下去,直到z的幅值大於2或者迭代次數已經達到某種任意規定的限度.

分析 :

MandelbrotWidget

主線程,負責繪製Mandelbrot圖像(渲染線程RenderThread傳遞過來的Mandelbrot圖像引用)

RenderThread

渲染線程,負責迭代計算生成Mandelbrot圖像,完成時傳遞參數給主線程繪製

  • 外部調用,啓動渲染render:
/*!
 * \brief 外部調用,啓動渲染,傳入渲染參數(QMutexLocker保護)
 * \param centerX       中心x
 * \param centerY       中心y
 * \param scaleFactor   縮放比例
 * \param resultSize    結果大小
 */
void RenderThread::render(double centerX, double centerY, double scaleFactor,QSize resultSize)
{
    QMutexLocker locker(&mutex);

    this->centerX = centerX;
    this->centerY = centerY;
    this->scaleFactor = scaleFactor;
    this->resultSize = resultSize;

    if (!isRunning()) {//--啓動
        start(LowPriority);
    } else {//--重啓喚醒休眠的渲染線程
        restart = true;
        condition.wakeOne();
    }
}
  • 線程函數run:

void RenderThread::run()
{
    forever {

        //--mutex保護訪問臨界區資源
        //--傳入參數
        mutex.lock();
        QSize resultSize = this->resultSize;
        double scaleFactor = this->scaleFactor;
        double centerX = this->centerX;
        double centerY = this->centerY;
        mutex.unlock();



        int halfWidth = resultSize.width() / 2;

        int halfHeight = resultSize.height() / 2;

        //--QImage::Format_RGB32 存儲使用32位RGB格式的圖像(0xffrrggbb)透明度a最大
        QImage image(resultSize, QImage::Format_RGB32);

        ///-此數越大 圖像精細度越高
        const int NumPasses = 8;
        int pass = 0;

        while (pass < NumPasses) {

            /// \brief 最大迭代
            const int MaxIterations = (1 << (2 * pass + 6)) + 32;

            /// \brief 幅值 Limit = 2^2 = 4
            const int Limit = 4;

            bool allBlack = true;


             //--逐行掃描,生成圖像
            for (int y = -halfHeight; y < halfHeight; ++y) {

                //--重啓
                if (restart)
                    break;

                //--終止
                if (abort)
                    return;

                //--讀取當前行第一個像素
                uint *scanLine =
                        reinterpret_cast<uint *>(image.scanLine(y + halfHeight));

                ///--虛部
                double ay = centerY + (y * scaleFactor);

                //--每行逐個掃描像素
                //--單個像素滿足複平面函數關係Mandelbrot 集
                for (int x = -halfWidth; x < halfWidth; ++x) {

                    ///--實部
                    double ax = centerX + (x * scaleFactor);

                    double a1 = ax;
                    double b1 = ay;

                    /// \brief 當前迭代次數
                    int numIterations = 0;


                    //--單個像素點處,迭代計算
                    //--Mandelbrot 集:
                    // Z(n+1)=(Zn)^2+C
                    // 複數Z = a + bi
                    // 連續計算MaxIterations次
                    //--求得滿足Mandelbrot集合的 第MaxIterations 個點(直到z的幅值大於2)
                    do {

                        //--增加迭代次數
                        ++numIterations;

                        //-- Z(n+1)=(Zn)^2+C
                        //-- 複數Z = a + bi

                        //---迭代一次-----
                        //--實部
                        double a2 = (a1 * a1) - (b1 * b1) + ax;
                        //--虛部
                        double b2 = (2 * a1 * b1) + ay;

                        //--直到z的幅值大於2, Limit = 2^2,退出
                        if ((a2 * a2) + (b2 * b2) > Limit)
                            break;

                        //---繼續迭代一次,記錄上一次結果,用於下一次迭代-----
                        ++numIterations;
                        a1 = (a2 * a2) - (b2 * b2) + ax;
                        b1 = (2 * a2 * b2) + ay;

                        //--直到z的幅值大於2, Limit = 2^2,退出
                        if ((a1 * a1) + (b1 * b1) > Limit)
                            break;

                    } while (numIterations < MaxIterations);//--循環迭代直到次數大於限制或者z的幅值大於2


                    //--退出循環時,幅值大於2,顏色取值
                    //--輪廓外部彩色
                    if (numIterations < MaxIterations) {

                        //--設置此行當前位(x位置) 像素顏色,取顏色空間的值
                        *scanLine++ = colormap[numIterations % ColormapSize];
                        allBlack = false;
                    }

                    //--退出循環時,迭代到最大次數,顏色取值
                    //--輪廓內部黑色
                    else {
                        //--設置此行當前位(x位置) 像素顏色
                        *scanLine++ = qRgb(0, 0, 0);
                    }

                }//--for每行逐個掃描
            }//--for逐行掃描

            //--第一輪,並且當前圖像全黑,從第5(pass=4)輪開始
            //--因爲這種情況下pass = 0 1 2 3 是全黑
            //--在輪廓內部
            if (allBlack && pass == 0) {
                pass = 4;
            } else {//--包含輪廓,觸發GUI線程繪製圖像
                if (!restart)
                    emit renderedImage(image, scaleFactor);
                ++pass;
            }
        }//--while

        //--渲染完畢休眠等待condition.wakeOne()喚醒
        mutex.lock();
        if (!restart)
            condition.wait(&mutex);
        restart = false;
        mutex.unlock();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章