Вращение с постоянной скоростью в представлении OpenGL ES с использованием CADisplayLink на iPhone

Мои классы и код OpenGL ES взяты из примера кода Apple GLES2Sample. Я использую их, чтобы показать трехмерные объекты, плавно вращающиеся вокруг одной оси, с ожидаемой постоянной скоростью вращения. В настоящее время приложение использует интервал кадра 1, и каждый раз, когда рисуется представление OpenGL (в EAGLView drawView метод), я поворачиваю модель на определенный угол.

На практике это дает приличные результаты, но не идеальные: во время вращения, когда большие части объекта исчезают из поля зрения, рендеринг становится быстрее, и вращение, таким образом, не имеет постоянной угловой скорости. У меня вопрос: как мне сделать это более плавным?

Хотя я приветствую все предложения, у меня уже есть одна идея: измерять FPS рендеринга каждые полсекунды и регулировать угол поворота при каждой перерисовке, основываясь на этом. Однако это звучит не очень хорошо, поэтому: что вы об этом думаете и как бы вы справились с этой проблемой?

2 ответа

Решение

Я склонен использовать CADisplayLink для запуска новых кадров и простого вычисления времени в запросе кадров, чтобы выяснить, насколько далеко продвинется моя логика.

Предположим, у вас есть переменная-член timeOfLastDraw типа NSTimeInterval. Вы хотите, чтобы ваша логика составляла, скажем, 60 ударов в секунду. Затем (с целой кучей переменных, чтобы сделать код более понятным):

- (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];
}

В вашем случае updateLogic будет применять фиксированное количество вращения. Если постоянное вращение действительно все, что вам нужно, вы можете просто умножить постоянную вращения на numberOfTicks или даже пропустить весь этот подход и сделать что-то вроде:

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

вместо того, чтобы хранить свою собственную переменную. В любом случае, кроме самого тривиального случая, вы обычно хотите делать целую кучу сложных вещей за квант времени.

Если вы не хотите, чтобы скорость рендеринга изменялась, и ваш открытый цикл (т. Е. Полный наклон) с CADisplayLink или другим таймером анимации, вы можете сделать две вещи:

1) Оптимизируйте свой код так, чтобы он никогда не падал ниже 60 FPS - максимальная частота кадров для устройства при любых обстоятельствах с вашей моделью.

2) Во время выполнения измерьте частоту кадров вашего приложения в течение нескольких полных циклов и установите скорость прорисовки так, чтобы она никогда не превышала вашу самую низкую измеренную производительность при рисовании.

Я полагаю, что регулировка угла поворота не является правильным подходом для этой проблемы, потому что вы сейчас пытаетесь удерживать два параметра в движении друг с другом (скорость прорисовки и скорость вращения), а не просто закреплять один параметр: скорость прорисовки.

Приветствия.

Другие вопросы по тегам