![]() |
are you ready for a mindfuck? | ||||||||||||||||||||||||
|
....::::Menu::::.... ---------------------------...::About::... ...::Articles::... ...::Contact::... ...::Home & News::... ...::Links & Credits::... --------------------------- --------------------------- |
fractalby Elie De BrauwerGoal of this fileEverybody has seen fractals somewhere and now we are going to make them. Altough it might seems complicated at first they end up being rather simnple to create. They don't require much higher math and the math in the stair article is more difficult than this one. You only need to now what complex numbers are and how the field of complex numbers is created. This article does not offer a complete introduction to complex numbers, so for easy reading I suggest that you at least know what a complex number is and how you multiply these. History of this file
What are fractals ?First take a look at wikipedia, this gives you a better explanation about fractals than any explanataion that I'm able to give you. In this article I'll provide you with an example application that is able to generate at least two fractals. These are called The links to wikipedia offer a general discription about the math behind fractals. But in short here is a summary:First we define a sequence: zn+1=zn2+c, in this sequence z is a complex number (thus having a real and a imaginairy part, looking like a+bj where j*j=-1 or in computer terms just call it (a,b)). For each pixel on your screen you define a starting complex number (z0) whose parts are the pixel (or a transformation of the pixel) you want to draw. No you develop this series. When n goes to infinity zn can go to zero or it can go to infinity. Now the number of iterations we can do before zk (where k is between 0 and infinity) results in zero (as an optimization in our algorithm we define zero as a circle with midpoint the origin and with radius 2) defines the color of the current pixel. Now the easy way to define the difference between a Mandelbrot set and a Julia set. If you define c as z0 (different for each point) you obtain a mandelbrot set, if you 'pick' a c as a complex number which is the same for each iteration you obtain a Julia set. Now that you know
First a little note on speed. Suppose you have a window with a size of
500 by 500 pixels and you use 256 iterations to determine the color, this means
that for one frame the inner loop does 64 million cycles. Keep this in mind
before calling it 'slow'. #include <GL/glut.h> #include <iostream> #include <cstdio> using namespace std; typedef unsigned char uchar; void disp(void); void reshape(int x,int y); void mouse(int button,int state, int x, int y); void keyb(uchar key,int x,int y); static int winx; static int winy; static double xbegin = -1.8; static double xend=1.1; static double ybegin = -1.2; static double yend=1.2; Above you can see the definition some global variables and the function prototypes. These variables are used to store the window dimensions and our place in the complex plane. The will be modified by the reshape and the mouse callback.
int main(int argc, char **argv){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutCreateWindow("Fractal");
glutInitWindowSize(600,600);
// clear the background to white so we can see any holes
glClearColor(1.0,1.0,1.0,0.0);
glutDisplayFunc(disp);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutKeyboardFunc(keyb);
glutMainLoop();
}
Yet another main function, with yet another double buffered window, the only difference is that we use white as the color to clear the window instead of black. This is because we are going to draw each pixel and 'black holes' are difficult to spot on a black background.
void disp(void){
glClear(GL_COLOR_BUFFER_BIT);
// begin definition
glBegin(GL_POINTS);
// for each pixel on the screen
for(float i=0;i<winx;i+=0.999){
for(float j=0;j<winy;j+=0.999){
// define z_0
double zIm = j*(yend-ybegin)/(winy*1.0)+ybegin;
double zRe = i*(xend-xbegin)/(winx*1.0)+xbegin;
// define c
// alter this sequence to create a Julia set
// e.g
// double cIm = 0.5;
// double cRe = 0.5;
double cIm = zIm;
double cRe = zRe;
// res is the iteration counter
double temp;
int res = 0;
// we take 255 iteration at max, resulting in 255 distinct
// blue's
while(res<255 && (zIm*zIm + zRe*zRe)<=4.0 ){
// z^2= (a+bj)*(a+bj)= a^2 - b^2 + j*(2*a*b)
// temp = zRe*zRe - zIm *zIm + i*(xend-xbegin)/(winx*1.0)+xbegin;
// zIm = 2*zRe * zIm + j*(yend-ybegin)/(winy*1.0)+ybegin;
temp = zRe*zRe - zIm *zIm + cRe;
zIm = 2*zRe * zIm + cIm;
zRe = temp;
res++;
}
// define the color which is the number of iterations
// here we link the number of iteration to the blue byte
glColor3f(0,0,(res*1.0)/255.0);
glVertex2f(i,j);
}
}
glEnd();
glutSwapBuffers();
}
The display function, all the magic happens here and I think I have some explaining to do now. First we clear the window, we say OpenGL that we want to draw some point (a whole lot of points). So basicly the code looks like:
glBegin(GL_POINTS);
for(all pixels on the screen){
determine the color
glColor3f(the color)
glVertex2f(the current pixel)
}
glEnd();
Easy isn't it ? The only odd thing you might notice is that in my for loop is use 0.999 as in increment. That is
because when using integers or floats and 1 as an increment there tend to exist white lines (see the white
background remarkt above), possible because some type mismatch or wrong roundoff but this fixes the problem
(at least I haven't seen anything while with 0.999 as in incremnt).
void keyb(uchar key, int x, int y){
if(key=='Q'||key=='q'){
exit(0);
}else if(key=='r'){
xbegin = -1.8;
xend=1.1;
ybegin = -1.2;
yend=1.2;
reshape(winx,winy);
}
}
We implement a 'reset' functionality and a quit functionality. The reset resets the initial boundaries (see initialisation).
void mouse(int button,int state,int x,int y){
if(state == GLUT_DOWN){
// remind the Y axis is point down and the X axis is pointing to the right
y = winy - y;
double dx = (xend-xbegin);
double dy = (yend-ybegin);
if(dx != 0 && dy != 0){
if(button == GLUT_LEFT){
xend = x * dx/winx +xbegin + dx/10;
xbegin = x * dx/winx +xbegin - dx/10;
yend = y * dy/winy +ybegin + dy/10;
ybegin = y * dy/winy +ybegin - dy/10;
}else{
xend = x * dx/winx +xbegin + 5*dx;
xbegin = x * dx/winx +xbegin - 5*dx;
yend = y * dy/winy +ybegin + 5*dy;
ybegin = y * dy/winy +ybegin - 5*dy;
}
glutPostRedisplay();
}else{
cout << "Underflow detected, zooming aborted" << endl;
cout << "Press 'r' to reset" << endl;
}
}
}
When you click you can zoom in or zoom out depending on the mouse button you pressed.
Zooming is nothing more than changing the boundaries, but fractals are
so fun and you can keep zooming so we should consider possible overflow. Below you can see yet another reshape function, nothing special except that we store the window size for further usage.
void reshape(int x,int y){
winx =x;
winy =y;
glViewport(0,0,x,y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,x,0,y);
glMatrixMode(GL_MODELVIEW);
glutPostRedisplay();
}
And again, the code can be found here.
And now, some eyecandy
In the endIf you've made your way thru this article you most probably have a fascination for these fractals. If you want to know more, find other c values, ask google and let google surprise you. |