Гладкий спектр для рендеринга набора Мандельброта
в настоящее время я пишу программу для создания действительно огромных (65536x65536 пикселей и выше) изображений Мандельброта, и я хотел бы разработать спектр и схему окраски, которая делает их справедливыми. The Википедия показала изображение Мандельброта кажется отличным примером, особенно как палитра остается разнообразной на всех уровнях масштабирования последовательности. Я не уверен, вращает ли он палитру или делает какой-то другой трюк для достижения этого.
Я знаком с алгоритм гладкой окраски для набора Мандельброта, поэтому я могу избежать кольцевания, но мне все еще нужен способ назначить цвета выходным значениям из этого алгоритма.
изображения, которые я генерирую, являются пирамидальными (например, серия изображений, каждое из которых имеет половину размеров предыдущего), поэтому я могу использовать вращающуюся палитру, если изменение палитры между последующими уровнями масштабирования не слишком очевидно.
6 ответов
это гладкий алгоритм цвета:
допустим, вы начинаете с комплексного числа z0
и перебирать n
раз, пока он не убежит. Пусть конечная точка будет zn
.
гладкое значение будет
nsmooth := n + 1 - Math.log(Math.log(zn.abs()))/Math.log(2)
это работает только для Мандельброта, если вы хотите вычислить гладкую функцию для наборов 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_iter
чтобы получить значение между 0 и 1.
чтобы получить гладкий цвет от значения:
это можно назвать, например (на Java):
Color.HSBtoRGB(0.95f + 10 * smoothcolor ,0.6f,1.0f);
поскольку первое значение в параметрах цвета HSB используется для определения цвета из цветового круга.
используйте алгоритм гладкой раскраски для вычисления всех значений в окне просмотра, а затем сопоставьте палитру от самого низкого до самого высокого значения. Таким образом, когда вы увеличиваете масштаб и более высокие значения больше не видны, палитра также будет уменьшаться. С теми же константами для n и B вы получите диапазон от 0.0 до 1.0 для полностью увеличенного набора, но при более глубоких масштабах динамический диапазон уменьшится, скажем, от 0.0 до 0.1 при 200% Зуме, от 0.0 до 0.0001 при 20000% зуме и т. д.
вот типичный внутренний цикл для наивного генератора Мандельброта. Чтобы получить гладкий цвет, вы хотите пройти в реальных и сложных "длинах"и итерации, которые вы выручили. Я включил код Мандельброта, чтобы вы могли видеть, какие vars использовать для вычисления цвета.
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 позволяет изменять используемые цвета.
кажется простым сделать методом проб и ошибок. Предположим, вы можете определить HSV1 и HSV2 (оттенок, насыщенность, значение) цветов конечной точки, которые вы хотите использовать (черный и белый; синий и желтый; темно-красный и светло-зеленый и т. д.), и предположим, что у вас есть алгоритм для присвоения значения P между 0.0 и 1.0 каждому из ваших пикселей. Тогда цвет этого пикселя становится
(H2 - H1) * P + H1 = HP
(S2 - S1) * P + S1 = SP
(V2 - V1) * P + V1 = VP
после этого просто наблюдайте за результатами и посмотрите, как они вам нравятся. Если алгоритм назначения P непрерывен, то градиент также должен быть гладким.
мое окончательное решение состояло в том, чтобы создать красивую (и довольно большую) палитру и сохранить ее как постоянный массив в источнике, а затем интерполировать между индексами в нем с помощью алгоритма гладкой раскраски. Палитра обертывается (и предназначена для непрерывного), но это, похоже, не имеет большого значения.
здесь вы можете найти версию с 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);
}