1

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

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

#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;
}
4

1 回答 1

4

问题 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;
}
于 2012-06-17T16:45:42.697 回答