Вращение с постоянной скоростью в представлении 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) Во время выполнения измерьте частоту кадров вашего приложения в течение нескольких полных циклов и установите скорость прорисовки так, чтобы она никогда не превышала вашу самую низкую измеренную производительность при рисовании.
Я полагаю, что регулировка угла поворота не является правильным подходом для этой проблемы, потому что вы сейчас пытаетесь удерживать два параметра в движении друг с другом (скорость прорисовки и скорость вращения), а не просто закреплять один параметр: скорость прорисовки.
Приветствия.