1

我正在计算 mandelbrot 集,能够使用 OpenGL 将其缩放并打印到屏幕上。

如您所知,mandelbrot 集由一个矩形(右上角和左下角)定义,每次我“放大”和“缩小”时,我都会通过将左下点移动到右上角和右上角反之亦然。最终,这 2 个点应该会相遇并完成缩放过程。
根据许多 YOUTUBE 电影,我可以清楚地看到有些电影可以让您达到 1000 倍变焦。在我的程序中,我几乎无法达到 X6。在调试时,我可以看到我的 2 点,它们定义了 mandelbrot 集到达彼此(x1,y1) = (x2,y2)
几件事:
(x1,y1) & (x2,y2)被定义为浮点数。我应该使用 double 代替吗?我的 mandelbrot 由 (512X512) 点定义。够了吗?虽然我不确定它是否与问题有关。

我面临的另一个问题是 - 我将集合打印为高度图(3D 集合)。当每个 Y 分量代表某个点达到无穷大所需的迭代次数时。但是,每次我放大整个集合时,我的相机位置都会越来越高,最终我的相机被集合消耗掉了。有没有办法计算差异并将相机从集合中移开(相应地从缩放点移开?)

计算集合的代码:

double ratiox = instance->foundPointOnHost.x / ((instance->constVal[1][0] - instance->constVal[0][0]));;
            double ratioy = 1-instance->foundPointOnHost.z / ((instance->constVal[1][1] - instance->constVal[0][1]));;
            double xrange = instance->m_GraphConfig.xru-instance->m_GraphConfig.xld;
            double yrange = instance->m_GraphConfig.yru-instance->m_GraphConfig.yld;
            instance->m_GraphConfig.xld += 5*direction*0.005*ratiox*xrange;
            instance->m_GraphConfig.xru -= 5*direction*0.005*(1.-ratiox)*xrange;
            instance->m_GraphConfig.yld += 5*direction*0.005*(1.-ratioy)*yrange;
            instance->m_GraphConfig.yru -= 5*direction*0.005*ratioy*yrange;  

一些事情 :

instance->FoundPointOnHost = 我要放大的点。
instance->constVal = 包含集合的原始大小(开始等于 [xru,yru] [xld,yld])
(xru,yru) = 右上角集合的点 (xld,yld) = 集合的左下点

谢谢!

4

2 回答 2

1

是的,如果可以的话,你应该使用double而不是float.

浮点数只有 24 位尾数,当以精度限制的值反复乘以生成的额外位时,就会丢失。

FWIW,我的 WebGL mandelbrot 生成器(由于 WebGL 而被限制float)确实管理了大约 5000 倍的缩放。

于 2012-01-31T22:13:41.027 回答
1

感谢您提供您的代码!(+1 让事情变得清晰并包含所有相关信息的问题)。

它表明您的缩放理论是正确的。在每一步,xrangeyrange都将按一个常数因子(0.975?)进行缩放,并且窗口将朝着foundPointOnHost保持窗口中相同的相对位置(ratioXratioY)移动。这绝对是变焦背后的正确理论。但在实践中,由于浮点运算的工作方式,您实现缩放的方式将迅速累积舍入误差。

我的经验法则:

  • 每次将两个浮点数相乘或相除时,都有机会引入一个错误,其大小是结果的最后一位
  • 但是每次添加或减去两个浮点数时,都有可能引入一个错误,其大小是最大操作数的最后一位

工作示例。假设您正在放大 (0.5,0.3),并且您已经放大到 0.001 左右xrangeyrange假设您正在使用浮点数(23 位,精度约为 7 位小数)。在您的代码中,当您减去x要重新计算的两个值时xrange,您将减去两个都接近 0.5 的数字,这意味着您的错误xrange高达 0.0000001 - 0.5 的小数点后七位 - 因为结果大约是0.001,您xrange只精确到小数点后四位。然后你缩放、比例和添加xrange回到窗口的边缘(现在大约是 0.00025),它又是大约 0.5,所以算术运算也只能精确到 0.0000001,并且窗口边缘本身与之前的调用不准确。显然,如果您使用双打,情况会更好,但同样,您xrange每次循环都会失去精度;当您达到 x6 缩放级别时,您可能已经失去了一切。

然后,“技巧”是减少加法和减法的数量(目前,您在每个缩放步骤中执行 6 次),并确保您的错误不会在每次迭代中累积。我建议如下:保留xrangeand yrange,并在每一步重新计算边缘:(ratioXandratioY是恒定的,您可能需要弄清楚哪个是ratio哪个是1-ratio:)

xrange *= 0.975;
xld = foundPointOnHost.x - ratioX * xrange;
xru = foundPointOnHost.x + (1-ratioX) * xrange;

和同样的y。更好的是,xrange使用本身不会在每次乘法中累积错误的操作进行计算:

// xrange = INITIAL_RANGE * SCALE_FACTOR^frame_number
xrange = exp ( log(INITIAL_RANGE) + frame_number*log(SCALE_FACTOR) );
于 2012-02-01T20:37:58.043 回答