QT多线程渲染Mandelbrot集
·
QT多线程渲染 Mandelbrot集
Mandelbrot集是人类有史以来做出的最奇异,最瑰丽的几何图形,曾被称为“上帝的指纹”。
这个点集均出自公式:Zn+1=Zn2+CZ_{n+1} = Z_n^2 +CZn+1=Zn2+C,对于非线性迭代公式Zn+1=Zn2+CZ_{n+1} = Z_n^2 +CZn+1=Zn2+C,
所有使得无限迭代后的结果能保持有限数值的复数C的集合,构成Mandelbrot集.

公式: Zn+1=Zn2+CZ_{n+1} = Z_n^2 +CZn+1=Zn2+C
复数 Z = a + bi
- 式中 Zn+1Z_{n+1}Zn+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();
}
}
更多推荐



所有评论(0)