【问题标题】:simple 2d animation in glutglut中的简单2d动画
【发布时间】:2012-06-17 16:30:36
【问题描述】:

我有一个任务要做,但我似乎无法真正理解它。 任务如下:向背景(太阳系)添加纹理,向 2 个对象(绘制的形状)添加纹理,并添加一个动画,其中两个对象必须从彼此和远处的墙壁反弹(如屏幕末尾)。

除了动画,我什么都做了。 我该怎么做这种动画? p.s.里面的动画是我能想到的最好的。

#include <gl/glut.h>
#include <gl/gl.h >
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>


float x;
float y;



unsigned char *imageData;
int imageRows, imageCols;

extern void loadBMP(char *);
char cotton1[] = "cotton1.bmp";
char cotton2[] = "cotton2.bmp";
char fons[] = "solar.bmp";

GLuint texture[3];
float cube[1], Vcube[1];

/* GLUT callback Handlers */

void init()
{
cube[0]=0;
Vcube[0]=0.01;
cube[1]=0;
Vcube[1]=0.01;


glShadeModel(GL_SMOOTH);

glGenTextures( 3, &texture[0] );

   loadBMP(cotton1);
   glBindTexture( GL_TEXTURE_2D, texture[0] );
   glTexImage2D(GL_TEXTURE_2D, 0, 3, imageCols, imageRows,
         0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

     loadBMP(cotton2);
   glBindTexture( GL_TEXTURE_2D, texture[1] );
   glTexImage2D(GL_TEXTURE_2D, 0, 3, imageCols, imageRows,
         0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

        loadBMP(fons);
   glBindTexture( GL_TEXTURE_2D, texture[2] );
   glTexImage2D(GL_TEXTURE_2D, 0, 3, imageCols, imageRows,
         0, GL_RGB, GL_UNSIGNED_BYTE, imageData);

   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

}

static void 
resize(int width, int height)
{
    const float ar = (float) width / (float) height;

    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity() ;
}

static void 
idle(void)
{
    glutPostRedisplay();
}

void animation()
{
cube[1]+=Vcube[1];
if (cube[1]<0.1)
{ Vcube[1]+=Vcube[1]; }
if (cube[1]>0.095)
{ Vcube[1]=-0.01; }
if (cube[1]<0)
{ Vcube[1]=+0.01; }

glTranslatef(cube[1],0,0);
       Sleep(100);
       glutPostRedisplay();
}


void animation2()
{
  cube[0]+=Vcube[0];
if (cube[0]<(-0.1))
{ Vcube[0]-=0.01; }
if (cube[0]>0)
{ Vcube[0]-=0.01; }
if (cube[0]<0.1)
{ Vcube[0]+=0.01; }


glTranslatef(cube[0],0,0);
       Sleep(100);
       glutPostRedisplay();
}

void display() {


    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);  


//Background
    glLoadIdentity();
    glBindTexture( GL_TEXTURE_2D, texture[2]);
    glEnable( GL_TEXTURE_2D );

    glPushMatrix();
     glBegin( GL_QUADS );
      glTexCoord2f(1.0,1.0); glVertex2f(-1.0,1.0);
      glTexCoord2f(0.0,1.0); glVertex2f(1.0,1.0);
      glTexCoord2f(0.0,0.0); glVertex2f(1.0,-1.0);
      glTexCoord2f(1.0,0.0); glVertex2f(-1.0,-1.0);
      glEnd();
      glPopMatrix();
    glDisable(GL_TEXTURE_2D);

  animation();

//TEXTURE 1
    glBindTexture( GL_TEXTURE_2D, texture[0]);

    glEnable( GL_TEXTURE_2D );

    glPushMatrix();
    glBegin( GL_TRIANGLE_FAN );
glTexCoord2f(0.5f, 0.5f);  glVertex2f(  0.5f,  0.0f);   //center
glTexCoord2f(1.0f, 0.5f);  glVertex2f(  0.8f+x,  0.0f);   //right
glTexCoord2f(0.75f, 1.0f); glVertex2f(  0.55f+x, 0.3f+x);  //top right
glTexCoord2f(0.25f, 1.0f); glVertex2f(  0.35f-x, 0.3f+x);   //Top left
glTexCoord2f(0.0f, 0.5f);  glVertex2f(  0.25f-x, 0.0f);   //left
glTexCoord2f(0.25f, 0.0f); glVertex2f(  0.45f-x,-0.3f-x);   //bottom left
glTexCoord2f(0.75f, 0.0f); glVertex2f(  0.7f+x, -0.2f-x);   //bottom right
glTexCoord2f(1.0f, 0.5f);  glVertex2f(  0.8f+x,  0.0f);   //right
      glEnd();
      glPopMatrix();
    glDisable(GL_TEXTURE_2D);
//TEXTURE 2

    animation2();   
    glBindTexture( GL_TEXTURE_2D, texture[1]);

    glEnable( GL_TEXTURE_2D );
    glPushMatrix();
    glBegin( GL_TRIANGLE_FAN );
glTexCoord2f(0.5f, 0.5f);  glVertex2f( -0.5f,  0.0f);  //center
glTexCoord2f(1.0f, 0.5f);  glVertex2f( -0.2f+y,  0.0f);  //right
glTexCoord2f(0.75f, 1.0f); glVertex2f( -0.4f+y,  0.2f+y);  //top right
glTexCoord2f(0.25f, 1.0f); glVertex2f( -0.7f-y,  0.1f+y);  //Top left
glTexCoord2f(0.0f, 0.5f);  glVertex2f( -0.8f-y,  0.0f);  //left
glTexCoord2f(0.25f, 0.0f); glVertex2f( -0.7f-y, -0.1f-y);  //bottom left
glTexCoord2f(0.75f, 0.0f); glVertex2f( -0.3f+y, -0.2f-y);  //bottom right
glTexCoord2f(1.0f, 0.5f);  glVertex2f( -0.2f+y,  0.0f);  //right
      glEnd();
      glPopMatrix();
    glDisable(GL_TEXTURE_2D);
glutSwapBuffers();
glFlush();
}


static void 
key(unsigned char key, int a, int b)
{
    switch (key) 
    {
        case 27 : 
        case 'q':
            exit(0);
            break;

        case '+':
             if ((x+0.01)<0.98)
             x=x+0.01;
             if ((y+0.01)<0.98)
             y=y+0.01;         
            break;

        case '-':
             if ((x-0.1)>(-0.15))
                x=x-0.01;
             if ((y-0.1)>(-0.10))
                y=y-0.01;
           break;

        case 'o':
             if ((x+0.01)<0.98)
             x=x+0.01;
             break;
        case 'p':
             if ((x-0.1)>(-0.15))
             x=x-0.01;
             break;


        case '[':
             if ((y+0.01)<0.98)
             y=y+0.01;
             break;
        case ']':
             if ((y-0.1)>(-0.10))
             y=y-0.01;
             break;



    }
    glutPostRedisplay();
}

int main(int argc, char *argv[]) {
   glutInit(&argc, argv);   

   glutInitWindowSize(640, 640);   
   glutInitWindowPosition(50, 50); 
   glutCreateWindow("Assignment number 3"); 
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);



   glutReshapeFunc(resize);
   glutDisplayFunc(display);      
   glutKeyboardFunc(key);         
   glutIdleFunc(idle);

   glClearColor(1.0, 1.0, 1.0, 1.0);

   init();

   glutMainLoop();               
   return EXIT_SUCCESS;
}

【问题讨论】:

    标签: c++ animation opengl 2d glut


    【解决方案1】:

    问题 1:您将 OpenGL 误认为是场景图。以你的 animation1 函数为例:

    void animation2(
         )
    {
        cube[0] += Vcube[0];
        if( cube[0] < ( -0.1 ) ) {
            Vcube[0] -= 0.01;
        }
        if( cube[0] > 0 ) {
            Vcube[0] -= 0.01;
        }
        if( cube[0] < 0.1 ) {
            Vcube[0] += 0.01;
        }
    
    
        glTranslatef( cube[0], 0, 0 );
        Sleep( 100 );
        glutPostRedisplay(  );
    }
    

    最后的 glTranslatef 只会在 OpenGL 上下文中当前处于活动状态的任何矩阵上丢弃。这不是怎么做的。

    下一个问题:您正在从绘图代码中调用动画函数。在绘制点时应确定所有场景状态。此外,调用该动画函数将在您的显示函数中休眠。这不是怎么做的。

    好,怎么办:首先把所有的动画进度器函数放到空闲循环中。不要睡觉,而是测量动画迭代之间的时间并相应地推进动画。不要在动画函数中调用 glutPostRedisplay。在空闲处理程序结束时是的,但不是在动画师中。在绘图代码中,使用评估的动画状态来相应地放置对象。使用矩阵堆栈(glPushMatrix、glPopMatrix)将事物很好地分开。


    #include <GL/glut.h>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    /* for gettimeofday */
    #include <sys/time.h>
    
    /* In general, littering your program with global variables should be avoided.
     * I admit, that sometimes even I don't adhere to this rule, and especially
     * using GLUT it takes to jump several very arcane hoops to avoid it.
     * So in this case, yes, having global variables is in order.
     *
     * The principle idea of global variables is to put data into them, that is
     * valid and the same for the whole of the program. More importantly they
     * must not be used to pass around data.
     *
     * It's also a good idea to make those variables static, so that they are
     * contained withing this compilation unit.
     */
    static float x;
    static float y;
    
    /* This is not how globals should be used. They're used to pass data around between
     * functions. DON'T DO THAT!
     *
     * Also this misses the extern keyword. Unless the compilation unit loading the
     * bmp files declares those being extern, hence relying on another compilation unit
     * to expose them like this, this code is likely to break.
    
    unsigned char *imageData;
    int imageRows, imageCols;
    
    extern void loadBMP(char *); 
    
     * BTW: You don't need the extern keyword here.
    
     * Instead have a nice little function that loads a BMP file and puts it into a
     * newly allocated texture object.
    */
    
    GLuint loadBmpToTexture(char const * const filename)
    {
        /* Implementation of this left as an exercise to the reader */
        return 0;
    }
    
    static double ftime(void)
    {
        /* Now this is a bit complicated: There's no portable high resolution
         * timer function. On Linux and Unices (hence also MacOS X) you have
         * gettimeofday, on Windows there are the High Performance Counters.
         * ... Totally annoying. 
         * Look here for a comparison:
         * http://www.songho.ca/misc/timer/timer.html
         *
         * Since I'm on a Linux box this is using gettimeofday
         */
    
        struct timeval t;
        gettimeofday(&t, NULL);
    
        return 1.0*t.tv_sec + 1e-6*t.tv_usec;
    }
    
    /* In this variable we store the time of the last iteration of the animation
     * loop to determine the time to time difference for the next one. */
    static double last_T;
    
    /* Actually those should be of type char const * const
     * This is one of the finer details of C. The arrays like you've declared them
     * here are mutable, but of constant size.
     * However you normally don't want string constant be like this. The preferred
     * modus operandi is to have the string constants in read only memory and pointers
     * to them. Like this:
     */
    char const * const cotton1 = "cotton1.bmp";
    char const * const cotton2 = "cotton2.bmp";
    char const * const fons = "solar.bmp";
    
    /* Okay, now consider what would happen if you had several objects, not just two or
     * three? How would you keep track of all those indices? Really, that's bad style.
     * If you've data belonging together, like state of an object, put it into a struct
     * and then also use useful variable names.
     */
    
    GLuint texture_background;
    
    typedef struct s_Cube {
        float x, V_x;
        GLuint texture;
    } Cube;
    
    /* also we can statically initialize here */
    Cube cube[2] = {
        {-0.05, 0.01, 0},
        {0.05, -0.02, 0}
    };
    
    /* GLUT callback Handlers */
    
    static void init(void)
    {
        /* loadBmpToTexture is defined to return 0 in case of failure
         * which is also the OpenGL default texture object, so this
         * fails safely. */
        texture_background = loadBmpToTexture(fons);
        cube[0].texture = loadBmpToTexture(cotton1);
        cube[1].texture = loadBmpToTexture(cotton2);
    
        glClearColor( 0.0, 0.5, 0.7, 1.0 );
    
        last_T = ftime();
    }
    
    static void animation(
        float const speed
         )
    {
        /* The objective is to let the cubes bounce into each other
         * (collision) and with the walls. First the collision: */
    
        if( cube[0].x > cube[1].x && cube[0].V_x > 0 && cube[1].V_x < 0 ) {
            /* cubes bounced off each other. Exchange their velocities */
            double const V_x = cube[0].V_x;
            cube[0].V_x = cube[1].V_x;
            cube[1].V_x = V_x;
    
            double const x = cube[0].x;
            cube[0].x = cube[1].x;
            cube[1].x = x;
        }
    
        /* and the wall bounce */
        if( cube[0].x < -0.1 && cube[0].V_x < 0 ) {
            /* left cube bounced into left wall */
            cube[0].V_x *= -1;
        }
    
        if( cube[1].x > 0.1 && cube[1].V_x > 0 ) {
            /* right cube bounced into left wall */
            cube[1].V_x *= -1;
        }
    
        cube[0].x += speed * cube[0].V_x;
        cube[1].x += speed * cube[1].V_x;
    }
    
    /* Ideally we'd use a precise animation loop interleaved with event processing here.
     * Unfortunately GLUT doesn't offer those, so we use this arcane kludge.
     *
     * It would get a bit more robust by putting the whole timing into the display function
     * but better abandon GLUT and get a true event loop.
     */
    static void idle(
        void )
    {
        const double now_T = ftime();
        const double delta_T = now_T - last_T;
        last_T = now_T;
    
        const double speed = delta_T * 60;
    
        animation( speed );
        glutPostRedisplay(  );
    }
    
    
    static void display(void)
    {
        /* We try to be as stateless as possible. Yes, in the face of a statefull
         * API, like OpenGL, this may sound a bit pedantic. */
        const int width = glutGet(GLUT_WINDOW_WIDTH);
        const int height = glutGet(GLUT_WINDOW_HEIGHT);
        const float ar = ( float ) width / ( float ) height;
    
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
        /* It's really best practice to set everything related to drawing
         * – and that includes the projection – in the drawing function */
        glViewport( 0, 0, width, height );
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        glOrtho(-ar, ar, -1, 1, -1, 1);
    
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
    //Background
        if(texture_background) {
            glBindTexture( GL_TEXTURE_2D, texture_background );
            glEnable( GL_TEXTURE_2D );
            glBegin( GL_QUADS );
            glTexCoord2f( 1.0, 1.0 );
            glVertex2f( -1.0, 1.0 );
            glTexCoord2f( 0.0, 1.0 );
            glVertex2f( 1.0, 1.0 );
            glTexCoord2f( 0.0, 0.0 );
            glVertex2f( 1.0, -1.0 );
            glTexCoord2f( 1.0, 0.0 );
            glVertex2f( -1.0, -1.0 );
            glEnd();
            glDisable( GL_TEXTURE_2D );
        }
    
    //TEXTURE 1
        glBindTexture( GL_TEXTURE_2D, cube[1].texture );
        glEnable( GL_TEXTURE_2D );
        /* Remember we're still in modelview matrix mode.
         * This push creates a copy of the currently modelview matrix,
         * for our disposal. With a following pop we restore to the
         * state saved now. Pushes and Pops nest. */
        glPushMatrix();
    
        /* This applies our animation position to the modelview matrix.
         * All geometry drawing to follow is subject to this additional
         * transformation, until the matrix changes again. */
        glTranslatef(cube[1].x, 0, 0);
        glBegin( GL_TRIANGLE_FAN );
        glTexCoord2f( 0.5f, 0.5f );
        glVertex2f( 0.5f, 0.0f );   //center
        glTexCoord2f( 1.0f, 0.5f );
        glVertex2f( 0.8f + x, 0.0f );   //right
        glTexCoord2f( 0.75f, 1.0f );
        glVertex2f( 0.55f + x, 0.3f + x );  //top right
        glTexCoord2f( 0.25f, 1.0f );
        glVertex2f( 0.35f - x, 0.3f + x );  //Top left
        glTexCoord2f( 0.0f, 0.5f );
        glVertex2f( 0.25f - x, 0.0f );  //left
        glTexCoord2f( 0.25f, 0.0f );
        glVertex2f( 0.45f - x, -0.3f - x ); //bottom left
        glTexCoord2f( 0.75f, 0.0f );
        glVertex2f( 0.7f + x, -0.2f - x );  //bottom right
        glTexCoord2f( 1.0f, 0.5f );
        glVertex2f( 0.8f + x, 0.0f );   //right
        glEnd(  );
        glPopMatrix(  );
        glDisable( GL_TEXTURE_2D );
    //TEXTURE 2
        /* in the original code you didn't use the other texture, 
         * Probably because you lost track of variables and indices. */
        glBindTexture( GL_TEXTURE_2D, cube[0].texture ); 
        glEnable( GL_TEXTURE_2D );
        glPushMatrix();
        glTranslatef(cube[0].x, 0, 0);
        glBegin( GL_TRIANGLE_FAN );
        glTexCoord2f( 0.5f, 0.5f );
        glVertex2f( -0.5f, 0.0f );  //center
        glTexCoord2f( 1.0f, 0.5f );
        glVertex2f( -0.2f + y, 0.0f );  //right
        glTexCoord2f( 0.75f, 1.0f );
        glVertex2f( -0.4f + y, 0.2f + y );  //top right
        glTexCoord2f( 0.25f, 1.0f );
        glVertex2f( -0.7f - y, 0.1f + y );  //Top left
        glTexCoord2f( 0.0f, 0.5f );
        glVertex2f( -0.8f - y, 0.0f );  //left
        glTexCoord2f( 0.25f, 0.0f );
        glVertex2f( -0.7f - y, -0.1f - y ); //bottom left
        glTexCoord2f( 0.75f, 0.0f );
        glVertex2f( -0.3f + y, -0.2f - y ); //bottom right
        glTexCoord2f( 1.0f, 0.5f );
        glVertex2f( -0.2f + y, 0.0f );  //right
        glEnd();
        glPopMatrix();
        glDisable( GL_TEXTURE_2D );
    
        glutSwapBuffers();
        /* Your glFinish here was totally pointless.
         * First it would belong _before_ glutSwapBuffers.
         * Second glutSwapBuffers implies a glFinish, so it's totally redundant. */
    }
    
    
    static void key(
        unsigned char key,
        int a,
        int b )
    {
        switch ( key ) {
        case 27:
        case 'q':
            exit( 0 );
            break;
    
        case '+':
            if( ( x + 0.01 ) < 0.98 )
                x = x + 0.01;
            if( ( y + 0.01 ) < 0.98 )
                y = y + 0.01;
            break;
    
        case '-':
            if( ( x - 0.1 ) > ( -0.15 ) )
                x = x - 0.01;
            if( ( y - 0.1 ) > ( -0.10 ) )
                y = y - 0.01;
            break;
    
        case 'o':
            if( ( x + 0.01 ) < 0.98 )
                x = x + 0.01;
            break;
        case 'p':
            if( ( x - 0.1 ) > ( -0.15 ) )
                x = x - 0.01;
            break;
    
    
        case '[':
            if( ( y + 0.01 ) < 0.98 )
                y = y + 0.01;
            break;
        case ']':
            if( ( y - 0.1 ) > ( -0.10 ) )
                y = y - 0.01;
            break;
        }
        glutPostRedisplay();
    }
    
    int main(
        int argc,
        char *argv[] )
    {
        glutInit( &argc, argv );
    
        glutInitWindowSize( 640, 640 );
        glutInitWindowPosition( 50, 50 );
        /* glutInitDisplayMode must be called before calling glutCreateWindow
         * GLUT, like OpenGL is stateful */
        glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH );
        glutCreateWindow( "Assignment number 3" );
    
        glutDisplayFunc( display );
        glutKeyboardFunc( key );
        glutIdleFunc( idle );
    
        init();
    
        glutMainLoop(  );
        return EXIT_SUCCESS;
    }
    

    【讨论】:

    • @user1462023:仅供参考:我冒昧地将所有提到的固定和更改添加到您的代码中。所以如果你被卡住了,我可以给你一个工作版本。
    • @user1462023:顺便说一句,您的动画代码本身也存在一些问题,例如测试错误的边界条件。
    • 如果你能做到,我会很荣幸。似乎我对这些对象的动画毫无用处。
    • @user1462023:好吧,让我彻底评论一下,这样你就明白了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-09
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2014-02-15
    • 2021-07-06
    相关资源
    最近更新 更多