在维基百科的Mandelbrot set页面上,有很多美丽的Mandelbrot图像。
我也刚刚实现了自己的Mandelbrot算法。给定n
是用于计算每个像素的迭代次数,我将它们从黑色变为绿色到非常简单(使用C ++和Qt 5.0):
QColor mapping(Qt::white);
if (n <= MAX_ITERATIONS){
double quotient = (double) n / (double) MAX_ITERATIONS;
double color = _clamp(0.f, 1.f, quotient);
if (quotient > 0.5) {
// Close to the mandelbrot set the color changes from green to white
mapping.setRgbF(color, 1.f, color);
}
else {
// Far away it changes from black to green
mapping.setRgbF(0.f, color, 0.f);
}
}
return mapping;
我的结果如下:
我已经非常喜欢它,但维基百科中的图像使用了哪种颜色渐变?如何使用给定的n
次迭代计算该梯度?
(这个问题不关于smoothing。)
答案 0 :(得分:22)
渐变可能来自Ultra Fractal。它由5个控制点定义:
Position = 0.0 Color = (0, 7, 100)
Position = 0.16 Color = (32, 107, 203)
Position = 0.42 Color = (237, 255, 255)
Position = 0.6425 Color = (255, 170, 0)
Position = 0.8575 Color = (0, 2, 0)
其中position位于[0,1]范围内,颜色为RGB,范围为0到255.
问题在于它是不是线性渐变。点之间的插值是立方(或类似的)。下图显示了线性和Monotone cubic插值之间的差异:
正如你所看到的那样,立方体更平滑,更“漂亮”。我使用单调立方插值来避免可能由基本立方体引起的颜色范围的“过冲”。插值的单调立方体总是在输入点的范围内(0-255)。
我使用以下代码根据迭代i
计算颜色:
double size = Math.Sqrt(re * re + im * im);
double smoothed = Math.Log(Math.Log(size) * ONE_OVER_LOG2) * ONE_OVER_LOG2;
int colorI = (int)(Math.Sqrt(i + 1 - smoothed) * gradient.Scale + gradient.Shift) % colors.Length;
Color color = colors[colorI];
i
是分歧的迭代次数,re
和im
是分歧坐标,gradient.Scale
是256,gradient.Shift
是0,{{1上面显示了具有预离散梯度的数组。它的长度通常是2048。
答案 1 :(得分:14)
好吧,我使用Photoshop吸管对维基百科中使用的颜色进行了一些逆向工程。此渐变中有16种颜色:
R G B
66 30 15 # brown 3
25 7 26 # dark violett
9 1 47 # darkest blue
4 4 73 # blue 5
0 7 100 # blue 4
12 44 138 # blue 3
24 82 177 # blue 2
57 125 209 # blue 1
134 181 229 # blue 0
211 236 248 # lightest blue
241 233 191 # lightest yellow
248 201 95 # light yellow
255 170 0 # dirty yellow
204 128 0 # brown 0
153 87 0 # brown 1
106 52 3 # brown 2
只需使用模数和QColor数组,我就可以迭代渐变中的所有颜色:
if (n < MAX_ITERATIONS && n > 0) {
int i = n % 16;
QColor mapping[16];
mapping[0].setRgb(66, 30, 15);
mapping[1].setRgb(25, 7, 26);
mapping[2].setRgb(9, 1, 47);
mapping[3].setRgb(4, 4, 73);
mapping[4].setRgb(0, 7, 100);
mapping[5].setRgb(12, 44, 138);
mapping[6].setRgb(24, 82, 177);
mapping[7].setRgb(57, 125, 209);
mapping[8].setRgb(134, 181, 229);
mapping[9].setRgb(211, 236, 248);
mapping[10].setRgb(241, 233, 191);
mapping[11].setRgb(248, 201, 95);
mapping[12].setRgb(255, 170, 0);
mapping[13].setRgb(204, 128, 0);
mapping[14].setRgb(153, 87, 0);
mapping[15].setRgb(106, 52, 3);
return mapping[i];
}
else return Qt::black;
结果看起来非常像我在寻找:
:)
答案 2 :(得分:1)
我相信它们是Ultra Fractal中的默认颜色。评估版本附带了许多参数的来源,我认为包含该颜色图(如果你无法从头版的屏幕截图推断它),也可能还有背后的逻辑为每个场景适当地动态缩放该颜色贴图。
答案 3 :(得分:0)
这是NightElfik很好答案的扩展。
Python库Scipy在1.5.2版中具有带pchip_interpolate的单调三次插值方法。我在下面包含了用于创建渐变的代码。我决定包括小于0且大于1的辅助值,以帮助插值从头到尾进行包裹(无尖角)。
#set up the control points for your gradient
yR_observed = [0, 0,32,237, 255, 0, 0, 32]
yG_observed = [2, 7, 107, 255, 170, 2, 7, 107]
yB_observed = [0, 100, 203, 255, 0, 0, 100, 203]
x_observed = [-.1425, 0, .16, .42, .6425, .8575, 1, 1.16]
#Create the arrays with the interpolated values
x = np.linspace(min(x_observed), max(x_observed), num=1000)
yR = pchip_interpolate(x_observed, yR_observed, x)
yG = pchip_interpolate(x_observed, yG_observed, x)
yB = pchip_interpolate(x_observed, yB_observed, x)
#Convert them back to python lists
x = list(x)
yR = list(yR)
yG = list(yG)
yB = list(yB)
#Find the indexs where x crosses 0 and crosses 1 for slicing
start = 0
end = 0
for i in x:
if i > 0:
start = x.index(i)
break
for i in x:
if i > 1:
end = x.index(i)
break
#Slice away the helper data in the begining and end leaving just 0 to 1
x = x[start:end]
yR = yR[start:end]
yG = yG[start:end]
yB = yB[start:end]
#Plot the values if you want
#plt.plot(x, yR, color = "red")
#plt.plot(x, yG, color = "green")
#plt.plot(x, yB, color = "blue")
#plt.show()