0

My OpenGL ES classes and code are derived from Apple's GLES2Sample code sample. I use them to show 3D objects rotating smoothly around one axis, at what I expect to be a constant rotation speed. Currently, the app uses a frame interval of 1, and each time the OpenGL view is drawn (in the EAGLView's drawView method), I rotate the model by a certain angle.

In practice, this gives decent results, but not perfect: during the rotation, when large parts of the object go out of sight, rendering becomes faster and the rotation thus does not have constant angular velocity. My question is: how do I make it smoother?

Although I welcome all suggestions, I already have one idea: measure the rendering FPS every half-second, and adjust the rotation angle at each redraw based on that. It does not sound very good, however, so: what do you think of this, and how would you handle the issue?

F'x
  • 12,105
  • 7
  • 71
  • 123

2 Answers2

3

I tend to use a CADisplayLink to trigger new frames and a simple time calculation within the frame request to figure out how far to advance my logic.

Suppose you have a member variable, timeOfLastDraw, of type NSTimeInterval. You want your logic to tick at, say, 60 beats per second. Then (with a whole bunch of variables to make the code more clear):

- (void)displayLinkDidTick
{
    // get the time now
    NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];

    // work out how many quantums (rounded down to the nearest integer) of
    // time have elapsed since last we drew
    NSTimeInterval timeSinceLastDraw = timeNow - timeOfLastDraw;
    NSTimeInterval desiredBeatsPerSecond = 60.0;
    NSTimeInterval desiredTimeInterval = 1.0 / desiredBeatsPerSecond;

    NSUInteger numberOfTicks = (NSUInteger)(timeSinceLastDraw / desiredTimeInterval);

    if(numberOfTicks > 8)
    {
        // if we're more than 8 ticks behind then just do 8 and catch up
        // instantly to the correct time
        numberOfTicks = 8;
        timeOfLastDraw = timeNow;
    }
    else
    {
        // otherwise, advance timeOfLastDraw according to the number of quantums
        // we're about to apply. Don't update it all the way to now, or we'll lose
        // part quantums
        timeOfLastDraw += numberOfTicks * desiredTimeInterval;
    }

    // do the number of updates
    while(numberOfTicks--)
        [self updateLogic];

    // and draw
    [self draw];
}

In your case, updateLogic would apply a fixed amount of rotation. If constant rotation is really all you want then you could just multiply the rotation constant by numberOfTicks, or even skip this whole approach and do something like:

glRotatef([NSDate timeIntervalSinceReferenceData] * rotationsPerSecond, 0, 0, 1);

instead of keeping your own variable. In anything but the most trivial case though, you usually want to do a whole bunch of complicated things per time quantum.

Tommy
  • 99,986
  • 12
  • 185
  • 204
1

If you don't want the rendering to speed to vary, and your running open-loop (i.e. full tilt) with CADisplayLink or other animation timer, there are two things you can do:

1) Optimize your code so it never wanders below 60 FPS - the maximum frame rate for the device under any circumstances with your model.

2) At run time, measure the frame rate of your application through a few complete cycles and set the draw rate such that it will never exceed your lowest measured drawing performance.

I believe adjusting your rotation angle isn't the right approach for this problem because you're now trying to keep two parameters jiving with one another (draw rate and rotation rate) rather than simply pinning down a single parameter: draw rate.

Cheers.

user487158
  • 460
  • 3
  • 9