diff options
| -rw-r--r-- | README.md | 32 | ||||
| -rw-r--r-- | inc/config.h | 3 | ||||
| -rw-r--r-- | inc/mandel.h | 4 | ||||
| -rw-r--r-- | screenshot.png | bin | 0 -> 541677 bytes | |||
| -rw-r--r-- | shader/fragment.glsl | 115 | ||||
| -rw-r--r-- | src/event.c | 21 | ||||
| -rw-r--r-- | src/shader.c | 6 | ||||
| -rw-r--r-- | src/state.c | 2 |
8 files changed, 137 insertions, 46 deletions
@@ -1,29 +1,17 @@ -# Mandelbrot set visualizer +# Mandelbrot visualizer -A visualizer for the [Mandelbrot Set](https://en.wikipedia.org/wiki/Mandelbrot_set). +A visualizer of the [Mandelbrot Set](https://en.wikipedia.org/wiki/Mandelbrot_set). -## Dependencies - -- [SDL2](https://www.libsdl.org/) for the graphics - -## Install - -SDL2: `sudo apt install libsdl2-dev` + ## Usage -1. compile: `make all` -2. run: `./mandel` +``` +> make all +> ./mandel +``` -## TODO +## Dependencies -- [ ] Color gradient with control points and cubic iterpolation - like <https://stackoverflow.com/a/25816111> -- [ ] Smooth shading for out of set iteration colors. -- [ ] Draw to bmp image. -- [x] Create pixel array and update the render line by line. -- [ ] Display coordinates and other useful info. -- [x] Computation done in parallel. -- [x] Command line options -- [x] Anti-aliasing with supersampling -- [ ] Other (not random?) supersampling algorithm +- [SDL2](https://www.libsdl.org/) - window and OpenGL context +- [glew](http://glew.sourceforge.net/) - OpenGL implementation diff --git a/inc/config.h b/inc/config.h index 4ce9f2e..c336c51 100644 --- a/inc/config.h +++ b/inc/config.h @@ -4,7 +4,6 @@ # define MANDEL_WINDOW_WIDTH 640 # define MANDEL_WINDOW_HEIGHT 480 # define MANDEL_WINDOW_TITLE "Mandelbrot" -# define MANDEL_SURFACE_DEPTH 8 -# define MANDEL_ITERATIONS 20 +# define MANDEL_ITERATIONS 50 #endif diff --git a/inc/mandel.h b/inc/mandel.h index f220132..0383a39 100644 --- a/inc/mandel.h +++ b/inc/mandel.h @@ -69,6 +69,8 @@ typedef struct int imag_end; int iterations; int texture; + int smooth; + int samples; } location; } Shader; @@ -93,6 +95,8 @@ typedef struct double imag_start; double imag_end; int iterations; + bool smooth; + float samples; } State; // mandelbrot.c diff --git a/screenshot.png b/screenshot.png Binary files differnew file mode 100644 index 0000000..dcb739b --- /dev/null +++ b/screenshot.png diff --git a/shader/fragment.glsl b/shader/fragment.glsl index 6720236..6bc028c 100644 --- a/shader/fragment.glsl +++ b/shader/fragment.glsl @@ -11,36 +11,117 @@ uniform float u_imag_start; uniform float u_imag_end; uniform int u_iterations; +uniform bool u_smooth; +uniform float u_samples; uniform sampler1D u_texture; #define ESCAPE_RADIUS 4.0 +#define LOG_2 0.69314718056 -void main() -{ - float ca = u_real_start + float(gl_FragCoord.x) / float(u_width) * (u_real_end - u_real_start); - float cb = u_imag_start + float(gl_FragCoord.y) / float(u_height) * (u_imag_end - u_imag_start); - float zr = ca; - float zi = cb; - float zr_square; - float zi_square; +int mandelbrot_func(vec2 c) +{ + vec2 z; + vec2 z_square; int n; + z = c; for (n = 0; n < u_iterations; n++) { - zi_square = zi * zi; - zr_square = zr * zr; - if (zr_square + zi_square > ESCAPE_RADIUS) + z_square = z * z; + if (z_square.x + z_square.y > ESCAPE_RADIUS) break; - zi = 2.0 * zr * zi; - zr = zr_square - zi_square; - zi += cb; - zr += ca; + z.y = 2.0 * z.x * z.y; + z.x = z_square.x - z_square.y; + z += c; } + return n; +} +float mandelbrot_smooth(vec2 c) +{ + vec2 z; + vec2 z_square; + int n; + + z = c; + for (n = 0; n < u_iterations; n++) + { + z_square = z * z; + if (z_square.x + z_square.y > ESCAPE_RADIUS) + break; + z.y = 2.0 * z.x * z.y; + z.x = z_square.x - z_square.y; + z += c; + } if (n == u_iterations) - out_color = vec4(0.0, 0.0, 0.0, 1.0); + return float(n); + // http://linas.org/art-gallery/escape/escape.html + z_square = z * z; + z.y = 2.0 * z.x * z.y; + z.x = z_square.x - z_square.y; + z += c; + z_square = z * z; + z.y = 2.0 * z.x * z.y; + z.x = z_square.x - z_square.y; + z += c; + float modulus = sqrt(z.x * z.x + z.y * z.y); + return float(n) - log(log(modulus)) / LOG_2; +} + +vec4 mandelbrot_color(vec2 c) +{ + float n; + + if (u_smooth) + n = mandelbrot_smooth(c); + else + n = float(mandelbrot_func(c)); + + if (n == float(u_iterations)) + return vec4(0.0, 0.0, 0.0, 1.0); + else + return texture(u_texture, n / float(u_iterations)); + +} + +vec4 supersample_grid(vec2 c) +{ + vec2 epsilon; + vec2 _sample; + vec2 _step; + vec4 color; + + color = vec4(0.0, 0.0, 0.0, 0.0); + _step.x = (u_real_end - u_real_start) / float(u_width); + _step.y = (u_imag_end - u_imag_start) / float(u_height); + epsilon.y = 0.0; + while (epsilon.y < u_samples) + { + epsilon.x = 0.0; + while (epsilon.x < u_samples) + { + _sample = c + _step * (epsilon / u_samples); + color += mandelbrot_color(_sample); + epsilon.x += 1.0; + } + epsilon.y += 1.0; + } + return color / (u_samples * u_samples); +} + +void main() +{ + vec2 c; + float n; + + c.x = u_real_start + float(gl_FragCoord.x) / float(u_width) * (u_real_end - u_real_start); + c.y = u_imag_start + float(gl_FragCoord.y) / float(u_height) * (u_imag_end - u_imag_start); + + + if (u_samples == 1.0) + out_color = mandelbrot_color(c); else - out_color = texture1D(u_texture, float(n) / float(u_iterations)); + out_color = supersample_grid(c); } diff --git a/src/event.c b/src/event.c index 252f9d3..9d384fc 100644 --- a/src/event.c +++ b/src/event.c @@ -8,15 +8,15 @@ static void st_set_key(SDL_Keycode sym, bool value); static void st_apply_keys(State *state); static bool g_key_states[] = { - [KEY_UP] = false, - [KEY_DOWN] = false, + [KEY_UP] = false, + [KEY_DOWN] = false, [KEY_RIGHT] = false, - [KEY_LEFT] = false, + [KEY_LEFT] = false, [KEY_INC_ITERATIONS] = false, [KEY_DEC_ITERATIONS] = false, - [KEY_ZOOM_IN] = false, + [KEY_ZOOM_IN] = false, [KEY_ZOOM_OUT] = false, }; @@ -33,7 +33,18 @@ void event_handle(State *state) break; case SDL_KEYDOWN: - st_set_key(e.key.keysym.sym, true); + if (e.key.keysym.sym == SDLK_s) + state->smooth = !state->smooth; + else if (e.key.keysym.sym == SDLK_w) + state->samples += 1.0; + else if (e.key.keysym.sym == SDLK_q) + { + state->samples -= 1.0; + if (state->samples <= 0.0) + state->samples = 1.0; + } + else + st_set_key(e.key.keysym.sym, true); break; case SDL_KEYUP: diff --git a/src/shader.c b/src/shader.c index 68295ab..52c8df8 100644 --- a/src/shader.c +++ b/src/shader.c @@ -30,6 +30,8 @@ bool shader_init(Shader *shader) || (shader->location.imag_start = st_get_location(shader->id, "u_imag_start")) == -1 || (shader->location.imag_end = st_get_location(shader->id, "u_imag_end")) == -1 || (shader->location.iterations = st_get_location(shader->id, "u_iterations")) == -1 + || (shader->location.smooth = st_get_location(shader->id, "u_smooth")) == -1 + || (shader->location.samples = st_get_location(shader->id, "u_samples")) == -1 || (shader->location.texture = st_get_location(shader->id, "u_texture")) == -1) return false; return true; @@ -55,6 +57,10 @@ void shader_set_uniforms(Shader *shader, State *state) GL_CALL(glUniform1i(shader->location.iterations, state->iterations)); + GL_CALL(glUniform1i(shader->location.smooth, state->smooth)); + + GL_CALL(glUniform1f(shader->location.samples, state->samples)); + GL_CALL(glUniform1i(shader->location.texture, 0)); GL_CALL(glActiveTexture(GL_TEXTURE0)); GL_CALL(glBindTexture(GL_TEXTURE_1D, state->texture)); diff --git a/src/state.c b/src/state.c index 37eeccc..f661de6 100644 --- a/src/state.c +++ b/src/state.c @@ -55,6 +55,8 @@ bool state_init(State *state) state->imag_start = -2.0; state->imag_end = 2.0; state->running = true; + state->smooth = false; + state->samples = 1.0; return true; } |
