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

в 16:45, , рубрики: glsl, iOS, opengl es, анимация, разработка под iOS, метки: , , ,

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

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

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

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

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

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

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

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

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

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);
    }
}

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

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

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

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

Автор: nullbie

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js