1

I'm using SFML for c++ to generate the Mandelbrot set. The actual generation part is done in the fragment shader, for performance. Please note that I don't have a lot of experience with shaders.

For some reason, when I zoom in beyond a certain point, this happens.

enter image description here

It really doesn't make a lot of sense, since I'm not scaling anything, just adjusting parameters in the generation. My best guess is that it has something to do with drawing fragments, not pixels.

Here's the fragment shader (copy/paste did something weird with the formatting)

#version 400

uniform int window_width;
uniform int window_height;

uniform float x_translation;
uniform float y_translation;
uniform float zoom;
uniform int iterations = 255;
uniform int color;


float map(float n, float start1, float stop1, float start2, float stop2)
{
    return ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
}

void main()
{

float diversion_threshold = 4.0;

float saved_brightness = 0.0;

float real_translated_a = ((-2.0 - x_translation));
float real_translated_b = ((2.0 - x_translation));

float imaginary_translated_a = ((-2.0 + y_translation));
float imaginary_translated_b = ((2.0 + y_translation));

//float z_real = map(gl_FragCoord.x, 0.0, float(window_width), real_translated_a * zoom, real_translated_b * zoom);
//float z_imaginary = map(gl_FragCoord.y, 0.0, float(window_height), imaginary_translated_a * zoom, imaginary_translated_b * zoom);

float z_real = x_translation + (gl_FragCoord.x/window_width - 0.5) * zoom;
float z_imaginary = y_translation + (gl_FragCoord.y/window_height - 0.5) * zoom;

float z_real_original = z_real;
float z_imaginary_original = z_imaginary;
float z_real_temp;
float z_imaginary_temp;

float brightness = 0.0;

for (int n = 0;n <= iterations; n++) {
    z_real_temp = z_real * z_real - z_imaginary * z_imaginary;
    z_imaginary_temp = 2.0 * z_real * z_imaginary;

    z_real = z_real_temp + z_real_original;
    z_imaginary = z_imaginary_temp + z_imaginary_original;

    if (color != 0)brightness = map(float(n), 0.0, float(iterations), 0.0, 1.0);
    else brightness = map(float(n), 0.0, float(iterations), 1.0, 0.0);

    brightness = map(sqrt(brightness), 0.0, 1.0, 0.0, 1.0);

    if ((z_real * z_real + z_imaginary * z_imaginary) > diversion_threshold) {
        break;
    }
}

if (color == 0) gl_FragColor = vec4(brightness, brightness, brightness, 1.0);
if (color == 1) gl_FragColor = vec4(brightness, 0.0, 0.0, 1.0);
if (color == 2) gl_FragColor = vec4(0.0, 0.0, brightness, 1.0);
if (color == 3) gl_FragColor = vec4(0.0, brightness, 0.0, 1.0);
if (color == 4) gl_FragColor = vec4(brightness, 0.0, brightness * 0.67, 1.0);
if (color == 5) gl_FragColor = vec4(brightness * 0.1, brightness, brightness * 0.56, 1.0);

}

The shader is simply applied to a rectangle and that's it.

genpfault
  • 51,148
  • 11
  • 85
  • 139
SGl0bGVy
  • 11
  • 2
  • 9
    It's probably when you run out of precision. For deep zooming into the Mandelbrot set you will need to use an arbitrary precision mathematics library. – Richard Critten Mar 09 '21 at 12:57
  • As @RichardCritten says, you're running out of precision as you zoom in more deeply. You'll get much more 'depth' available by using `double` variables rather than `float` but, even then, there will be a point at which the resolution will fail. Depending on your platform/compiler, you may even have an extended-precision `long double` type. That will go even deeper (but will be noticeably slower than `float` or `double`). – Adrian Mole Mar 09 '21 at 15:43
  • @AdrianMole no there is no long double ... sometimes even double is not present – Spektre Mar 10 '21 at 08:45
  • see [Can't find a way to color the Mandelbrot-set the way i'm aiming for](https://stackoverflow.com/a/56197067/2521214) for inspiration. Yes the problem is floating precision. If you can use double, however beware it will be ~2x slower and you can not pass `varying double` between vertex and fragment. So all the doubles will be in fragment shader only and beware you need to cast all the conversions between `double` and `float`. Also see [how do I get infinitely small numbers (for fractals)](https://stackoverflow.com/a/65018319/2521214). Your question is OK but its a duplicate so I Close voted – Spektre Mar 10 '21 at 08:55
  • @Spektre `long double` is a type specified by the C and C+ standards. On some (many?) implementations, it is the same as `double`, however. On the speed issue: don't know what platform OP is using but, on x86/x64 systems, `double` arithmetic is not noticeably slower than `float` (single precision). But, for those systems that implement 80- or 128-bit `long double` types using software emulation, then `long double` arithmetic will be much slower, as I said in my first comment. See, for example, [here](https://www.learncpp.com/cpp-tutorial/floating-point-numbers). – Adrian Mole Mar 10 '21 at 11:23
  • @AdrianMole its GLSL so computed inside GPU fragment shader there is no long double ... its not fully C++ compliant there are many restrictions and missing stuff in GLSL specification in comparison to standardized C++. – Spektre Mar 10 '21 at 11:25
  • @Spektre Ah, OK. But, if performance is the issue, then calculating `brightness` *inside* the `for` loop is not ideal. That can be left until the final iteration count is known, surely. – Adrian Mole Mar 10 '21 at 11:52
  • @AdrianMole yes the brightness/color should be moved after the for loop its direct function of the last iterations value so its absolutely pointless to iterate it on each iteration of for loop. The computation itself is not as a big deal the major performance hit is the presence of `if else` statement causing branching related slow downs which on GLSL is usually a major hit inside Fragment. – Spektre Mar 11 '21 at 08:45
  • @SGl0bGVy btw what zoom is this? with `double` I can go up to `zoom=10^14` where pixelation start to occur. – Spektre Mar 11 '21 at 08:49

0 Answers0