Pull to refresh

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

Development for 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!

Tags:OpenGL ESGLSLiOSанимация
Hubs: Development for iOS
Total votes 34: ↑30 and ↓4 +26
Views16.5K

Popular right now