rotating camera in OpenGL using Glut libraries and gluLookAt

3.3k views Asked by At

I am trying to implement a software in OpenGl which is able to draw a Lorenz system. I achieved my purpose but in a static way: the system is drawn once and that's all. Now I want to move my camera around the system and show the 3D-ness of the system itself. What I noticed is that I cannot update the drawn image because if I do update the points of the system, they keep changing in each update (Lorenz system is the result of mathematical equations, therefore I have big floats number as results). I then realized that I have to draw the system just once and then move the camera around it somehow. Unfortunately I don't know how to do it. I especially have problems in changing that gluLookAt call for my purposes. Let's say that I want to move the camera according to an input given by keyboard. Can you kindly help me? Here you can have a look to my simple code.

Initialization method:

void myinit() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glColor4f(1.0f, 0.0f, 0.0f, 0.09f);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_POINT_SMOOTH);
glPointSize(1.0f);

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Really Nice Perspective Calculations

glViewport(0, 0, 400, 400); //glViewport(0, 0, width_of_window_rendering_area, height_of_window_rendering area);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,  (GLfloat)400/400, 0.1, 100); //Sets the frustum to perspective mode, sets up the way in which objects

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

Drawing method

void mydisplay() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //if perspective
glLoadIdentity();
gluLookAt(0.0, 0.0, 100.0,  //position
          0.0, 0.0, 0.0,  //where we are looking
          0.0, 1.0, 0.0); //up vector

glBegin(GL_POINTS);
for (int i = 0; i < iterations; i++) {
    if(i == 200000){
        glColor4f(1.0f, 0.0f, 0.0f, 0.09f);
    }
    if(i == 400000){
        glColor4f(1.0f, 0.0f, 1.0f, 0.09f);
    }
    if(i == 600000){
        glColor4f(0.0f, 0.0f, 1.0f, 0.09f);
    }
    if(i == 800000){
        glColor4f(0.0f, 1.0f, 1.0f, 0.09f);
    }

    // compute a new point using the strange attractor equations
    float xnew=x + h*(s*(y - x));
    float ynew=y + h*(x*(p - z) - y);
    float znew=z + h*(x*y - b*z);

    x = xnew;
    y = ynew;
    z = znew;

    glVertex3f(x, y, z);
}
glEnd();

glutSwapBuffers();
}

main

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

glutInit(&argc,argv);

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

glutInitWindowSize(400, 400);
glutCreateWindow("Strange Attractors in C++ and OpenGL Tutorial");

glutDisplayFunc(mydisplay);
glutKeyboardFunc(mykey);

myinit();
glutMainLoop();

while(esc != true){
    glutDisplayFunc(mydisplay);
}
return 0;
}

This is the result:

prova

2

There are 2 answers

5
genpfault On BEST ANSWER

Use a timer callback to increment an angle and post a redraw:

#include <GL/glut.h>
#include <vector>

struct Vertex
{
    float x, y, z, w;
    float r, g, b, a;
};
std::vector< Vertex > verts;

void fillVerts()
{
    // calculate vertices
    // http://paulbourke.net/fractals/lorenz/
    double h = 0.01;
    double a = 10.0;
    double b = 28.0;
    double c = 8.0 / 3.0;

    Vertex cur;
    cur.a = 0.09f;

    double x0 = 0.1;
    double y0 = 0;
    double z0 = 0;
    for( unsigned int i = 0; i < 100000; i++ ) 
    {
        if(i == 20000)
        {
            cur.r = 1.0f;
            cur.g = 0.0f;
            cur.b = 0.0f;
        }
        if(i == 40000)
        {
            cur.r = 1.0f;
            cur.g = 0.0f;
            cur.b = 1.0f;
        }
        if(i == 60000)
        {
            cur.r = 0.0f;
            cur.g = 0.0f;
            cur.b = 1.0f;
        }
        if(i == 80000)
        {
            cur.r = 0.0f;
            cur.g = 1.0f;
            cur.b = 1.0f;
        }

        const double x1 = x0 + h * a * (y0 - x0);
        const double y1 = y0 + h * (x0 * (b - z0) - y0);
        const double z1 = z0 + h * (x0 * y0 - c * z0);
        x0 = x1;
        y0 = y1;
        z0 = z1;

        if( i > 100 )
        {
            cur.x = x0;
            cur.y = y0;
            cur.z = z0;
            verts.push_back( cur );
        }
    }
}

float angle = 0;
void timer( int extra )
{
    // spin
    angle += 0.5;

    glutPostRedisplay();
    glutTimerFunc( 16, timer, 0 );
}

void display(void)
{
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    const double w = glutGet( GLUT_WINDOW_WIDTH );
    const double h = glutGet( GLUT_WINDOW_HEIGHT );
    gluPerspective( 60.0, w / h, 1.0, 10000.0 );

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 70, 70, 70, 0, 0, 0, 0, 0, 1 );

    glRotatef( angle, 0, 0, 1 );

    // draw curve
    glEnableClientState( GL_VERTEX_ARRAY );
    glEnableClientState( GL_COLOR_ARRAY );
    glVertexPointer( 3, GL_FLOAT, sizeof( Vertex ), &verts[0].x );
    glColorPointer( 4, GL_FLOAT, sizeof( Vertex ), &verts[0].r );
    glDrawArrays( GL_LINE_STRIP, 0, verts.size() );
    glDisableClientState( GL_VERTEX_ARRAY );
    glDisableClientState( GL_COLOR_ARRAY );

    glutSwapBuffers();
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutInitWindowSize( 800,600 );
    glutCreateWindow( "Attractor" );

    glutDisplayFunc( display );
    glutTimerFunc( 0, timer, 0 );

    fillVerts();

    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

    glEnable( GL_POINT_SMOOTH );
    glPointSize(1.0f);

    glutMainLoop();
    return 0;
}

It's also a good idea to calculate the point positions/colors ahead of time instead of in the display callback.

2
jwlaughton On

I think you might find that in general using the glut functions as you are will turn out to be too slow to do useful animation.

I do something similar with a resolution of 1200 X 800 X 1200 pixels, and I started out with openGL functions similar to what you're using. Passing data to the gpu one point at a time was just extremely slow.

Now I calculate the image first, then to animate the image I use glsl shaders to program the gpu.

I pass the large arrays (like the vertex array and the color texture) to the gpu once, then I only need to pass small amounts of data (like an updated model matrix and some uniforms) on each redraw. The redraws are done in a method called by a timer (maybe 30-60 times/s).

The camera remains in one spot; the object rotates (the updated model matrix).

Because the majority of the data is transferred to the gpu in one (fast) operation and because a lot of the calculation is off-loaded from the cpu to the gpu pipeline, this is a very efficient process.

The learning curve for this may be more time than one want to invest, but if you're willing the Red Book and Orange Book are good places to start. They're a bit dated, but still good.

The Red Book (OpenGL Programming Guide, 8th Edition) and the Orange Book (OpenGLĀ® Shading Language, Second Edition) are both available for free download. Just google them; they're easy to find.