QT多線程渲染 Mandelbrot集
Mandelbrot集
是人類有史以來做出的最奇異,最瑰麗的幾何圖形,曾被稱爲“上帝的指紋”。
這個點集均出自公式:,對於非線性迭代公式,
所有使得無限迭代後的結果能保持有限數值的複數C的集合,構成Mandelbrot集
.
公式:
複數 Z = a + bi
- 式中 是複數 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();
}
}