2

I am trying to display a Mandelbrot set fractal using C programming and OpenGL. Here is my code. It is only displaying a dot in the center right now. I cannot figure out where I am going wrong. I'm pretty sure my math is correct. Maybe I have something in the wrong loop?

This picture is what Im trying to get

Here is my code so far:

#include <GLUT/glut.h>
#include <math.h>

void init(void);
void display(void);

const int screenWidth = 640;
const int screenHeight = 480;

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(screenWidth, screenHeight);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Mandelbrot");

    // glViewport(-320, -320, 320, 320);
    init();
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

void init(void) {
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(-500.0, screenWidth, -500.0, screenHeight);
    // A = screenWidth / 4.0;
    // B = 0.0;
    // C = D = screenHeight / 2.0;
}

void display(void) {
    GLdouble x, f, y, xtemp, y0, x0, iteration, maxInteration;
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glPointSize(1);
    glColor3f(0.0, 0.0, 0.0);

    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    for (y0 = - 1; y0 < 1.1; y0 = y0 + 0.0025) {
        for (x0 = -2.5; x0 < 1.1; x0 = x0 + 0.0025) {
            x = 0;
            y = 0;
            iteration = 0;
            maxInteration = 1000;

            while (((x * x) + (y * y) < (2 * 2)) && iteration < maxInteration) {
                xtemp = (x * x) - (y * y) + x0;
                y = (2 * x * y) + y0;
                x = xtemp;
                iteration = iteration + 1;

                if (y <= 2) {
                    glBegin(GL_POINTS);

                    glVertex2d(x / 750, y / 750);
                    glEnd();
                }
            }
        }
    }    
    glFlush();
}

Here is my updated code after fixing suggestions in comments.. It results in the above image.. However, now I am trying to create the grey circles around the object??? Im attempting to do this through the else at the end... any thoughts?

#include <GLUT/glut.h>
#include <math.h>

void init(void);
void display(void);


const int screenWidth = 640;
const int screenHeight = 640;
GLdouble A, B, C, D;

int main(int argc, char** argv) {
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize(screenWidth, screenHeight);
  glutInitWindowPosition(0, 0);
  glutCreateWindow("Mandelbrot");
glViewport(-1, 1, -1, 1);
  init();
  glutDisplayFunc(display);
  glutMainLoop();
  return 0;
}

  void init(void) {
  //glMatrixMode(GL_PROJECTION);
   gluOrtho2D(-3.0, 3.0, -3.0, 3.0);
  A = screenWidth / 4.0;
  B = 0.0;
  C = D = screenHeight / 2.0;
}

void display(void)
{
GLdouble x, f, y, xtemp, y0, x0, iteration, maxInteration;
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(1);


glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);



for(y0 = -1; y0< 1.1; y0 = y0 + 0.0025){

for (x0 = -2.5; x0 < 1.1; x0 = x0 + 0.0025) {
    x = 0;
    y = 0;
    iteration = 0;
    maxInteration = 200;

    while(((x*x) + (y*y) <(2*2)) && iteration <maxInteration){
        xtemp = (x*x) - (y*y) + x0;
        y = (2*x*y) +y0;
        x = xtemp;
        iteration = iteration + 1;


        }
    if(iteration >= maxInteration){
        glBegin(GL_POINTS);

        glVertex2d(x0 , y0);
         glColor3f(0.0, 0.0, 0.0);
        glEnd();
    }
    else{
        ????

    }



}
}



glFlush();
  }
I31
  • 57
  • 1
  • 1
  • 11
  • 1
    "Pretty sure" your math is correct? I'd suggest verifying it -- print out the coordinates of each glVertex2d call. Also, your Ortho call is suspect: you're trying to set up a window that goes from x = -500 to x = screen width. That's an odd mix. If you're just getting a dot in the center, I'm guessing the vertex calls are all emitting values close to zero, ending up in the "middle" of [-500,w] – L. Scott Johnson May 25 '17 at 18:41
  • I'm curious, why would anyone downvote this question? Sometimes i struggle understanding SO, +1 :P – BPL May 25 '17 at 19:06
  • 3
    Your viewport is *way* too big; the whole of the Mandelbrot set fits in the range -2-2i to 2+2i. Start by setting your viewport coordinates as -2,2,-2,2. – John Bode May 25 '17 at 19:20
  • The iteration math is correct, so as @JohnBode suggests, one pixel in the centre seems to be an issue of scale. – Weather Vane May 25 '17 at 19:26
  • thanks for the help. I changed it to @JohnBode suggestion and it's still just a single dot in the center – I31 May 25 '17 at 19:29
  • Don't divide `x` and `y` by 750 in your `glVertex2d` call. – John Bode May 25 '17 at 19:32
  • Whatever `glVertex2d(x /750, y/750);` does, should it be using `x0` and `y0`? And that code block should be *outside* the iteration loop? And testing `iteration` instead of `y`? You do not normally plot a pixel until the iteration is complete. – Weather Vane May 25 '17 at 19:34
  • added a picture to what im getting after your suggestions – I31 May 25 '17 at 19:42
  • Doesn't look like **one** dot, but a lot of dots near the centre. – Weather Vane May 25 '17 at 19:43
  • it was a single dot after I made your changes it turned to this – I31 May 25 '17 at 19:43
  • Usually you plot a dot at the original coordinate, coloured by the number of iterations. At its simplest in/out of Mandelbrot set. – Weather Vane May 25 '17 at 19:44
  • Re update: so it is a matter of scale. – Weather Vane May 25 '17 at 19:50
  • I updated the image again.. To get this I changed if(iteration <=2) to 10... The mandelbrot shape is there.. just super small and its white instead of black? – I31 May 25 '17 at 19:50
  • `if (iteration < maxInteration)` or other way round - `if (iteration >= maxInteration)`. As for scaling I have used opengl a few times but rusty. – Weather Vane May 25 '17 at 19:51
  • Fixed the scale... It almost looks right. How do you add the extra black and the grey circles? Thanks for all your help!!! – I31 May 25 '17 at 20:01
  • Bingo: but please note, the question should usually remain as posted apart from additional information, as opposed to a dynamic response to comments. A solution may be posted as an edit or (perhaps) an answer. – Weather Vane May 25 '17 at 20:03
  • You add the grey circles by scaling the iterations between white and black. So if `maxInteration = 1000` if the function value escapes at 500 then the grey scale will be `0x80`. – Weather Vane May 25 '17 at 20:04
  • I made an else but its not working.. I think it is my lack of understanding of opengl – I31 May 25 '17 at 20:50

2 Answers2

1

First of all, here's some advices regarding to your code:

  • When working with complex numbers or vectors i'd recommend you to use a proper fast math library so you can avoid operating with individual components, there are very fast cpu math libraries out there which can use SIMD instructions and your code will become more readable
  • The way your drawing the mandelbrot is really a bad idea. I mean, yeah, it's alright if you just want to dump simple images and learning the basics but that's pretty much. Don't use GL_POINTS and try to render/update textures directly, or even better, use fragment shaders + glsl (recommended way) so your mandelbrot will be rendered very fast even if you're using non-optimized maths.
  • Coordinate systems, if you still insist on using GL_POINTS the way you're doing, i'd just use directly the window coordinates and going from that space to the mandelbrot math domain ie: [0,0,w,h]<->[-1,-1,1,1]

Here's a little example of what i mean:

#include <GL/glut.h>
#include <math.h>
#include <stdio.h>

const int screen_width = 640;
const int screen_height = 480;
float c[4];
float z[4];

float clamp(float x, float vmin, float vmax) {
    if (x < vmin) {
        return vmin;
    } else if (x > vmax) {
        return vmax;
    }

    return x;
}

void dc_add(float *a, float *b, float *res) {
    res[0] = a[0] + b[0];
    res[1] = a[1] + b[1];
    res[2] = a[2] + b[2];
    res[3] = a[3] + b[3];
}

void dc_mul(float *a, float *b, float *res) {
    res[0] = a[0] * b[0] - a[1] * b[1];
    res[1] = a[0] * b[1] + a[1] * b[0];
    res[2] = a[0] * b[2] + a[2] * b[0] - a[1] * b[3] - a[3] * b[1];
    res[3] = a[0] * b[3] + a[3] * b[0] + a[2] * b[1] + a[1] * b[2];
}

void dc_sqr(float *a, float *res) {
    res[0] = a[0] * a[0] - a[1] * a[1];
    res[1] = 2.0f * a[0] * a[1];
    res[2] = 2.0f * (a[0] * a[2] - a[1] * a[3]);
    res[3] = 2.0f * (a[0] * a[3] + a[1] * a[2]);
}

float dot(float x, float y) { return x * x + y * y; }

void init(void) {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, screen_width, 0, screen_height);
}

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT);
    glPointSize(1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    for (int y = 0; y < screen_height; y++) {
        for (int x = 0; x < screen_width; x++) {
            float px = -1.0f + 2.0f * (float)x / (float)screen_width;
            float py = -1.0f + 2.0f * (float)y / (float)screen_height;
            px *= (float)screen_width / (float)screen_height;

            float tz = 0.5f;
            float zo = powf(1.2f, 1.2f);

            float m2 = 0.0f;
            float co = 0.0f;
            float temp[4];
            c[0] = px * zo; c[1] = py * zo; c[2] = 1.0; c[3] = 0.0;
            z[0] = 0.0f; z[1] = 0.0f; z[2] = 0.0f; z[3] = 0.0f;

            for (int i = 0; i < 256; i++) {
                if (m2 > 1024.0f) continue;

                dc_sqr(z, temp);
                dc_add(temp, c, z);
                m2 = dot(z[0], z[1]);
                co += 1.0f;
            }

            float d = 0.0f;
            if (co < 256.0f) {
                d = sqrtf((dot(z[0], z[1]) / dot(z[2], z[3]))) *
                    logf(dot(z[0], z[1]));
            }

            d = clamp(4.0f * d / zo, 0.0f, 1.0f);
            d = powf(d, 0.25f);
            glColor3f(d, d, d);
            glBegin(GL_POINTS);
            glVertex2d(x, y);
            glEnd();
        }
    }

    glFlush();
    glutSwapBuffers();
}

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(screen_width, screen_height);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Mandelbrot");
    init();
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutMainLoop();

    return 0;
}

And here's the output:

enter image description here

Maths of the above example are based on this shadertoy.

The above code is terrible unefficient and slow but it serves the main purpose to prove you the way you shouldn't ever code a proper mandelbrot.

Happy coding.

BPL
  • 9,632
  • 9
  • 59
  • 117
  • ok thanks for the tips. I was using https://skr1986.wordpress.com/2012/11/12/mandelbrot-set-implementation-using-c-and-opengl/ as an example – I31 May 25 '17 at 21:42
  • 1
    Using `float` will not allow much zooming into the Mandelbrot set :( – chqrlie May 25 '17 at 21:49
  • @chqrlie Indeed, `double` better... also, if using shaders you'll also be quite limited by "highp" range. – BPL May 25 '17 at 22:03
0

There is a simple problem in you code: you use a division instead of a multiplication to compute the pixel coordinates: change glVertex2d(x / 750, y / 750); to

glVertex2d(x * 750, y * 750);

However, this is not the correct method to compute the Mandelbrot set. You should instead compute the number of iterations for the squared module to exceed 4.0, and then set the color of the pixel at (x0 * 300, y0 * 300) to that number as a palette entry or a gray level.

chqrlie
  • 131,814
  • 10
  • 121
  • 189