I'm new to graphics, so I don't know how people usually control the frame rate for rendering things. I mean, how can you set you application to render at, say, 30fps? There's probably lots of APIs that offers such thing, but I need to code it from scratch.
问题:
回答1:
There are two "usual" ways of "controlling" framerate, and neither is quite that simple.
The first and more controlling of the two, and something that is usually optional, is VSync. This forces the video card to only push out a new frame when the monitor is done refreshing. Many monitors refresh at 60 Hz, so you tend to get 60 FPS.
This works quite well to cap framerate to monitor refresh rate, but when framerate drops below refresh, it's forced to the next multiple. Thus, as the framerate starts to drop a bit, you lose quite a bit of potential rendering time, because it's forced to 60, then 30, then 20, etc.
(a bit of info about vsync in DirectX and OpenGL)
The second method, commonly used (with vsync optionally added on) is not to limit framerate. Instead, adjust your code to handle differences. This is far more flexible in the long run and generally better coding, IMO, and much simpler than trying to force a particular FPS count.
Assuming you have a simple render loop, it starts out looking something like:
while ( gameloop )
{
float framedelta = ( timeNow - timeLast )
timeLast = timeNow;
for each ( GameObject object in World )
{
object->Animate(framedelta);
object->Move(speed * framedelta)
}
render();
}
You want to look at the time difference/time passed/delta, and work from there. Allow the framerate to scale based on hardware and settings (there are too many variations for you to predict or handle even half), and make your game work with that instead of controlling it. Much easier for you and more flexible and stable in practice.
回答2:
The typical way to yield a predictable (if not constant) frame rate (with video or 3D graphics) is described in the following pseudo-code.
Algorithm
- Prepare the next frame (render in back-buffer);
- Sleep for the remainder of the time slice;
- Ask the frame to be displayed (swap front and back buffers).
Note the position of the sleep operation. It's sandwiched between the preparation and display of the same frame. This is the principal key to a constant frame rate! You want the preparation of the frame to count in the total time slice for displaying the frame.
There are a number of variants on how to implement each of these steps (choice of sleep operation based on it's resolution, reliability, etc.), but the core is there.
Tips for extra reliability
- Don't invoke your sleep function once for the entire interval. Define some constant with the maximum error you're ready to accept and repeatedly sleep for periods of this interval until the time remaining is smaller or equal to this amount.
- Don't be scared to occasionally drop a frame when you know you won't be able to prepare the next frame in time. It's visually more appealing to skip a frame than it is to produce a variable frame rate.
- If you can avoid background threads on a single-processor system, avoid them. Using worker threads will really mess up the reliability of your sleep function. The best approach is to split your background work into small chunks and have this work executed instead of sleeping.
The 1st is easy to implement and can be hidden away in a helper function. The 2nd is a little more touchy as it requires you to keep statistics of average rendering speed, among other things. The 3rd is hard to implement as it's often difficult to predict how much time different tasks will take. It's only usually implemented in real-time systems with hard constraints.