Рендеринг Мандельброта, установленного в цвете (и чтобы он выглядел хорошо) - PullRequest
0 голосов
/ 30 января 2019

Я пытаюсь визуализировать набор Мандельброта в цвете и заставить его выглядеть хорошоЯ хочу скопировать изображение со страницы Википедии .Изображение из Википедии также включает файл параметров Ultra Fractal 3.

mandelZoom00MandelbrotSet {
fractal:
  title="mandel zoom 00 mandelbrot set" width=2560 height=1920 layers=1
  credits="WolfgangBeyer;8/21/2005"
layer:
  method=multipass caption="Background" opacity=100
mapping:
  center=-0.7/0 magn=1.3
formula:
  maxiter=50000 filename="Standard.ufm" entry="Mandelbrot" p_start=0/0
  p_power=2/0 p_bailout=10000
inside:
  transfer=none
outside:
  density=0.42 transfer=log filename="Standard.ucl" entry="Smooth"
  p_power=2/0 p_bailout=128.0
gradient:
  smooth=yes rotation=29 index=28 color=6555392 index=92 color=13331232
  index=196 color=16777197 index=285 color=43775 index=371 color=3146289
opacity:
  smooth=no index=0 opacity=255
}

Я пытался расшифровать этот файл и написать программу для воспроизведения изображения из Википедии.Это моя лучшая попытка:

Mandelbrot set

Это часть рендерера.Это немного грязно, потому что я возился и переписывал большие куски, пытаясь разобраться в этом.

using real = double;
using integer = long long;

struct complex {
  real r, i;
};

using grey = unsigned char;

struct color {
  grey r, g, b;
};

struct real_color {
  real r, g, b;
};

grey real_to_grey(real r) {
  // converting to srgb didn't help much
  return std::min(std::max(std::round(r), real(0.0)), real(255.0));
}

color real_to_grey(real_color c) {
  return {real_to_grey(c.r), real_to_grey(c.g), real_to_grey(c.b)};
}

real lerp(real a, real b, real t) {
  return std::min(std::max(t * (b - a) + a, real(0.0)), real(255.0));
}

real_color lerp(real_color a, real_color b, real t) {
  return {lerp(a.r, b.r, t), lerp(a.g, b.g, t), lerp(a.b, b.b, t)};
}

complex plus(complex a, complex b) {
  return {a.r + b.r, a.i + b.i};
}

complex square(complex n) {
  return {n.r*n.r - n.i*n.i, real{2.0} * n.r * n.i};
}

complex next(complex z, complex c) {
  return plus(square(z), c);
}

real magnitude2(complex n) {
  return n.r*n.r + n.i*n.i;
}

real magnitude(complex n) {
  return std::sqrt(magnitude2(n));
}

color lerp(real_color a, real_color b, real t) {
  return real_to_grey(lerp(a, b, t));
}

struct result {
  complex zn;
  integer n;
};

result mandelbrot(complex c, integer iterations, real bailout) {
  complex z = {real{0.0}, real{0.0}};
  integer n = 0;
  real bailout2 = bailout * bailout;
  for (; n < iterations && magnitude2(z) <= bailout2; ++n) {
    z = next(z, c);
  }
  return {z, n};
}

struct table_row {
  real index;
  real_color color;
};

real invlerp(real value, real min, real max) {
  return (value - min) / (max - min);
}

color lerp(table_row a, table_row b, real index) {
  return lerp(a.color, b.color, invlerp(index, a.index, b.index));
}

color mandelbrot_color(complex c, integer iterations, real bailout) {
  const result res = mandelbrot(c, iterations, bailout);
  if (res.n == iterations) {
    // in the set
    return {0, 0, 0};
  } else {
    table_row table[] = {
      // colors and indicies from gradient section
      {28.0*0.1, {0x00, 0x07, 0x64}},
      {92.0*0.1, {0x20, 0x6B, 0xCB}},
      {196.0*0.1, {0xED, 0xFF, 0xFF}},
      {285.0*0.1, {0xFF, 0xAA, 0x00}},
      {371.0*0.1, {0x31, 0x02, 0x30}},
      // interpolate towards black as we approach points that are in the set
      {real(iterations), {0, 0, 0}}
    };
    // it should be smooth, but it's not
    const real smooth = res.n + real{1.0} - std::log(std::log2(magnitude(res.zn)));
    // I know what a for-loop is, I promise
    if (smooth < table[1].index) {
      return lerp(table[0], table[1], smooth);
    } else if (table[1].index <= smooth && smooth < table[2].index) {
      return lerp(table[1], table[2], smooth);
    } else if (table[2].index <= smooth && smooth < table[3].index) {
      return lerp(table[2], table[3], smooth);
    } else if (table[3].index <= smooth && smooth < table[4].index) {
      return lerp(table[3], table[4], smooth);
    } else {
      return lerp(table[4], table[5], smooth);
    }
  }
}

Цвета из секции gradient находятся в таблице в mandelbrot_color.Индексы из секции gradient также есть в таблице, но я умножил их на 0.1.Цвета выглядят совершенно нечеткими, если я не умножу на 0.1.

В секции formula есть maxiter=50000 и p_bailout=10000.Это iterations и bailout в коде.Я не знаю, что означает p_start=0/0 p_power=2/0.Я не знаю, почему в разделе outside упоминается другая помощь, и я не знаю, что означает density=0.42, transfer=none, transfer=log.В разделе gradient также упоминается rotation=29, но я не понимаю, как можно повернуть градиент.

Причина, по которой я задаю этот вопрос, заключается в том, что мне не нравятся белые полосы вокруг моего изображения (Я бы предпочел плавное свечение, как на картинке из Википедии).Мне также не нравится темно-фиолетовая кожа, вызванная интерполяцией к черному (последняя строка в таблице mandelbrot_color).Если мы удалим эту строку, мы получим темно-синий скин.

Я подозреваю, что существует какое-то соответствие между индексами в разделе gradient и количеством итераций.Может быть, * 0.1 является приближением того отображения, которое иногда работает.Может иметь отношение к transfer, density или rotation.Оставьте комментарий, если вы хотите, чтобы я опубликовал всю программу.Это зависит от stb_image_write (библиотека для записи изображений с одним заголовком).

В качестве примечания я приведу этот код в порядок и очистил его, добавив фрагментный шейдер, скорее всего, он будет быстрее (вообще говоря)) чем работает многопоточный процессор?

...