PDA

View Full Version : Some question on animation



inegvey
09-05-2016, 06:25 AM
Hi, all!

I have, maybe, abstract question, though I have some code for it. Small program draws a circle which can be moved by arrow keys (by some distance in one of the four directions), and I tried to make this transition animated. Here is my display function:



void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBufferData(GL_ARRAY_BUFFER, sizeof(circle), circle, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLE_FAN, 0, N);
glutSwapBuffers();
}


When some specific key (in my program it's an arrow key) is pressed the following function is called inside the glutKeyboardFunc:



void move_circle(vec v)
{
vec delta = normalize(v) / 50.0;

for (int j = 0; j < 10; j++) {
for (int i = 0; i < N; i++)
circle[i] += delta;
display();
}
}


Vector v is always one of the following: (0,1), (0,-1), (1,0), (-1,0) (UP, DOWN, RIGHT and LEFT arrow keys, respectively). Everything works fine, but when that special key is held down, there is a delay between key is released and end of animation. Since circle always moves for some fixed distance it seems logical that delay will be after releasing key during the transition, but it's quite longer than just one move and I can't figure it out.

Thanks.

GClements
09-05-2016, 10:26 AM
First, you shouldn't be moving the triangle by modifying the vertex positions. You should modify a transformation which is used to transform the vertex positions (either via a vertex shader or, if using the fixed-function pipeline, via the matrix functions).

As for delays: are you calling the display() function directly from the key-press handler? If so, you may be generating rendering commands faster than they can be processed. Event handlers should simply call glutPostRedisplay() to schedule a redraw.

inegvey
09-05-2016, 11:15 AM
First, you shouldn't be moving the triangle by modifying the vertex positions. You should modify a transformation which is used to transform the vertex positions (either via a vertex shader or, if using the fixed-function pipeline, via the matrix functions).

As for delays: are you calling the display() function directly from the key-press handler? If so, you may be generating rendering commands faster than they can be processed. Event handlers should simply call glutPostRedisplay() to schedule a redraw.

Thank you for your answer, wanted to check something.

First of all, I don't understand what's the difference between the linear transformation (that is matrix multiplication) and just changing needed coordinates explicitly how I did (moreover, I think it's faster than multiplying all vertices by matrix).

About display(), I call it not in handler, I call it in move_circle function which is called in key handler one time, if I use glutPostRedisplay(), than I basically se no animation, because transformation finishes faster, than next time display function is called.

GClements
09-05-2016, 01:31 PM
First of all, I don't understand what's the difference between the linear transformation (that is matrix multiplication) and just changing needed coordinates explicitly how I did (moreover, I think it's faster than multiplying all vertices by matrix).

Performing the transformation on the GPU takes advantage of the GPU's performance, and avoids having to copy the data from system memory to video memory afterwards.

If you're using a vertex shader, you can use vector addition rather than a matrix multiplication (but the GPU can probably perform matrix multiplies faster than the CPU can perform vector addition and transfer the updated data). If you're using the fixed-function pipeline, the matrix multiplication happens anyhow; multiplying by an identity matrix isn't necessarily faster than any other matrix.



About display(), I call it not in handler, I call it in move_circle function which is called in key handler one time, if I use glutPostRedisplay(), than I basically se no animation, because transformation finishes faster, than next time display function is called.
You shouldn't be performing multiple render operations (or any render operations) in an input handler, just updating state. For a multi-frame animation, have a timer function update the state then call glutPostRedisplay(). Performing back-to-back redraws results in the animation speed being dependent upon the redraw speed, which is unlikely to be desirable (with vsync disabled or with triple buffering, you can get 1000 FPS for simple geometry, meaning the animation is never seen).

The Little Body
09-05-2016, 05:18 PM
You have to use glutTimerFunc() with a glutPostRedisplay() call inside the timer function for to handle the animation.

Here a simple example where you can animate the white circle using arrows keys and where the speed of animation can to be selected using numericals keys from 0 to 9, with a refresh rate of 50 fps



#include <stdio.h>
#include <math.h>
#include <GL/glut.h>

typedef struct
{
float x, y, z;
} vertex_t;

#define N 11

float vCircle[N+2][3];

float speed = 1.00f;
float incX = 0.00f;
float incY = 0.00f;

void InitCircle()
{
int i;

vCircle[0][0] = 0.0f;
vCircle[0][1] = 0.0f;
vCircle[0][2] = -5.0f;

for( i = 0 ; i < N+1 ; i++ )
{
vCircle[i+1][0] = cos( (float)(i) * 2.0f * M_PI / (float)(N) );
vCircle[i+1][1] = sin( (float)(i) * 2.0f * M_PI / (float)(N) );
vCircle[i+1][2] = -5.0f;
}
}

void DrawCircle()
{
glVertexPointer(3, GL_FLOAT, 0, vCircle);
glDrawArrays(GL_TRIANGLE_FAN, 0, N+2);
}

void Init()
{
glEnable(GL_DEPTH_TEST);
glEnableClientState(GL_VERTEX_ARRAY);

InitCircle();
}

void AnimateCircle()
{
int i;

for ( i = 0 ; i < N + 2 ; i++)
{
vCircle[i][0] += incX * speed;
vCircle[i][1] += incY * speed;
}

if( vCircle[0][0] < -1.0f ){ incX = +0.01f; }
else
if( vCircle[0][0] > +1.0f ){ incX = -0.01f; }

if( vCircle[0][1] < -1.0f ){ incY = +0.01f; }
else
if( vCircle[0][1] > +1.0f ){ incY = -0.01f; }
}

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

DrawCircle();

glutSwapBuffers();
}

void ReshapeFunc(int w, int h)
{
//Tell OpenGL how to convert from coordinates to pixel values
glViewport(0, 0, w, h);

//Switch to setting the camera perspective
glMatrixMode(GL_PROJECTION);

//Set the camera perspective
glLoadIdentity();

//Reset the camera
gluPerspective( 45.0, (double)w / (double)h, 1.0, 200.0);
}

void KeyboardFunc(unsigned char key,int x, int y)
{
switch (key)
{

case 27: /* ESC = Quit */
case 'Q':
case 'q':
exit(0);
break;

case '0' : speed = 0.0f; break;
case '1' : speed = 1.0f; break;
case '2' : speed = 1.0f; break;
case '3' : speed = 2.0f; break;
case '4' : speed = 4.0f; break;
case '5' : speed = 5.0f; break;
case '6' : speed = 6.0f; break;
case '7' : speed = 7.0f; break;
case '8' : speed = 8.0f; break;
case '9' : speed = 9.0f; break;
}
}

void SpecialKeyboardFunc(int key,int x, int y)
{
int i;

switch (key)
{
case GLUT_KEY_LEFT :

incX = -0.01f;
break;

case GLUT_KEY_RIGHT :

incX = +0.01f;
break;

case GLUT_KEY_UP :

incY = +0.01f;
break;

case GLUT_KEY_DOWN :

incY = -0.01f;
break;
}

glutPostRedisplay();
}

void TimerFunc(int value)
{
AnimateCircle();

glutPostRedisplay();

// Refresh all 50ms
glutTimerFunc(50, TimerFunc, value);
}

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

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

glutCreateWindow("Circle move");

Init();

glutDisplayFunc(DisplayFunc);
glutReshapeFunc(ReshapeFunc);
glutKeyboardFunc(KeyboardFunc);
glutSpecialFunc(SpecialKeyboardFunc);
glutTimerFunc(50, TimerFunc, 0);

glutMainLoop();

return 0;
}


This sample is very simple to compile on a linux box :


gcc circle.c -o circle -lm -lGL -lglut -lGLU

inegvey
09-06-2016, 06:15 AM
Thanks you all for your replies. I think I have to go a bit further through my book (Angel E. - Interactive Computer Graphics) to understand how to use GPU in order to make some arithmetic calculations. By the way, in the second chapter (introduction to OpenGL) author uses the following way to create animation of rotation:



void display()
{
for (int i = 0; i < 3; i++)
{
float x = cos(angle) * points[i].x - sin(angle) * points[i].y;
float y = sin(angle) * points[i].x + cos(angle) * points[i].y;
points[i].x = x;
points[i].y = y;
}

glBufferData(GL_ARRAY_BUFFER, sizeof(points), points,
GL_STATIC_DRAW);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFlush();
}


That is, he calls glutPostRedisplay() in glutIdleFunc. So, how I understand it now, is it just a software animation?

The Little Body
09-06-2016, 12:54 PM
Yes, it is.

OpenGL/GLUT only handle a static image for each frame with of course multiples "planes" like the color-buffer, the depth-buffer or the alpha-buffer.

For to handle dynamics scenes on OpenGL/GLUT , we have to construct this scene frame per frame with the use of glutPostRedisplay() for to display each frame one by one.

The animation is handled with fonctions like glutTimerFunc(), glutIdleFunc() , glutKeyboardFunc or glutMouseFunc() for examples.