Smooth Animations

Started by
18 comments, last by flery 14 years ago
Hi, i am trying to achieve really smooth animations under the following conditions:
  • vsync on or off
  • opengl or directx
  • windowmode or fullscreen
I have read everything i can find on the topic. Some suggest turning on vsync some suggest turning it off. Some say use fixed timesteps others say use a speedfactor. I have tried:
  • vsync on
  • vsync off
  • fullscreen
  • windowmode
  • both render APIs
  • double buffering
  • hires timers (windows)
  • ctimer
  • fixed time steps
  • dynamic time steps
All of my animations did not run really smooth. The best results (close to perfect) were achieved using fullscreen mode with vsync off. The most problematic case is windowmode with vsync on. Any helpful links or tips would really be appreciated. Some informations on my setup. I am currently rendering a none textured quad that moves on a straight line.
Advertisement
Hi,

could you please be more specific as how it looks now and how it should look?
“Always programm as if the person who will be maintaining your program is a violent psychopath that knows where you live”
What kind of animation are you implementing? 2D Sprite? Skeletal Animation (vertex skinning)? Are you interpolating between keyframes?
like i said. i am trying to move a quad from 0,0,0 to 5,0,0 with a linear animation speed. the animation seems jerky (you can realise that espacially looking at the edges). i would make a video but fraps would probably disturb the results.
if you are not able to relate to the problem directly dont worry just give any information on the topic that comes to your head. links, keywords, anything...

i would especially like to hear how you take care of animation speeds.

thanks
ok so you arent doing 2d sprite animation or 3d skeletal keyframed animation, you are just moving a box across the screen.

So i'm going to have to mind read you on this cause you didnt give us very much info but i think i know whats going on.

Ok, the problem is that you are moving the box a constant amount every frame (such as 10 units lets say), but frame rate fluctuates from frame to frame based on all sorts of factors outside of your control, which makes it so that some frames might be 15ms lets say, and others are 30ms, but both those frames have the box moving 10 units, which means that the box is actually changing how fast it moves across the screen as the frame time changes.

There is a solution though!

Basically what you need to do is measure how long your last frame was, and multiply your speed by that amount when moving it ever frame.

For example here's simplistic some pseudo code:

void Main(void){  DWORD dLastFrameTime;  DWORD dFrameElapsedMS;  //fake the first frame  dLastFrameTime = GetTickCount() - 15;  //move the box at this speed  float fBoxSpeed = 2.0f;  while(1)  {    dFrameElapsedMS = dLastFrameTime - GetTickCount();    dLastFrameTime = GetTickCount();    BoxMovementSpeed = (((float)dFrameElapsedMS) * fBoxSpeed);    MoveBox(BoxMovementSpeed);  }}


Hopefully you understand the essence of that, but what that does is make it so that if the frame takes longer, it moves the box farther. If the frame takes less time, it doesn't move the box as far.

Make sense?
This is a common question, there could be several things happening.

When you run in windowed mode, vsync is disabled and the framerate is unbounded. If your games update is linked with framerate or your interpolating motion on a frame by frame basis, the incremental movement vector is so small that any subtle 2ndary factors (cpu load on ur machine from background processes for instance) can result in large variance of the movement vector. Even though you may have +200fps, the large variance between frames can result in un-even movement.

Better to have a solid 60fps low variance than 200+fps with high variance.

Could be your not interpolating motion over time or your time interpolation has some subtle errors or side effects, that happens alot as well. Like above if your just taking the raw difference in time between frames, at high frame rates the variance can be very high. These large variance in time will then trickle down through the system and exhibit itself as slightly annoying jerky movement, even though you have high fps.

You mentioned that you tried fixed timesteps. Depending upon how you implement it this also can result in jerky movement. Just updating a fixed step every frame can result in jerky movement if your framerate itself is highly variable. For instance if your frame rate is say 60fps on average and but ever once and a while it drops down to 30fps, but only a few frames. This will exhibit itself as a stutter or slow motion when using non-time corrected fixed timesteps.

If you tried to time correct the fixed timestep method (ie multiple fixed steps to maintain close to realtime) this also can result in jerkyness, mostly due to how you implement it. If you keep a sum of time between frames and when the sum > fixed timestep time you do an update, this scheme can result in a periodic frame doubling where you get a occasional double update. This can appear as a periodic skipping.

Good Luck!

-ddn
hi,
thanks for all the responses, especially the last one.
i probably did not put enough info in to the first post. i`ll try to elaborate:

- fixed time steps: i used the technique described in this link (which is probably more physics oriented but still should work just fine): http://gafferongames.com/game-physics/fix-your-timestep/

- dynamic time steps: very common approach use the time difference of each frame to move the quad with a dynamic speed (speed * frame_time)

- filtered time steps: i collected different frame times (for the last: 30, 60 or 100 fps) and averaged the result. frame_time = avg_frame_time

Since the last i moved the windows msg pump handling to the top of update loop which helped a bit. I am still not fully satisfied with the result.
Maybe i am just trying to hard and not much can be done running something in window mode. threading and multitasking could just interfere to much with the update loop. if i have time i`ll post some sample applications that could be used to compare my experiences with your observations.

@Atrix: i hoped that dynamic time steps was information enough so that you could imagine that thats exactly what i am doing.
Ok, I don't have any links, but I will try my best to explain how I do it (Sorry for my English):

First, I use fixed time step. (60 fps, or 30). But I do not render 60 fps.

Every frame:
int numberOfFrameToUpdate = fixedTimeStepTimer.UpdateTimer();while (numberOfFrameToUpdate--){   // Update your scene}// Render your scene


Then now, you have to find what is the remaining time before the next update. If your fixedTimeStepTimer.UpdateTimer() returns you one frame, but almost 2. You should try to find that percentage left (0 to 1).

Then in your update, you always keep track of the last position/matrix for every of your objects. And you lerp in your rendering.

This gives incredible smooth results.
Cons:
- Need to keep track of last frame variables all the time, for every animated things. (If you have a good entity system, you probably have only one place to add this ;))
- You are behind simulation a bit. So you might noticed delay in the inputs in some cases, like if you update at 30 fps instead of 60.
Quote:Original post by flery
Since the last i moved the windows msg pump handling to the top of update loop which helped a bit. I am still not fully satisfied with the result.

Post your message loop, there is a risk it's still not perfect. Where do you update the variables, and where do you render the graphics?

Here follows my message loop that I used a while ago, it works smoothly for me when I use accumulated fixed time steps in the "Update" call:

//run message loop until user quitsMSG msg;bool bRun = true;while (bRun){	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	{		if (msg.message == WM_QUIT)		{			bRun = false;			break;		}		TranslateMessage(&msg);		DispatchMessage(&msg);	}	if (bRun)	//some app-functions need active window	{		gApp.Update();		gApp.Display();	}}
int DLL start(IApplication* app){	int argc = 0;	app->OnInit();	bool done = FALSE;										msw::Window* w = new msw::DX10Window();	//msw::Window* w = new msw::GLWindow();	w->create("F3 Test",1000, 1000, 32, false);	w->setPosition(0,0);	w->initRenderAPI();	w->show();		float t = 0.0f;	const float dt = 0.01f;	float currentTime = f3::util::time();	float accumulator = 0.0f;	while (!done)	{		HandleMsgPump(done, w);		float newTime = f3::util::time();		float deltaTime = newTime - currentTime;		currentTime = newTime;		accumulator += deltaTime;		while (accumulator>=dt)		{			app->OnUpdate();			t += dt;			accumulator -= dt;		}		if (w != NULL)			w->paint();			}	return 0;}void HandleMsgPump(bool& done, msw::Window* w){	MSG msg;										if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))		{		if (msg.message==WM_QUIT)						{			done=TRUE;									}		else if (msg.message == WM_KEYDOWN)		{			switch (msg.wParam)			{			case VK_F4:				if (w->getFullscreen() == false)				{					w->setFullscreen(true);				}				else				{					w->setFullscreen(false);				}				break;			}		}		else										{			TranslateMessage(&msg);						DispatchMessage(&msg);					}	}}

thats the accumulator version
thanks for sharing Daivuk & Dim_Yimma_H

This topic is closed to new replies.

Advertisement