37

我目前正在编写一个程序来生成非常巨大的(65536x65536 像素及以上)Mandelbrot 图像,我想设计一个光谱和着色方案来使它们公正。维基百科的特色 mandelbrot 图像似乎是一个很好的例子,尤其是调色板如何在序列的所有缩放级别保持变化。不过,我不确定它是在旋转调色板还是在做其他一些技巧来实现这一点。

我熟悉 mandelbrot 集的平滑着色算法,所以我可以避免条带,但我仍然需要一种方法来为该算法的输出值分配颜色。

我正在生成的图像是金字塔形的(例如,一系列图像,每个图像的尺寸都是前一个的一半),所以我可以使用某种旋转调色板,只要在后续调色板之间发生变化缩放级别不是太明显。

4

7 回答 7

42

这是平滑颜色算法:

假设您从复数开始z0并迭代n次数,直到它逃脱。设终点为zn

一个平滑的值是

nsmooth := n + 1 - Math.log(Math.log(zn.abs()))/Math.log(2)

这仅适用于 mandelbrot,如果您想计算 julia 集的平滑函数,请使用

Complex z = new Complex(x,y);
double smoothcolor = Math.exp(-z.abs());

for(i=0;i<max_iter && z.abs() < 30;i++) {
    z = f(z);
    smoothcolor += Math.exp(-z.abs());
}

然后smoothcolor就是在区间(0,max_iter)

除以smoothcolor得到max_iter0 和 1 之间的值。

要从值中获得平滑的颜色:

这可以被称为,例如(在 Java 中):

Color.HSBtoRGB(0.95f + 10 * smoothcolor ,0.6f,1.0f);

因为 HSB 颜色参数中的第一个值用于定义色环的颜色。

于 2009-08-07T09:21:25.367 回答
6

使用平滑着色算法计算视口中的所有值,然后将调色板从最低值映射到最高值。因此,当您放大并且不再可见较高的值时,调色板也会按比例缩小。对于 n 和 B 使用相同的常数,对于完全缩小的设置,您最终会得到 0.0 到 1.0 的范围,但在更深的缩放中,动态范围会缩小,例如 200% 缩放时为 0.0 到 0.1,在 200% 缩放时为 0.0 到 0.0001 20000% 变焦等

于 2008-12-16T09:22:43.547 回答
6

这是一个简单的 Mandelbrot 生成器的典型内部循环。要获得平滑的颜色,您需要传递真实和复杂的“长度”以及您退出的迭代。我已经包含了 Mandelbrot 代码,因此您可以查看使用哪些变量来计算颜色。

for (ix = 0; ix < panelMain.Width; ix++)
    {
    cx = cxMin + (double )ix * pixelWidth;
    // init this go 
    zx = 0.0;
    zy = 0.0;
    zx2 = 0.0;
    zy2 = 0.0;
    for (i = 0; i < iterationMax && ((zx2 + zy2) < er2); i++)
        {
        zy = zx * zy * 2.0 + cy;
        zx = zx2 - zy2 + cx;
        zx2 = zx * zx;
        zy2 = zy * zy;
        }
    if (i == iterationMax)
        {
        // interior, part of set, black
        // set colour to black
        g.FillRectangle(sbBlack, ix, iy, 1, 1);
        }
    else
        {
        // outside, set colour proportional to time/distance it took to converge
        // set colour not black
        SolidBrush sbNeato = new SolidBrush(MapColor(i, zx2, zy2));
        g.FillRectangle(sbNeato, ix, iy, 1, 1);
        }

和下面的 MapColor :(请参阅此链接以获取 ColorFromHSV 函数

 private Color MapColor(int i, double r, double c)
                {
                double di=(double )i;
                double zn;
                double hue;

                    zn = Math.Sqrt(r + c);
                    hue = di + 1.0 - Math.Log(Math.Log(Math.Abs(zn))) / Math.Log(2.0);  // 2 is escape radius
                    hue = 0.95 + 20.0 * hue; // adjust to make it prettier
                    // the hsv function expects values from 0 to 360
                    while (hue > 360.0)
                        hue -= 360.0;
                    while (hue < 0.0)
                        hue += 360.0;

                    return ColorFromHSV(hue, 0.8, 1.0);
                }

MapColour 正在“平滑”从 0 到 1 的救助值,然后可用于映射颜色而不会出现可怕的条纹。使用 MapColour 和/或 hsv 函数可以让您更改使用的颜色。

于 2013-09-07T23:19:44.753 回答
4

通过反复试验似乎很简单。假设您可以定义您希望使用的端点颜色(黑色和白色;蓝色和黄色;深红色和浅绿色等)的 HSV1 和 HSV2(色调、饱和度、值),并假设您有一个算法来分配每个像素的值 P 介于 0.0 和 1.0 之间。然后那个像素的颜色变成

(H2 - H1) * P + H1 = HP
(S2 - S1) * P + S1 = SP
(V2 - V1) * P + V1 = VP

完成后,只需观察结果,看看你喜欢它们。如果分配 P 的算法是连续的,那么梯度也应该是平滑的。

于 2008-12-15T20:00:38.533 回答
4

我最终的解决方案是创建一个漂亮(且相当大)的调色板并将其作为常量数组存储在源中,然后使用平滑着色算法在其中的索引之间进行插值。调色板包裹(并且被设计为连续的),但这似乎并不重要。

于 2008-12-17T10:52:53.987 回答
0

该图像中的颜色映射发生了什么是它在索引上使用“对数传递函数”(根据文档)。它到底是怎么做的,我还没有弄清楚。生成它的程序使用 400 种颜色的调色板,因此索引范围为 [0,399),如果需要,可以环绕。我已经设法非常接近匹配它的行为。我使用 [0,1) 的索引范围并像这样映射它:

    double value = Math.log(0.021 * (iteration + delta + 60)) + 0.72;
    value = value - Math.floor(value);

我必须在其中使用这些特殊常量来使我的结果匹配,这有点奇怪,因为我怀疑它们会做任何事情。但最终不管怎样,对吧?

于 2019-04-04T17:42:56.410 回答
-2

在这里你可以找到一个带有 javascript 的版本

用法 :

var rgbcol = [] ;
var rgbcol = MapColor ( Iteration , Zy2,Zx2 ) ;
point ( ctx , iX, iY ,rgbcol[0],rgbcol[1],rgbcol[2] );  

功能

/*
 * The Mandelbrot Set, in HTML5 canvas and javascript.
 * https://github.com/cslarsen/mandelbrot-js
 *
 * Copyright (C) 2012 Christian Stigen Larsen
*/

/*
 * Convert hue-saturation-value/luminosity to RGB.
 *
 * Input ranges:
 *   H =   [0, 360] (integer degrees)
 *   S = [0.0, 1.0] (float)
 *   V = [0.0, 1.0] (float)
 */
function hsv_to_rgb(h, s, v)
{
  if ( v > 1.0 ) v = 1.0;
  var hp = h/60.0;
  var c = v * s;
  var x = c*(1 - Math.abs((hp % 2) - 1));
  var rgb = [0,0,0];

  if ( 0<=hp && hp<1 ) rgb = [c, x, 0];
  if ( 1<=hp && hp<2 ) rgb = [x, c, 0];
  if ( 2<=hp && hp<3 ) rgb = [0, c, x];
  if ( 3<=hp && hp<4 ) rgb = [0, x, c];
  if ( 4<=hp && hp<5 ) rgb = [x, 0, c];
  if ( 5<=hp && hp<6 ) rgb = [c, 0, x];

  var m = v - c;
  rgb[0] += m;
  rgb[1] += m;
  rgb[2] += m;

  rgb[0] *= 255;
  rgb[1] *= 255;
  rgb[2] *= 255;

  rgb[0] = parseInt ( rgb[0] ); 
  rgb[1] = parseInt ( rgb[1] );
  rgb[2] = parseInt ( rgb[2] );  

  return rgb;
}

// http://stackoverflow.com/questions/369438/smooth-spectrum-for-mandelbrot-set-rendering
// alex russel : http://stackoverflow.com/users/2146829/alex-russell

function MapColor(i,r,c)
{
    var di= i;
    var zn;
    var hue;

        zn = Math.sqrt(r + c);
        hue = di + 1.0 - Math.log(Math.log(Math.abs(zn))) / Math.log(2.0);  // 2 is escape radius
        hue = 0.95 + 20.0 * hue; // adjust to make it prettier
        // the hsv function expects values from 0 to 360
        while (hue > 360.0)
            hue -= 360.0;
        while (hue < 0.0)
            hue += 360.0;

        return hsv_to_rgb(hue, 0.8, 1.0);
}
于 2016-06-21T10:03:35.357 回答