При разработке 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!
Автор: nullbie