Помню, когда я в первый раз взял в руки iPad, более всего меня поразило приложение iBooks c его натуралистичным перелистыванием страниц. Все остальные «фишки» как-то не особо впечатляли, но это… это показалось воплощением полной интерактивности. По сравнению с обычными графическими интерфейсами, использующими стандартные элементы GUI, интерфейсы с использованием анимации OpenGL это новый шаг в развитии пользовательских интерфейсов. Историческое отступление: первыми стали использовать элементы анимации в GUI рабочие станции Indy компании Silicon Graphics, которая и является основоположником языка OpenGL. На десктопах этих компьютеров были трехмерные кнопки меню, которые при нажатии переворачивались в 3D, открывая списки подменю из таких же кнопок. Это было здорово, и это было действительно красиво. Жаль, что Silicon Graphics ушла в небытие из-за неправильного менеджмента, эта компания была действительно первой во многом (OpenGL, STL, журналируемые файловые системы).
Поэтому не буду петь дифирамбы Apple, они лишь внедрили хорошо забытое старое. Но справедливости ради стоит отметить, что и это было прорывом, ведь они задали тенденцию и унылые статические интерфейсы, надеюсь, скоро останутся в прошлом.
Итак, OpenGL анимация и GUI.
По роду деятельности мне уже приходилось разрабатывать проекты с использованием OpenGL, поэтому когда я столкнулся с необходимостью создать анимацию, эмулирующую перелистывание бумажных страниц, передо мной встали две основные задачи:
проект разрабатывался для операционной системы MacOS X, используя основные фреймворки и язык Objective-C.
Алгоритм деформации бумажного листа
Идея взята отсюда. В качестве алгоритма деформации используется уравнение конуса. Этот алгоритм был разработан в исследовательских лабораториях Xerox PARC еще в 2004 году.
Изгиб бумажного листа определяется поверхностью некоего конуса, изменяя параметры которого можно добиться требуемого эффекта анимации.
Я не буду вдаваться в подробности описания самого алгоритма и тригонометрии. Скажу лишь что он пересчитывает любую 2D точку P(x,y) плоскости листа в 3D точку T(x,y,z), используя изменяемые параметры А и θ. Для тех, кто желает подробностей, есть исходники тестового проекта для iOS и OpenGL-ES.
Интеграция OpenGL и пользовательского интерфейса
Методы анимации, используемые в пользовательских интерфейсах, обычно выполнены по следующей схеме:
первоначальное состояние — фреймы анимации — конечное состояние
То есть берутся некие начальное и конечное состояния анимированного объекта интерфейса, сам объект на время анимации обычно либо прячется, либо перекрывается объектом, который рендерит фреймы перехода, по окончании рендерер прячется и показывается объект в конечном состоянии.
В применении к перелистываемым страницам первоначальное и конечное состояния соответствуют двум последовательным разворотам страниц книги.
То есть алгоритм анимации должен быть следующим:
— взять текстуры текущего разворота страниц
— выполнить прокрутку страниц offscreen
— взять текстуры следующего разворота страниц из offscreen buffer
— перекрыть разворот страниц OpenGL рендерером
— запустить анимацию
— по завершении спрятать OpenGL рендерер и показать следующий разворот, который находится в offscreen.
В качестве рендререра OpenGL я использовал класс
NSOpenGLView, обладающий собственным OpenGL контекстом и несколькими удобными методами для управления этим контекстом.
В зависимости от направления перелистывания, рендерер инициализируется четырьмя текстурами страниц, — две статические текстуры и две динамические, деформируемые с помощью вышеприведенного алгоритма.
Рассмотрим перелистывание страницы справа-налево:
// создаём таймер анимации (об этом объекте чуть ниже)
// передаём ему указатель на наш рендерер
FlipAnimation *animation = [[FlipAnimation alloc] initWithDuration:1.0f
animationCurve:NSAnimationEaseInOut
view:rendererView];
// это значит, что startAnimation не выходит, пока анимация
// не закончена
[animation setAnimationBlockingMode:NSAnimationBlocking];
[animation startAnimation];
[animation release];
// наконец-то показываем наш рендерер, перекрывая текущий разворот
// книги такой же картинкой, только отрисованной в OpenGL
[rendererView setHidden:NO];
}
- (void)setCurrentProgress:(NSAnimationProgress)progress
{
// рендеринг анимации, значение progress меняется от 0 до 1
[rendererView updateTime: progress];
}
@end
Анимацией легко управлять и без таймера, например используя события мыши или тачпада. Так достигается эффект «перетаскивания» страницы, достаточно посылать рендереру сообщения updateTime: из обработчика событий.
Код рендерера OpenGL я приводить не буду, так как он банален. Маппинг текстур выполнен страйпами треугольников, добавлена подсветка сцены анимации.
Ну и в заключение вот как это выглядит в работе (извиняюсь за качество):
Совсем недавно Mutado выложили альфу замечательного контрола для iOS Mutado.PaperStack (по ссылке можно увидеть видео в действии и ссылку на GitHub)
Правда малость сыроват пока, но при желании допилить можно(и нужно).
Главное что безопасен для релиза в AppStore, начиная от iOS 3.2.
ИМХО, именно из-за таких «мотивов» крайние версии софта при запуске отжирают больше памяти, чем было установлено на компах, где использовались их предки (причем без большой разницы в функционале)
это было глобальный комментарий, не применительно к технологии, пример работы с которой вы показали, а в целом ко всей индустрии «сделать штоб красиво»
Никогда не понимал людей, которые «копят» ресурсы. ИМХО, есть в этом что-то от Шейлока или Гарпагона: навесить на систему стопиццот гигабайт оперативной памяти и ревнительно сидеть над открытым Activity Monitor-ом, — не дай бог какой-нить процесс откушает пару лишних мегабайт…
Я считаю, памятью, процессором, как и деньгами, нужно пользоваться. Память — она не должна висеть в системе пустой нагрузкой на блок питания. Это не значит, что нужно писать говнокод, который будет расходовать ее почем зря, — это другая крайность. А крайности вещь нехорошая. Ищите золотую середину и пользуйтесь ресурсами, которые у вас есть.
Вспоминать седое прошлое, когда на 386SX было 4 мегабайта и «все отлично работало», — глупо. Было так, а теперь иначе.
Соглашусь полностью с этим замечаниям, эх если бы были еще стандарты сколько у тебя должно быть памяти и т.п. Приложения же не пишут, как игры, сколько им надо оперативной памяти, процессора, состояние системы (критично для Windows).
Хорошо когда есть еще целевое устройство например Ipad 2, а если запустят на Ipad1, а там по-другому, что делать?
Я вот все жду-жду время когда же наконец всем приложениям будет хватать памяти, процессора, но все время появляется самая крутая ОС, крутой редактор, красивые приложения, так что в конце становится тормознутая ОС, падающий редактор и некрасивые приложения :(
ЗЫ: это я на маркетинг жалуюсь, надо же новые устройства продавать ;)
Спасибо за статью, много работал в области разработки приложений для электронных журналов, поэтому было очень интересно.
Реализация через openGl достаточно оригинальная, но все равно есть хороший, стабильный движок на CG.
Пока лучше его сложно было найти с реализаций изгиба, переворота, теней: Leaves project
Пример скриншота работы есть в статье: http://habrahabr.ru/blogs/macosxdev/131855/
Это не айс. Эту работу должен делать OpenGL, CPU не должен быть нагружен графикой, когда в системе есть мощный GPU, который практически бездействует. Этим должен заниматься исключительно GPU, иначе будет как писал freeznah чуть выше.
я не спорю! я лишь подчеркнул, что реализация именно правдоподобности изгиба, переворота страницы и теней лучше в движке, который указал по ссылке.
Решение намного более выгодное
раньше еще находил некий движок, работающий даже на iphone 3.2, тоже через CG, где лист мог гнуться как угодно, от той области, за которую ты начинал тянуть. Но лагов там было куча + ужасная пикселизация.
в openGl c aliasing будет полегче.
Вот PaperStack на OpenGL-ES, тоже на основе вышеописанного алгоритма, изменяя A и θ конуса можно добиться какого угодно изгиба.
Единственно, там через попу сделаны тени. Я конечно понимаю, в OpenGL работа с тенями довольно трудное дело, но зачем же текстурами-то тени городить? Оно конечно работает и красиво, но… не изящно что ли.
комментарии (24)