are you ready for a mindfuck ? are you ready for a mindfuck?

....::::Menu::::....
...::About::...
...::Articles::...
...::Contact::...
...::Home & News::...
...::Links & Credits::... OpenGL logo
Valid XHTML 1.0!

coil

by Elie De Brauwer

Goal of this file

In this file we go another little step further and we will draw some coil alike structures. This can be seen as an extension of the circle and the spiral programs into the third dimension. We will also implement the axis we saw in a a previous article. And we will make some variations on this theme.

History of this file

  • 30 october 2003: created
  • 9 november 2003: continued & finished

First contact

Within our current project we are starting to do some object oriented C++ programming so I assume you already have some knowledge about it, if that is not the case I strongly suggest some googling but on the otherhand the syntax is not that hard and you should be able to follow this (easy example). Let us first take a look at our main program. This basicly consists of nothing. We open a window, define a Coil object, define an idlefunc and define a displayfunc.The only difficult thing is drawing the axis which is explained in an older text so nothing new appears here. Now I present you the code:

#include <GL/glut.h>
#include <iostream>
#include "coil.h"
using namespace std;

void disp(void);
void idle(void);
void drawAxis(void);

static Coil *coil;

int main(int argc,char **argv){

  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutCreateWindow("Coil");
  glClearColor(0.0,0.0,0.0,0.0);

  coil = new Coil(0.2,0.1,10,90);
  glutDisplayFunc(disp);
  glutIdleFunc(idle);
  glutMainLoop();
}



void disp(void){
  glClear(GL_COLOR_BUFFER_BIT);
  glPushMatrix();
  glColor3f(1.0,1.0,1.0);
  coil->draw();
  drawAxis();
  glPopMatrix();
  glutSwapBuffers();
}

void drawAxis(){
  glBegin(GL_LINES);
  glColor3f(1.0,0.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.80,0.0,0.0);
  glColor3f(0.0,1.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.80,0.0);
  glColor3f(0.0,0.0,1.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.0,0.80);
  glEnd();

  glPushMatrix();
  glColor3f(1.0,0.0,0.0);
  glTranslatef(0.80,0.0,0.0);
  glRotatef(90,0.0,1.0,0.0);
  glutSolidCone(0.05,0.1,10,10);
  glPopMatrix();
  glPushMatrix();
  glColor3f(0.0,1.0,0.0);
  glTranslatef(0.0,0.80,0.0);
  glRotatef(-90,1.0,0.0,0.0);
  glutSolidCone(0.05,0.1,10,10);
  glPopMatrix();
  glPushMatrix();
  glColor3f(0.0,0.0,1.0);
  glTranslatef(0.0,0.0,0.80);
  glutSolidCone(0.05,0.1,10,10);
  glPopMatrix();

}

void idle(void){
  coil->setAngle(coil->getAngle()+1);
  glutPostRedisplay();
}

The code is also available. Now what is missing ? Everything that has anything to do with the coil we are willing to draw. Please note that the glPushMatrix() and glPopMatrix() are here so we don't need to call a glLoadIdentity() in the Coil::draw(); method.

Drawing the coil & parametric curves

In the previous code snippet we have seen we need a coil.h file which contains the definition of the coil. In that file we must have a constructor, a draw method,a getAngle method and a setAngle method. Internally some other functions are implemented which are not used from the main program. Now how do we define, do we draw this coil ?
Well the the principle we use for drawing this coil lies within the area of parametric curves. The coil we are drawing can be seen as a mathematical function where x=f(angle) and y=f(angle) and z=f(angle). So all three coordinates can be written as a function of the angle. In fact this is nothing more than a spiral with a linear growing z coordinate. Of entire coil.h file, the only important function is the void Coil::draw(void) function which does the actual drawing using parametric curves. So I won't put the constructor and the modifiers below but only the draw function. If you would be interested in the other member function you can always download the entire source

void Coil::draw(void) const{
  double angle;
  // create zstart to make it symmetrical around the origin
  double zstart = -(rounds*dz)/2.0;
  double z;
  glRotatef(this->angle,0.0,1.0,0.0);
  glBegin(GL_LINE_STRIP);
  for(int i = 0; i<rounds;i++){
    for(int j =0;j<NUM_LINES_A_LOOP;j++){
      // M_PI defined in cmath.h
      angle = j*2.0*M_PI/NUM_LINES_A_LOOP;
      z = zstart + i*dz + dz*angle/(2.0*M_PI);
      // we use vertex3f since we are currently in working in 3d 
      glVertex3f(radius*cos(angle),radius*sin(angle),z);
    }
  }
  glEnd();
}

The source of coil.h

Variation on this theme

For those with an interest in there parametric curves try to modify the code so the following results are shown:

  • Let the radius of the coil increase/decrease in a lineair way
  • Try to create a function that results in a cone
  • Modify the radius so that it follows ln(x) and or e^x
  • Replace the circle shape in the XZ plane by something else like a square or an ellipse

Projection transformation

OpenGL support by default 2 projection transformations, the Ortographic projection and the Perspective projection. The first is the default and is used for blueprints and second is used for more realistic visualisation.

Ortographic projection

Now take a closer look at the two snapshots of the result. The blue axis is the Z-axis. You notice that when you look in the direction of the Z-axis (snapshot2) you see no coil, you only see a circle. If you would try this in real life you would see more than just a circle. This is because in OpenGL you are by default using Ortographic projection. In if an object is right behind another object (like our rotated coil) you will only see the front this is because everything is projected perpendicular on the near clipping plane. The viewing volume is a rectangular parallelepiped. The size of the object will remains constant and is not dependant on the distance of the camera. If you see a large tree far far away it appears smaller than a small person standing near to you. In real life (looking) you don't use ortographic projection. Ortographic projection is used in CAD application like AutoCAD or QCAD, it is used for creating blueprints and maps.
The parameters of viewing volume can be modified by using the glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val) function call wich modifies the values of the left,right, bottom, top, near and far clipping plane. If you are only working in 2 dimensions you can use gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top) which does the same but fixes the Z clipping planes to -1 and 1.
You might mention that we use ortographic projection in this example but you can't find a call to glOrtho or glutOrtho2D anywhere, that's because OpenGL uses by default Ortographic projection which is the same as a call to glOrtho(-1,1,-1,1,-1,1);

Rotated over 90 degrees
Not rotated

Perspective projection

The viewing volume in perspective projection is a piramid with the eye at the top. OpenGL offers two ways of manipulating this viewing plane. The first is a call to glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far). The meaning of these values will become clear if you take a look at the image below. The viewing volume is the volume between the black,red and blue lines.

glFrustum

A second method of defining the perspective projection is by using a calls to gluPespective(GLdouble fovy, GLdouble aspect, GLdouble near, GLdouble far). The fovy parameter is the viewing angle in the YZ plane, aspect is the aspect ration of the frustum, this is the width of the clipping plane divided by the height of the clipping plane. Again for more information look at the image below.

gluPerspective

I agree in the beginning these values may look a bit strange and 'how do I know how big that angle has to be ?'. Don't worry, just play a bit with the parameters until you get them right. It might be a little awkward in the beginning.
Your first idea is probably that gluPerspective is too complicated and that glFrustum is much more easy, just try it you will notice that gluPerspective is more intuitive.

A note on errors

Please note that all values for the near and far clipping plane are relative to the eye coordinates. And thus can not be negative values. If you should supply a negative value for you far or near clipping plane this will result in an error. But OpenGL does not abort when an error occurs, OpenGL is very stubborn and silently continues hoping everything will go away. That makes it a little hard debugging at first sight that's why there exists something like GLenum glGetError(void). This function call returns the value of the error flag. At this point there are three values that might be of any interest to you: GL_INVALID_VALUE occurs when one of your (near of far) clipping planes are negative, GL_STACK_OVERFLOW and GL_STACK_UNDERFLOW occur when you overflow or underflow your matrix stacks.
After a call to glGetError() the error flag is set to GL_NO_ERROR and it stays that way until an error occurs.
GLU also provides a more friendly approach to dislaying the error messages by using const GLubyte * gluErrorString(GLenum errorCode) which returns a pointer to a writable error string.
A sample function that writes out all error messages (except the GL_NO_ERROR can be the following:

void handleErr(void){
  static GLenum errCode;
  errCode = glGetError();
  if(errCode != GL_NO_ERROR){
    cout << gluErrorString(errCode) << endl;;
  }
}

It is recommended to check for errors at least once every display. This also happened in the next coil version coil2.cpp which also applies the perspective projection, see further.

Applying Perspective projection

Since our project now usese ortographic projection (just like any other project this far) we will give it a try in Perspective mode. To obtain the results below we added a reshape callback, in that callback we modify the GL_PROJECTION matrix, we multiple this matrix with the perspective projection matrix that is created by using the gluPerspective call. After that we switch back to the GL_MODELVIEW matrix and call a gluLookAt to define the eye coordinates. The code we added is the following reshape function:

void reshape(int x,int y){
  glViewport(0,0,x,y);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(50,(x*1.0)/(y*1.0),1,6);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(2.0,0,0,0,0,0,0,1,0);
}

And these are the results:

Rotated over 90 degrees in perspective

We don't see a circle anymore but a spiral. Take also a look at the cones. In the orthographic projection they looked like triangles. But if you look at the bottom of the cones now you can see they are more round now.

Not rotated in perspective

In the orthographic projection we saw a periodic function new we see a symmetric figure (symmetric around the origin).

Random scene in perspective

This image give a 3 dimensional impression.Here you can actually see that objects close to the eye coordinates are drawn larger than objects that are further away.

The final sourcecode can be downloaded here: Coil2.cpp and the class here: coil.h.