![]() |
are you ready for a mindfuck? |
|
....::::Menu::::.... ---------------------------...::About::... ...::Articles::... ...::Contact::... ...::Home & News::... ...::Links & Credits::... --------------------------- --------------------------- |
Circleby Elie De BrauwerGoal of this fileThe goal of this articles is to give you an introduction to drawing 2 dimensional figures using lines and some mathematics. History of this file
The concept
In the introduction we mentioned that OpenGL works on a low level. Now it even works on such a low level
that no function exists for drawing something simple like a circle. So when we need a circle we are on our
own. In this article I will draw a circle composed from lines. If you create an approximation of a circle
using three lines you get a triangle, if you use four you get a square and if you use an infinite number of
lines you get a perfect circle. But when drawing a circle on a computer screen inifinite comes close to thirtyfive
(if you window isn't too large).
Let's assume we have a circle with radius L, now starting from the centerpoint (in the center of the plane
with coordinates 0,0). Now all points have the coordinates (L*cos(alpha),L*sin(alpha)) for alpha between
0 and 360 degrees of between 0 and 2*Pi radians. The circle in codeC/C++ provides us with the sine and consine functions so we don't need to define them ourself, we only need to include the cmath header.We determine the points that lie on a circle by using the following algorithm
FOR(i=0;i<NUM_LINES;i++){
xcoord = l * cos(i*2*PI/NUM_LINES);
ycoord = l * sin(i*2*PI/NUM_LINES);
}
Suppose NUM_LINES is three then a point will occur at an angle 0,120 and 240 (degrees). Note that the sine and cosine
functions take there paramters in radians [0,2*PI] instead of degrees. l is the radius of the circle. If we should draw it like
the result would be a symmetrical figure around the the x-axis. Vertices in OpenGLThis part is of major importance within OpenGL programming. Until now we only used predefined shapes like glutSolidTorus(), glutWireCube() and glRectf(). But these predefined functions won't get your very far. Now we'll learn to create our own volumes by specifying there vertices. First we tell OpenGL we want to begin to define a shape. We do this by calling glBegin(), we end it by calling glEnd(). Only glBegin() takes a parameter, this parameter defines what we want to draw we have the following options:
The following is a quoted from the glBegin manpage:
Only a subset of GL commands can be used between glBegin and glEnd.
The commands are glVertex, glColor, glIndex, glNormal, glTexCoord,
glEvalCoord, glEvalPoint, glArrayElement, glMaterial, and glEdgeFlag.
Also, it is acceptable to use glCallList or glCallLists to execute dis-
play lists that include only the preceding commands. If any other GL
command is executed between glBegin and glEnd, the error flag is set
and the command is ignored.
The meaning of these other commands and what displaylists are and such will be defined in other articles. We are only at the beginning know and explaining some of these would be overkill. Currently we will only use commands of the following form: // shape 1 glBegin(GLenum mode); // collection of calls to glVertex() and all of it's flavors glVertex3f(); glEnd(); ... // shape n glBegin(GLenum mode); // collection of calls to glVertex() and all of it's flavors glVertex3f(); glEnd(); Now the glVertex*() (a function called glVertex*() means 'all flavors of the glVertex' like glVertex2d(), glVertex2f(), glVertex3d(),glVertex3dv(),... ) function defines a vertex (a vertex is nothing more that a point in space with the difference you can attache some options to it like a color for example). Double bufferingThis is also the first example where double buffering is used. Double buffering is nothing more than using two virtual screens. One that is visible to the user and one that is used by the computer to draw vertices on. When the computer is finished drawing the two screens are swtiched, and the used views the new drawing and the computer can start with a new drawing. We use double buffering in glut by logically or'ing GLUT_DOUBLE with the glutInitDisplayMode(), call. We should switch the buffers at the end of the display callback function, we do this by calling void glutSwapBuffers(void). To view the difference between double and single buffering, compile the sourcecode, replace glutSwapBuffers() by glFlush() and replace GLUT_DOUBLE by GLUT_SINGLE (these two can't be used together). Now start the programs and keep the '+' pressed. Once you draw enought line segments you will see the circle 'being built'. You should use double buffering everytime when animation is used. Putting it all togetherIn the next code segment we use the reshape function discussed in the
hello article. We don't need to change the coordinate system
(gluOrtho2d(), glOrtho()) since the default of an OpenGL window is that it's coordinates are
mapped to [-1,1] for x and y. This means that a circle with radius one first perfectly into
a default window.
#include<iostream>
#include<cmath>
#include<GL/glut.h>
using namespace std;
typedef unsigned char uchar;
// number of line segments
static int num_lines = 3;
// callback prototypes
void disp(void);
void keyb(uchar k, int x, int y);
void reshape(int x, int y);
////////////////////////////////
// main
int main(int argc, char **argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(400,400);
glutInitWindowPosition(100,100);
glutCreateWindow("circle.cpp");
glClearColor(0.0,0.0,0.0,0.0);
glutDisplayFunc(disp);
glutKeyboardFunc(keyb);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
////////////////
// disp
void disp(void){
double angle;
glClear(GL_COLOR_BUFFER_BIT);
// identation like this is nice for glBegin() and glEnd()
// but emacs doesn't support it.
glBegin(GL_LINE_LOOP);
for(int i =0;i<num_lines;i++){
// M_PI defined in cmath.h
angle = i*2*M_PI/num_lines;
// we use vertex2f since we are currently in working
// in 2d.
glVertex2f(cos(angle),sin(angle));
// we don't need to multiply by the length since the
// radius is 1.
}
glEnd();
glutSwapBuffers();
}
///////////////////////////////////
// keyb
void keyb(uchar k, int x, int y){
switch (k){
case 'q':
exit(0);
break;
case '+':
if(num_lines < 99){
num_lines++;
cout << "Circle consists of " << num_lines << " lines " << endl;
glutPostRedisplay();
}
break;
case '-':
if(num_lines >3){
num_lines--;
cout << "Circle consists of " << num_lines << " lines " << endl;
glutPostRedisplay();
}
break;
}
}
//////////////////////////
// reshape
void reshape(int x,int y){
/*
we use the reshape2 function out of the
hello article but we skip the glOrtho2D call
because when we don't define it the defaults are
used, this is a window within the following ranges
[-1,1] for x and for y (0,0) in the center of the
screen and this is exactly what we want
*/
if(x<y)
glViewport(0,(y-x)/2,x,x);
else
glViewport((x-y)/2,0,y,y);
}
You can also download the sourcode here Snapshots
Circle drawn using 4 line segments (a square that is)
Circle drawn using 14 line segments
Circle drawn using 35 line segments, this makes in this resolution a rather nice circle. |