1

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

radical7
  • 8,957
  • 3
  • 24
  • 33
Tarta
  • 1,729
  • 1
  • 29
  • 63
  • 2
    store the result in a buffer and draw that buffer multiple times using different matrices – ratchet freak Dec 15 '14 at 16:10
  • @ratchetfreak Thanks! That's already a really useful tip I can use. I edited the question cause I am looking for deeper answers on how to modify the gluLookAt call. Thanks again! – Tarta Dec 15 '14 at 16:18
  • I don't know why people demote the question without even explaining why. I am striving to find a solution and asking help. – Tarta Dec 15 '14 at 16:56
  • I downvoted because of formatting issues and lack of a [MCVE](http://stackoverflow.com/help/mcve). – genpfault Dec 15 '14 at 17:14
  • While using modern constructs are the best, you could save around `iterations-4` calls to `glColor` by breaking your big `for` loop into four other loops with a single call to `glColor` before each. Aside from all the comparisons inside the loop, and function call overhead, it'll also let the OpenGL driver work more efficiently. – radical7 Dec 15 '14 at 18:02

2 Answers2

1

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.

genpfault
  • 51,148
  • 11
  • 85
  • 139
  • This is a really good one!! Thanks a lot! But I have just a couple of questions: 1) I don't get how you decided the arguments of that gluLookAt call. How did you find out that 70,70,70 is a good position for the camera? Moreover, I tried to tune them a bit differently just to figure out what would have changed. For example if I place the camera at 0,0,200 I should still be able to see the system (according to my honest opinion) but I am not, why? 2) Instead of using a timer, does glut allow me to use inputs from the keyboard in such a way to control the angle? – Tarta Dec 16 '14 at 16:42
  • 1
    For #2 look into `glutKeyboardFunc()` and `glutSpecialFunc()`. – genpfault Dec 16 '14 at 17:02
  • 1
    For #1 if you examine the [`gluLookAt()` docs](https://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml) you'll notice that it says `The UP vector must not be parallel to the line of sight from the eye point to the reference point.` If you want the camera to be directly above the origin looking down at it you'll have to choose an `up` vector that's perpendicular-ish to the view direction, for instance `(0, 1, 0)` would work. – genpfault Dec 16 '14 at 17:08
  • As for why I chose `(70, 70, 70)`, it puts the camera off of and at an angle to all three axes. It also will put you outside of an origin-centered object. – genpfault Dec 16 '14 at 17:12
  • 1
    I got it, that's why you used 0,0,1 as up vector! Indeed if I put the camera at 0,0,100 for example and as up vector 0,1,0 it works! Unfortunately I cannot express you my gratitude with anything else than a simple "thank you".. really.. thanks a lot.. – Tarta Dec 16 '14 at 17:20
0

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.

jwlaughton
  • 905
  • 1
  • 6
  • 11
  • Perhaps you meant to say "OpenGL fixed-function routines" where you said "glut functions"? The OP uses a lot of `glColor` and `glVertex` routines, as you correctly discuss. The number of GLUT functions used is both small, and by comparison, negligible on performance. – radical7 Dec 15 '14 at 17:38
  • @radical7 You are correct that it's the OpenGL fixed functions I was talking about. My bad. My point remains the same, however, that with a significant number of points to be drawn, it becomes much more efficient to set up the shaders and use something like a glDrawArrays(GL_POINTS, 0, numPOINTS) command. – jwlaughton Dec 16 '14 at 02:48