├── .gitignore ├── README ├── README.md ├── chaos_game ├── main.cpp └── makefile ├── dragon_curve ├── main.cpp └── makefile ├── hilbert_curve ├── main.cpp └── makefile ├── koch_snowflake ├── main.cpp └── makefile ├── logistic_bifurcation ├── main.cpp └── makefile └── menger_sponge ├── main.cpp └── makefile /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | */a.out -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | THIS REPOSITORY IS DEPRECATED. 2 | THE NEW VERSION CAN BE FOUND AT https://gitlab.com/carterturn/opengl_fractals 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OpenGL Fractals 2 | =============== 3 | 4 | This is a collection of C++ fractal generating programs using OpenGL with GLFW for display. 5 | 6 | None of the code is optimized yet, but most of it will run acceptably fast with the number of iterations visible. 7 | 8 | The Dragon and Hilbert Curves and the Koch Snowflake are generated by following the path along the curve, modifying the curve as the depth of the fractal increases. 9 | 10 | The Sierpinski Triangle is generated via the "Chaos Game" method. 11 | 12 | The Menger Sponge is generated by drawing the "holes." "Holes" that are inside other holes are also drawn, this allows the grid to be generated without making decisions. 13 | 14 | The Logistic Bifurcation is generated by running the calculation and plotting the last 5% of the values. 15 | -------------------------------------------------------------------------------- /chaos_game/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define WIDTH 768 10 | #define HEIGHT 768 11 | 12 | using namespace std; 13 | 14 | struct point{ 15 | double x; 16 | double y; 17 | }; 18 | 19 | int main(int argc, char * argv[]){ 20 | 21 | int depth = 131072; 22 | if(argc > 1){ 23 | depth = atoi(argv[1]); 24 | } 25 | 26 | glfwInit(); 27 | GLFWwindow * window = glfwCreateWindow(WIDTH, HEIGHT, "Chaos Game!", NULL, NULL); 28 | 29 | if(!window){ 30 | return -1; 31 | } 32 | 33 | glfwMakeContextCurrent(window); 34 | glOrtho(-WIDTH/2, WIDTH/2, 0, HEIGHT, -1.0, 1.0); 35 | 36 | int num_corners = 3; 37 | point corners[num_corners]; 38 | corners[0].x = 0; 39 | corners[0].y = HEIGHT; 40 | corners[1].x = WIDTH/2; 41 | corners[1].y = 0; 42 | corners[2].x = -WIDTH/2; 43 | corners[2].y = 0; 44 | 45 | default_random_engine rand; 46 | uniform_real_distribution height_dist(0.0, (double) HEIGHT); 47 | uniform_real_distribution width_dist((double) -WIDTH/2.0, (double) WIDTH/2.0); 48 | uniform_int_distribution corner_dist(0, num_corners-1); 49 | 50 | while(true){ 51 | glClear(GL_COLOR_BUFFER_BIT); 52 | glBegin(GL_POINTS); 53 | chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); 54 | point p; 55 | p.x = width_dist(rand); 56 | p.y = height_dist(rand); 57 | for(int i = 0; i < depth; i++){ 58 | int corner = corner_dist(rand); 59 | p.x = p.x - (p.x - corners[corner].x) / 2.0; 60 | p.y = p.y - (p.y - corners[corner].y) / 2.0; 61 | 62 | if(i > 32){ 63 | if(p.y > HEIGHT/2){ 64 | glColor3f(0.0f, 1.0f, 0.0f); 65 | } 66 | else{ 67 | if(p.x > 0){ 68 | glColor3f(1.0f, 0.0f, 0.0f); 69 | } 70 | else{ 71 | glColor3f(0.0f, 0.0f, 1.0f); 72 | } 73 | } 74 | glVertex2i(p.x, p.y); 75 | } 76 | } 77 | cout << chrono::duration_cast> 78 | (chrono::high_resolution_clock::now() - start).count() << "\n"; 79 | glEnd(); 80 | glfwSwapBuffers(window); 81 | this_thread::sleep_for(chrono::milliseconds(100)); 82 | } 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /chaos_game/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ main.cpp -lGL -lglfw 3 | -------------------------------------------------------------------------------- /dragon_curve/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define WIDTH 768 10 | #define HEIGHT 768 11 | 12 | #define PI 3.14159265358 13 | 14 | using namespace std; 15 | 16 | struct point{ 17 | double x; 18 | double y; 19 | }; 20 | 21 | int main(int argc, char * argv[]){ 22 | 23 | int max_depth = 10; 24 | if(argc > 1){ 25 | max_depth = atoi(argv[1]); 26 | } 27 | 28 | glfwInit(); 29 | GLFWwindow * window = glfwCreateWindow(WIDTH, HEIGHT, "Dragon Curve!", NULL, NULL); 30 | 31 | if(!window){ 32 | return -1; 33 | } 34 | 35 | glfwMakeContextCurrent(window); 36 | glOrtho(0, WIDTH, 0, HEIGHT, -1.0, 1.0); 37 | 38 | while(true){ 39 | glClear(GL_COLOR_BUFFER_BIT); 40 | glColor3f(0.0f, 1.0f, 0.0f); 41 | glBegin(GL_LINES); 42 | chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); 43 | vector points; 44 | 45 | points.push_back({.x = WIDTH/4.0, .y = HEIGHT/2.0}); 46 | points.push_back({.x = WIDTH/2.0, .y = HEIGHT/4.0}); 47 | points.push_back({.x = WIDTH*3.0/4.0, .y = HEIGHT/2.0}); 48 | double length = sqrt((WIDTH * WIDTH / 16.0) + (HEIGHT * HEIGHT / 16.0)); 49 | for(int d = 0; d < max_depth; d++){ 50 | length = length / sqrt(2.0); 51 | vector new_points; 52 | for(int i = 0; i < points.size() - 1; i++){ 53 | double x1 = points[i].x; 54 | double y1 = points[i].y; 55 | double theta = atan2(points[i+1].y - y1, points[i+1].x - x1); 56 | double length = sqrt(pow(points[i+1].x - x1, 2.0) + pow(points[i+1].y - y1, 2.0)) / sqrt(2.0); 57 | double delta_angle = ((i % 2) == 0 ? (PI / 4.0) : -(PI / 4.0)); 58 | double rx = x1 + length * cos(theta + delta_angle); 59 | double ry = y1 + length * sin(theta + delta_angle); 60 | new_points.push_back(points[i]); 61 | new_points.push_back({.x = rx, .y = ry}); 62 | } 63 | new_points.push_back(points[points.size() - 1]); 64 | points = new_points; 65 | } 66 | glVertex2i(points[0].x, points[0].y); 67 | for(int i = 1; i < points.size() - 1; i++){ 68 | glVertex2i(points[i].x, points[i].y); 69 | glVertex2i(points[i].x, points[i].y); 70 | } 71 | glVertex2i(points[points.size()-1].x, points[points.size()-1].y); 72 | 73 | cout << chrono::duration_cast> 74 | (chrono::high_resolution_clock::now() - start).count() << "\n"; 75 | glEnd(); 76 | glfwSwapBuffers(window); 77 | this_thread::sleep_for(chrono::milliseconds(100)); 78 | } 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /dragon_curve/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ main.cpp -lGL -lglfw 3 | -------------------------------------------------------------------------------- /hilbert_curve/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define WIDTH 768 10 | #define HEIGHT 768 11 | 12 | using namespace std; 13 | 14 | struct point{ 15 | double x; 16 | double y; 17 | }; 18 | 19 | int main(int argc, char * argv[]){ 20 | 21 | int max_depth = 6; 22 | if(argc > 1){ 23 | max_depth = atoi(argv[1]); 24 | } 25 | 26 | glfwInit(); 27 | GLFWwindow * window = glfwCreateWindow(WIDTH, HEIGHT, "Hilbert Curve!", NULL, NULL); 28 | 29 | if(!window){ 30 | return -1; 31 | } 32 | 33 | glfwMakeContextCurrent(window); 34 | glOrtho(0, WIDTH, 0, HEIGHT, -1.0, 1.0); 35 | 36 | while(true){ 37 | glClear(GL_COLOR_BUFFER_BIT); 38 | glColor3f(0.0f, 1.0f, 0.0f); 39 | glBegin(GL_LINES); 40 | chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); 41 | 42 | vector points; 43 | points.push_back({.x = WIDTH/4.0, .y = HEIGHT/4.0}); 44 | points.push_back({.x = WIDTH/4.0, .y = HEIGHT*3.0/4.0}); 45 | points.push_back({.x = WIDTH*3.0/4.0, .y = HEIGHT*3.0/4.0}); 46 | points.push_back({.x = WIDTH*3.0/4.0, .y = HEIGHT/4.0}); 47 | 48 | for(int d = 0; d < max_depth; d++){ 49 | vector new_points; 50 | 51 | for(int i = 0; i < points.size(); i += 4){ 52 | if(abs(points[i+1].x - points[i].x) < abs(points[i+1].y - points[i].y)){ 53 | double offset = (points[i+1].y - points[i].y) / 4.0; 54 | 55 | double x = points[i].x; 56 | double y = points[i].y; 57 | new_points.push_back({.x = x - offset, .y = y - offset}); 58 | new_points.push_back({.x = x + offset, .y = y - offset}); 59 | new_points.push_back({.x = x + offset, .y = y + offset}); 60 | new_points.push_back({.x = x - offset, .y = y + offset}); 61 | 62 | x = points[i+1].x; 63 | y = points[i+1].y; 64 | new_points.push_back({.x = x - offset, .y = y - offset}); 65 | new_points.push_back({.x = x - offset, .y = y + offset}); 66 | new_points.push_back({.x = x + offset, .y = y + offset}); 67 | new_points.push_back({.x = x + offset, .y = y - offset}); 68 | 69 | x = points[i+2].x; 70 | y = points[i+2].y; 71 | new_points.push_back({.x = x - offset, .y = y - offset}); 72 | new_points.push_back({.x = x - offset, .y = y + offset}); 73 | new_points.push_back({.x = x + offset, .y = y + offset}); 74 | new_points.push_back({.x = x + offset, .y = y - offset}); 75 | 76 | x = points[i+3].x; 77 | y = points[i+3].y; 78 | new_points.push_back({.x = x + offset, .y = y + offset}); 79 | new_points.push_back({.x = x - offset, .y = y + offset}); 80 | new_points.push_back({.x = x - offset, .y = y - offset}); 81 | new_points.push_back({.x = x + offset, .y = y - offset}); 82 | } 83 | else{ 84 | double offset = (points[i+1].x - points[i].x) / 4.0; 85 | 86 | double x = points[i].x; 87 | double y = points[i].y; 88 | new_points.push_back({.x = x - offset, .y = y - offset}); 89 | new_points.push_back({.x = x - offset, .y = y + offset}); 90 | new_points.push_back({.x = x + offset, .y = y + offset}); 91 | new_points.push_back({.x = x + offset, .y = y - offset}); 92 | 93 | x = points[i+1].x; 94 | y = points[i+1].y; 95 | new_points.push_back({.x = x - offset, .y = y - offset}); 96 | new_points.push_back({.x = x + offset, .y = y - offset}); 97 | new_points.push_back({.x = x + offset, .y = y + offset}); 98 | new_points.push_back({.x = x - offset, .y = y + offset}); 99 | 100 | x = points[i+2].x; 101 | y = points[i+2].y; 102 | new_points.push_back({.x = x - offset, .y = y - offset}); 103 | new_points.push_back({.x = x + offset, .y = y - offset}); 104 | new_points.push_back({.x = x + offset, .y = y + offset}); 105 | new_points.push_back({.x = x - offset, .y = y + offset}); 106 | 107 | x = points[i+3].x; 108 | y = points[i+3].y; 109 | new_points.push_back({.x = x + offset, .y = y + offset}); 110 | new_points.push_back({.x = x + offset, .y = y - offset}); 111 | new_points.push_back({.x = x - offset, .y = y - offset}); 112 | new_points.push_back({.x = x - offset, .y = y + offset}); 113 | } 114 | } 115 | 116 | points = new_points; 117 | } 118 | glVertex2i(points[0].x, points[0].y); 119 | for(int i = 1; i < points.size() - 1; i++){ 120 | glVertex2i(points[i].x, points[i].y); 121 | glVertex2i(points[i].x, points[i].y); 122 | } 123 | glVertex2i(points[points.size()-1].x, points[points.size()-1].y); 124 | 125 | cout << chrono::duration_cast> 126 | (chrono::high_resolution_clock::now() - start).count() << "\n"; 127 | glEnd(); 128 | glfwSwapBuffers(window); 129 | this_thread::sleep_for(chrono::milliseconds(100)); 130 | } 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /hilbert_curve/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ main.cpp -lGL -lglfw 3 | -------------------------------------------------------------------------------- /koch_snowflake/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define WIDTH 768 10 | #define HEIGHT 768 11 | 12 | using namespace std; 13 | 14 | struct point{ 15 | double x; 16 | double y; 17 | }; 18 | 19 | int main(int argc, char * argv[]){ 20 | 21 | int max_depth = 1; 22 | if(argc > 1){ 23 | max_depth = atoi(argv[1]); 24 | } 25 | 26 | glfwInit(); 27 | GLFWwindow * window = glfwCreateWindow(WIDTH, HEIGHT, "Koch Snowflake!", NULL, NULL); 28 | 29 | if(!window){ 30 | return -1; 31 | } 32 | 33 | glfwMakeContextCurrent(window); 34 | glOrtho(0, WIDTH, 0, HEIGHT, -1.0, 1.0); 35 | 36 | double side_length = (HEIGHT * 3.0/2.0) / sqrt(3.0); 37 | double width_offset = (WIDTH - side_length) / 2.0; 38 | 39 | while(true){ 40 | glClear(GL_COLOR_BUFFER_BIT); 41 | glColor3f(0.0f, 1.0f, 0.0f); 42 | glBegin(GL_LINES); 43 | chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); 44 | vector points; 45 | 46 | points.push_back({.x = width_offset, .y = HEIGHT/4.0}); 47 | points.push_back({.x = WIDTH - width_offset, .y = HEIGHT/4.0}); 48 | points.push_back({.x = WIDTH/2, .y = HEIGHT}); 49 | for(int d = 0; d < max_depth; d++){ 50 | vector new_points; 51 | for(int i = 0; i < points.size(); i++){ 52 | double x1 = points[i].x; 53 | double x2 = points[(i+1)%points.size()].x; 54 | double y1 = points[i].y; 55 | double y2 = points[(i+1)%points.size()].y; 56 | double rx1 = (x2 - x1) / 3.0 + x1; 57 | double ry1 = (y2 - y1) / 3.0 + y1; 58 | double rx2 = 2.0 * (x2 - x1) / 3.0 + x1; 59 | double ry2 = 2.0 * (y2 - y1) / 3.0 + y1; 60 | new_points.push_back(points[i]); 61 | new_points.push_back({.x = rx1, .y = ry1}); 62 | if(abs(ry1 - ry2) < 0.0000001){ 63 | new_points.push_back({.x = (rx2 - rx1) / 2.0 + rx1, 64 | .y = ry1 + (rx1 - rx2) * sqrt(3.0) / 2.0}); 65 | } 66 | else if((x2 > x1) == (y1 > y2)){ 67 | new_points.push_back({.x = rx1 + (rx1 - rx2), .y = ry2}); 68 | } 69 | else{ 70 | new_points.push_back({.x = rx2 + (rx2 - rx1), .y = ry1}); 71 | } 72 | new_points.push_back({.x = rx2, .y = ry2}); 73 | } 74 | points = new_points; 75 | } 76 | glVertex2i(points[points.size()-1].x, points[points.size()-1].y); 77 | for(int i = 0; i < points.size(); i++){ 78 | glVertex2i(points[i].x, points[i].y); 79 | glVertex2i(points[i].x, points[i].y); 80 | } 81 | glVertex2i(points[0].x, points[0].y); 82 | cout << chrono::duration_cast> 83 | (chrono::high_resolution_clock::now() - start).count() << "\n"; 84 | glEnd(); 85 | glfwSwapBuffers(window); 86 | this_thread::sleep_for(chrono::milliseconds(100)); 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /koch_snowflake/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ main.cpp -lGL -lglfw 3 | -------------------------------------------------------------------------------- /logistic_bifurcation/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define WIDTH 768 10 | #define HEIGHT 768 11 | 12 | using namespace std; 13 | 14 | int main(int argc, char * argv[]){ 15 | 16 | int max_depth = 2048; 17 | 18 | glfwInit(); 19 | GLFWwindow * window = glfwCreateWindow(WIDTH, HEIGHT, "Logistic Bifurcation!", NULL, NULL); 20 | 21 | if(!window){ 22 | return -1; 23 | } 24 | 25 | glfwMakeContextCurrent(window); 26 | glOrtho(0, WIDTH, 0, HEIGHT, -1.0, 1.0); 27 | 28 | while(true){ 29 | glClear(GL_COLOR_BUFFER_BIT); 30 | glColor3f(0.0f, 1.0f, 0.0f); 31 | glBegin(GL_POINTS); 32 | chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); 33 | 34 | for(int i = 0; i < WIDTH; i++){ 35 | double r = 4.0 * double(i) / ((double) WIDTH); 36 | double x = 0.6; 37 | for(int d = 0; d < max_depth; d++){ 38 | x = r * x * (1 - x); 39 | if(d >= max_depth * 0.95){ 40 | glVertex2i(i, x * HEIGHT); 41 | } 42 | } 43 | } 44 | 45 | cout << chrono::duration_cast> 46 | (chrono::high_resolution_clock::now() - start).count() << "\n"; 47 | glEnd(); 48 | glfwSwapBuffers(window); 49 | this_thread::sleep_for(chrono::milliseconds(100)); 50 | } 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /logistic_bifurcation/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ main.cpp -lGL -lglfw 3 | -------------------------------------------------------------------------------- /menger_sponge/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define WIDTH 768 10 | #define HEIGHT 768 11 | 12 | using namespace std; 13 | 14 | int three_pow(int exp){ 15 | int num = 1; 16 | 17 | for(int i = 0; i < exp; i++){ 18 | num *= 3; 19 | } 20 | 21 | return num; 22 | } 23 | 24 | int main(int argc, char * argv[]){ 25 | 26 | int max_depth = 5; 27 | 28 | glfwInit(); 29 | GLFWwindow * window = glfwCreateWindow(WIDTH, HEIGHT, "Menger Sponge!", NULL, NULL); 30 | 31 | if(!window){ 32 | return -1; 33 | } 34 | 35 | glfwMakeContextCurrent(window); 36 | glOrtho(0, WIDTH, 0, HEIGHT, -1.0, 1.0); 37 | 38 | while(true){ 39 | glClear(GL_COLOR_BUFFER_BIT); 40 | glColor3f(0.0f, 1.0f, 0.0f); 41 | glBegin(GL_QUADS); 42 | chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); 43 | for(int d = 0; d < max_depth; d++){ 44 | double depth = (double) three_pow(d); 45 | for(int i = 0; i < depth; i++){ 46 | double x_step = WIDTH / depth; 47 | for(int j = 0; j < depth; j++){ 48 | double y_step = HEIGHT / depth; 49 | double center_x = x_step*double(i) + x_step/2.0; 50 | double center_y = y_step*double(j) + y_step/2.0; 51 | glVertex2i(center_x + x_step/6.0, center_y + y_step/6.0); 52 | glVertex2i(center_x + x_step/6.0, center_y - y_step/6.0); 53 | glVertex2i(center_x - x_step/6.0, center_y - y_step/6.0); 54 | glVertex2i(center_x - x_step/6.0, center_y + y_step/6.0); 55 | } 56 | } 57 | } 58 | cout << chrono::duration_cast> 59 | (chrono::high_resolution_clock::now() - start).count() << "\n"; 60 | glEnd(); 61 | glfwSwapBuffers(window); 62 | this_thread::sleep_for(chrono::milliseconds(100)); 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /menger_sponge/makefile: -------------------------------------------------------------------------------- 1 | build: 2 | g++ main.cpp -lGL -lglfw 3 | --------------------------------------------------------------------------------