Покадровые анимации и шейдеры в iOS

от автора

При разработке 2D игр часто сталкиваешься с покадровыми анимациями, и чем выше их качество, тем больше памяти они потребляют. С такой проблемой мы столкнулись при рендере анимации волос персонажа — художники рисуют пол сотни кадров замечательной графики с кучей мелких деталей и это очень быстро занимает всю доступную память. Собрали, замеряли, получилось 4 текструы по 16 мегабайт каждая. Детализация графики того стоит, но многовато как-то для одной анимации 🙂

Нужно это все упаковать… Поразмыслили и на помощь пришла старая идея с частичным обновлением картинки — вряд-ли при анимации будет изменянятся вся область. Значит нужно разбить картинку на девять частей, восемь взять из базовой и центральную подменить необходимой модификацией. Примерно так:

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

В итоге реализовать задуманное получилось при помощи шейдеров — рисуем один прямоугольник с двумя текстурами и при помощи пиксельного шейдера решаем куда что показывать. Для реализации такого шейдера нам понадобится два sampler’а, две прямоуольные области на соответствующих текстурах и четыре значения — minX, mixY, maxX, maxY, по которым будет происходить переключение текстур.

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

Наши четыре параметра можно передать как один вектор из четырех компонентов согласно следующей схеме:

Дальше, слуедуя этой схеме, пишем пиксельный шейдер для переключения:

precision mediump float;  uniform sampler2D u_texture; // base texture uniform sampler2D u_overlay; // overlay texture uniform vec4 u_overlayRect; // in texture coordinates (minX, minY, maxX, maxY)  varying vec4 v_fragmentColor; varying vec2 v_texCoord; // base texture coordinates varying vec2 v_overlayTexCoord; // extrapolated overlay texture coordinates  void main() {     if (v_overlayTexCoord.x > u_overlayRect.x &&         v_overlayTexCoord.y > u_overlayRect.y &&         v_overlayTexCoord.x < u_overlayRect.z &&         v_overlayTexCoord.y < u_overlayRect.w)     {         gl_FragColor = v_fragmentColor * texture2D(u_overlay, v_overlayTexCoord);     }     else     {         gl_FragColor = v_fragmentColor * texture2D(u_texture, v_texCoord);     } } 

После этого сводим все кадры в одну текстуру (вместо четырех!)

И наслаждаемся анимацией без low memory warning!

ссылка на оригинал статьи http://habrahabr.ru/post/163775/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *