🌈 Шейдеры, часть 2. Делаем живой градиент
Продолжаю свою мини-серию про шейдеры.
В прошлый раз мы разбирались с mix(), сегодня пойдём дальше.
Градиент — это вещь, которую должен уметь каждый.
Я накидал простой пример (видите на видео), буду рассказывать на нем.
Как передавать параметры уже рассказывать не буду. Но в этом примере покажу, что в Compose у нас есть ShaderBrush. Это означает, что любой шейдер можно превратить в кисточку. И всё, что умеет рисовать Compose (drawRect, drawPath, drawCircle, текст, рамки) —
может быть нарисовано по правилам шейдера!
Разберем как сделать движушийся градиент. Частый ход использовать синус или косинус и передавать туда время. Чтобы вы на выходе получали значение от -1 до 1 волной.
Мы делим координаты пикселя на размер экрана — получаем uv в диапазоне 0..1
Тут получаем ту самую волну от -1 до 1, со скоростью 0.8 и шириной 4.0 (сколько волн поместится по ширине).
Так мы переводим синус в удобный диапазон
А это уже известная нам функция линейной интерполяции. Грубо говоря:
- t = 0 → цвет = color1
- t = 1 → цвет = color2
Вот и всё!
Теперь у нас градиент, который:
- горизонтально переливается из одного цвета в другой
- и одновременно движется благодаря синусу и времени
easy-peasy
#android #compose #ui #shaders #agsl #graphics #gradient
Продолжаю свою мини-серию про шейдеры.
В прошлый раз мы разбирались с mix(), сегодня пойдём дальше.
Градиент — это вещь, которую должен уметь каждый.
Я накидал простой пример (видите на видео), буду рассказывать на нем.
Как передавать параметры уже рассказывать не буду. Но в этом примере покажу, что в Compose у нас есть ShaderBrush. Это означает, что любой шейдер можно превратить в кисточку. И всё, что умеет рисовать Compose (drawRect, drawPath, drawCircle, текст, рамки) —
может быть нарисовано по правилам шейдера!
Разберем как сделать движушийся градиент. Частый ход использовать синус или косинус и передавать туда время. Чтобы вы на выходе получали значение от -1 до 1 волной.
vec2 uv = fragCoord / resolution;
Мы делим координаты пикселя на размер экрана — получаем uv в диапазоне 0..1
float wave = sin(time * 0.8 + uv.x * 4.0);
Тут получаем ту самую волну от -1 до 1, со скоростью 0.8 и шириной 4.0 (сколько волн поместится по ширине).
float t = 0.5 + 0.5 * wave;
Так мы переводим синус в удобный диапазон
return mix(color1, color2, t);
А это уже известная нам функция линейной интерполяции. Грубо говоря:
- t = 0 → цвет = color1
- t = 1 → цвет = color2
Вот и всё!
Теперь у нас градиент, который:
- горизонтально переливается из одного цвета в другой
- и одновременно движется благодаря синусу и времени
easy-peasy
#android #compose #ui #shaders #agsl #graphics #gradient
🔥21👍7💩2🥴1😍1
This media is not supported in your browser
VIEW IN TELEGRAM
🚨Code red! I repeat, CODE RED!! 🚨
Что-то очень странное сегодня происходит с приложением Drinkit.
Что-то очень странное сегодня происходит с приложением Drinkit.
1🔥45❤7🤔2😱2👾1
🔥 Live Updates в Dodo
Google на I/O представили новую фичу для Android — Live Updates.
Мы не могли пройти мимо 😅
Завезли и в Dodo Pizza и в Drinkit!
Серёга Орлов недавно сделал подробную статью — как мы прикрутили Live Updates в Dodo Pizza 🚚🍕
Всё уже в проде, и пользователи это реально видят, доставка теперь ощущается живее.
В Drinkit💙 мы тоже сделали Live Updates. И там получился свой прикольный кейс.
Про него бахну пост завтра 😉
#android #liveupdates #dodopizza #drinkit
Google на I/O представили новую фичу для Android — Live Updates.
Мы не могли пройти мимо 😅
Завезли и в Dodo Pizza и в Drinkit!
Серёга Орлов недавно сделал подробную статью — как мы прикрутили Live Updates в Dodo Pizza 🚚🍕
Всё уже в проде, и пользователи это реально видят, доставка теперь ощущается живее.
В Drinkit
Про него бахну пост завтра 😉
#android #liveupdates #dodopizza #drinkit
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍4
🐋🔔 Live Updates в Drinkit!
Мы в Drinkit затащили Live Updates на Android! Мне очень нравится, как это выглядит, прям кайф! ✨
Фичу делал Гриша Шимичев. Он год назад сделал наш "Live Activity" на Android (красивый rich notification, похожий на Live Activity на iOS). Теперь же, когда Google выкатил официальную штуку, затащил и её 😎
Кто не знает, что крутого в Live Updates:
- аккуратный chip в статус-баре
- прогресс-бар прямо в уведомлении
- поддержка Always-on Display
Мы добавили красоты: разных милых пиксельных зверят под разные статусы заказа 🐰🦊🐼
Если у вас Android 16+, приложение включает Live Updates.
Если ниже — будет наш старый красивый кастомный вариант.
Мне очень нравится, как всё получилось. И по внешнему виду, и по вниманию к нюансам.
Еще, у нас там интересный микс из пушей и ForegroundService для обновления статуса, но об этом расскажу как-нибудь позже 😉
💬 Как вам Live Updates? Вообще видите ли в нем ценность?
#android #notifications #drinkit #liveupdates
Мы в Drinkit затащили Live Updates на Android! Мне очень нравится, как это выглядит, прям кайф! ✨
Фичу делал Гриша Шимичев. Он год назад сделал наш "Live Activity" на Android (красивый rich notification, похожий на Live Activity на iOS). Теперь же, когда Google выкатил официальную штуку, затащил и её 😎
Кто не знает, что крутого в Live Updates:
- аккуратный chip в статус-баре
- прогресс-бар прямо в уведомлении
- поддержка Always-on Display
Мы добавили красоты: разных милых пиксельных зверят под разные статусы заказа 🐰🦊🐼
Если у вас Android 16+, приложение включает Live Updates.
Если ниже — будет наш старый красивый кастомный вариант.
Мне очень нравится, как всё получилось. И по внешнему виду, и по вниманию к нюансам.
Еще, у нас там интересный микс из пушей и ForegroundService для обновления статуса, но об этом расскажу как-нибудь позже 😉
💬 Как вам Live Updates? Вообще видите ли в нем ценность?
#android #notifications #drinkit #liveupdates
🔥21👍6🤮2💩1🦄1
🟢 Android Developers тоже ожидает доклад Ordering Coffee with Firebase AI
Предполагаю, что большинство читаталей канала живут в других городах. Но!
Если вы из Mersin, Istanbul, Izmir, Abu Dhabi (или Dubai) или Yerevan — приходите на конфу! Пообщаемся, затусим!🥳
Буду здесь:
🇹🇷 6 Dec — DevFest Mersin
🇹🇷 7 Dec — DevFest Istanbul
🇹🇷 13 Dec — DevFest Izmir
🇦🇪 15 Dec — Droidcon Abu Dhabi
🇦🇲 20 Dec — DevFest Yerevan
#devfest #droidcon #conference
Предполагаю, что большинство читаталей канала живут в других городах. Но!
Если вы из Mersin, Istanbul, Izmir, Abu Dhabi (или Dubai) или Yerevan — приходите на конфу! Пообщаемся, затусим!
Буду здесь:
🇹🇷 6 Dec — DevFest Mersin
🇹🇷 7 Dec — DevFest Istanbul
🇹🇷 13 Dec — DevFest Izmir
🇦🇪 15 Dec — Droidcon Abu Dhabi
🇦🇲 20 Dec — DevFest Yerevan
#devfest #droidcon #conference
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥33❤4👍3👎1
This media is not supported in your browser
VIEW IN TELEGRAM
❤18🤣7👍5💩3
⚡ Шейдеры, часть 3. Начинаем делать Glitch
Продолжаю мини-серию про шейдеры.
В прошлый раз мы делали живой градиент, сегодня начну подходить к теме Glitch — как сделать первый эффект из Upside-Down фичи.
Glitch строится на искажении картинки. И самое это волна.
Поэтому сделаем два маленьких примера.
1. Статическая волна
Что тут происходит:
- берём uv.y, вертикальную координату (номер строки пикселя)
- умножаем на 40, чтобы получить плотную волну
- пропускаем через sin(), получаем значение от -1 до 1
- сдвигаем пиксели по X пропорционально этой волне
В результате есть волна, но она стоит.
2. Анимированная волна
Здесь уже волна плывет. Вот почему:
- time * 5.0 это скорость движения волны
- синус теперь зависит не только от uv.y, но и от того, сколько времени прошло
- поэтому картинка начинает течь горизонтально
Это уже ближе к глитчу! В будущих постах буду дальше идти и дойдем до глитча!
#android #ui #compose #shaders #agsl #graphics #glitch
Продолжаю мини-серию про шейдеры.
В прошлый раз мы делали живой градиент, сегодня начну подходить к теме Glitch — как сделать первый эффект из Upside-Down фичи.
Glitch строится на искажении картинки. И самое это волна.
Поэтому сделаем два маленьких примера.
1. Статическая волна
float wave = sin(uv.y * 40.0);
float amplitude = 0.01 * intensity;
uv.x += wave * amplitude;
Что тут происходит:
- берём uv.y, вертикальную координату (номер строки пикселя)
- умножаем на 40, чтобы получить плотную волну
- пропускаем через sin(), получаем значение от -1 до 1
- сдвигаем пиксели по X пропорционально этой волне
В результате есть волна, но она стоит.
2. Анимированная волна
float wave = sin(uv.y * 40.0 + time * 5.0);
Здесь уже волна плывет. Вот почему:
- time * 5.0 это скорость движения волны
- синус теперь зависит не только от uv.y, но и от того, сколько времени прошло
- поэтому картинка начинает течь горизонтально
Это уже ближе к глитчу! В будущих постах буду дальше идти и дойдем до глитча!
#android #ui #compose #shaders #agsl #graphics #glitch
👍13🔥7🖕3💩2🤡2
⚡ Шейдеры, часть 4. Почти-Glitch! 📺
Продолжаю серию про шейдеры.
Сегодня сделаем шаг к полноценному глитчу.
Идея простая. Нам надо разрезать экран на горизонтальные полосы и двигать эти полосы, а не просто пиксели отдельно.
Погнали!
- тут делим экран на 12 полос
- каждая полоса получает свой индекс sliceIndex
- теперь можем двигать сразу всю полосу, как единый блок
- для каждой полосы мы считаем случайное число r
- если оно больше 0.7 (придумайте себе любое, я вообще много захардкодил тут), то полоса поедет
- чем больше r, тем сильнее её сдвигает
- каждая полоса либо смещается влево, либо вправо. Метод randDirection можете на скринах посмотреть. Он основан на методе rand, который тоже есть на скринах. Это скорее не rand, а больше как хеш, который с течением времени выглядит как будто рандом.
- получается случайно, но стабильно, потому что направление привязано к индексу полосы
А теперь оживим это. Нам надо сдвигать полосы резко и рандомно со временем. Создать эффект помех. Для этого изменим r:
- floor(time * 10.0) значит глитч обновляется 10 раз в секунду
- каждый апдейт это новое случайное значение
- но еще прокидываем из Kotlin кода realRandom, чтобы не было повторяющихся циклов. Если его не прокинуть, то наш рандом будет каждый раз одинаковый
🐸 Ну и уже почти то что надо!
📺 Полосы двигаются, как будто помехи!
Дальше немного украсим их и Glitch будет готов!
#android #ui #compose #shaders #agsl #graphics #glitch
Продолжаю серию про шейдеры.
Сегодня сделаем шаг к полноценному глитчу.
Идея простая. Нам надо разрезать экран на горизонтальные полосы и двигать эти полосы, а не просто пиксели отдельно.
Погнали!
int slices = 12;
float sliceHeight = 1.0 / float(slices);
int sliceIndex = int(uv.y / sliceHeight);
- тут делим экран на 12 полос
- каждая полоса получает свой индекс sliceIndex
- теперь можем двигать сразу всю полосу, как единый блок
float r = rand(float(sliceIndex));
if (r > 0.7) {
sliceGlitch = (r - 0.7) * 4.0 * intensity;
}
- для каждой полосы мы считаем случайное число r
- если оно больше 0.7 (придумайте себе любое, я вообще много захардкодил тут), то полоса поедет
- чем больше r, тем сильнее её сдвигает
float direction = randDirection(sliceIndex); // либо -1, либо +1
uv.x += sliceGlitch * 0.08 * direction;
- каждая полоса либо смещается влево, либо вправо. Метод randDirection можете на скринах посмотреть. Он основан на методе rand, который тоже есть на скринах. Это скорее не rand, а больше как хеш, который с течением времени выглядит как будто рандом.
- получается случайно, но стабильно, потому что направление привязано к индексу полосы
А теперь оживим это. Нам надо сдвигать полосы резко и рандомно со временем. Создать эффект помех. Для этого изменим r:
float t = floor(time * 10.0);
float r = rand(float(sliceIndex) + t + realRandom * 100.0);
- floor(time * 10.0) значит глитч обновляется 10 раз в секунду
- каждый апдейт это новое случайное значение
- но еще прокидываем из Kotlin кода realRandom, чтобы не было повторяющихся циклов. Если его не прокинуть, то наш рандом будет каждый раз одинаковый
Дальше немного украсим их и Glitch будет готов!
#android #ui #compose #shaders #agsl #graphics #glitch
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍3👏2❤1😁1
Завтра DevFest Armenia 2025
Последняя из 5 моих конференций подряд, финал в Ереване 🇦🇲
Вернусь и продолжу писать более мобильные посты 😊 за мной ещё должок дописать про шейдеры.
На DevFest Armenia буду рассказывать нашу реальную историю:
- как мы используем Firebase AI в приложении Drinkit
- как писать agentic loop прямо в аппе
- и как делать это без сжигания токенов и денег
Если кто-то из вас сейчас в Ереване — обязательно приходите 🙌
Буду рад познакомиться, пообщаться, обсудить AI, мобилу и жизнь.
📅 Dec 20, 2025
📍 Woods Center, Yerevan (Jrvezh area)
🔗 https://devfest.am/2025/
Увидимся завтра! ☕🚀
#devfest #devfestarmenia #conference
Последняя из 5 моих конференций подряд, финал в Ереване 🇦🇲
Вернусь и продолжу писать более мобильные посты 😊 за мной ещё должок дописать про шейдеры.
На DevFest Armenia буду рассказывать нашу реальную историю:
- как мы используем Firebase AI в приложении Drinkit
- как писать agentic loop прямо в аппе
- и как делать это без сжигания токенов и денег
Если кто-то из вас сейчас в Ереване — обязательно приходите 🙌
Буду рад познакомиться, пообщаться, обсудить AI, мобилу и жизнь.
📅 Dec 20, 2025
📍 Woods Center, Yerevan (Jrvezh area)
🔗 https://devfest.am/2025/
Увидимся завтра! ☕🚀
#devfest #devfestarmenia #conference
👍11🔥5🤮1
В прошлой части мы сделали самое важное: разрезали экран на полосы и научили их прыгать во времени.
Чтобы стало похоже на реальный глитч, давайте добавим суперпопулярный эффект в стиле VHS или плохой линзы — RGB split, он же Chromatic aberration.
🌈 RGB split — разъезжаются цветовые каналы
Это очень узнаваемый приём: красный и синий каналы чуть-чуть уезжают относительно зелёного.
Первая картинки — это демо эффекта. Вторая — простонаглядный пример Chromatic aberration из интернета.
Допишем вот такой код в наш шейдер:
float splitAmount = 0.005 * intensity;
vec2 uvR = baseUv + vec2(-splitAmount, 0.0);
vec2 uvG = baseUv;
vec2 uvB = baseUv + vec2( splitAmount, 0.0);
vec3 cR = image.eval(uvR * imageSize).rgb;
vec3 cG = image.eval(uvG * imageSize).rgb;
vec3 cB = image.eval(uvB * imageSize).rgb;
return vec3(cR.r, cG.g, cB.b);
Разберем что здесь происходит. Мы берём красный цвет не из этого пикселя, а из соседнего слева.
Синий из соседнего справа. Пока картинка однотонная, то разницы почти нет.
Но на краю объекта слева и справа уже разные цвета. Каналы перестают совпадать, и появляется цветная рамочка.
То есть финальный цвет получается так:
- final.r = левый_пиксель.r
- final.g = центральный_пиксель.g
- final.b = правый_пиксель.b
Представь, что у тебя большой однотонный фон, например серый. Внутри этого фона пиксель слева почти такой же, пиксель по центру почти такой же, пиксель справа почти такой же. Значит собранный цвет почти не отличается от оригинала. Но край объекта — это место, где картинка меняется сильно и быстро: слева ещё фон, а справа уже объект. То есть соседние пиксели сильно отличаются.
Я выложил свой Glitch modifier, можно посмотреть подробнее и этот код, и полную версию модифайера.
Еще один шаг и наш глитч полностью готов!
#android #compose #ui #shaders #agsl #graphics #glitch
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16👍1
This media is not supported in your browser
VIEW IN TELEGRAM
👾 Шейдеры. Финал 👾
Ну всё, собираем финальный глитч из всех частей, которые мы делали в прошлых постах.
У нас есть база (slices + смещение + RGB split), и давайте добавим две прикольные штуки, которые сделают эффект реально похожим на VHS: noise и color bars.
📺 Noise
Делаем типо грязный сигнал поверх всего. Тут будет не просто, я не сразу разобрался.
Тут мы рисуем вертикальные линии, которые немного дистортнутые. Разберем по строкам:
1 - узнаём, в какой части полосы сейчас находится пиксель
2 - делаем эффект через строку, а не каждую
3 - делаем у каждой строки свой шум, слегка сдвигая по X
4 - в итоге если mask = 0, эффекта нет, но если всё включено, пиксель чуть темнее
Если иначе сказать, то мы берём каждую вторую горизонтальную линию, слегка случайно затемняем её, и делаем это по-разному для каждой строки.
🟩🟪🟦 Color bars
Финальный трюк. Иногда вместо изображения появляется цветная заливка полосой, как будто поток реально развалился.
По мне так выглядит как настоящий артефакт декодинга!
Вот и всё 👾
Теперь у нас полноценный глитч, который можно настраивать параметрами!
Смотрите полный код в репе
#android #compose #ui #shaders #agsl #graphics #glitch
Ну всё, собираем финальный глитч из всех частей, которые мы делали в прошлых постах.
У нас есть база (slices + смещение + RGB split), и давайте добавим две прикольные штуки, которые сделают эффект реально похожим на VHS: noise и color bars.
📺 Noise
Делаем типо грязный сигнал поверх всего. Тут будет не просто, я не сразу разобрался.
float line = fract(uv.y * imageSize.y * 0.5);
float mask = step(0.5, line);
float noise = rand2(vec2(uv.x * 40.0, floor(uv.y * imageSize.y)));
return mix(1.0, 0.85, mask * noise * noiseIntensity);
Тут мы рисуем вертикальные линии, которые немного дистортнутые. Разберем по строкам:
1 - узнаём, в какой части полосы сейчас находится пиксель
2 - делаем эффект через строку, а не каждую
3 - делаем у каждой строки свой шум, слегка сдвигая по X
4 - в итоге если mask = 0, эффекта нет, но если всё включено, пиксель чуть темнее
Если иначе сказать, то мы берём каждую вторую горизонтальную линию, слегка случайно затемняем её, и делаем это по-разному для каждой строки.
🟩🟪🟦 Color bars
Финальный трюк. Иногда вместо изображения появляется цветная заливка полосой, как будто поток реально развалился.
float rnd = rand2(vec2(intensity * realRandom, sliceY));
if ((rnd * 2.5 + 0.5) < intensity) {
if (realRandom > 0.67) return vec4(0.0, 1.0, 0.3, 1.0); // green
else if (realRandom > 0.33) return vec4(1.0, 0.0, 0.85, 1.0); // magenta
else return vec4(0.0, 0.85, 1.0, 1.0); // cyan
}
По мне так выглядит как настоящий артефакт декодинга!
Вот и всё 👾
Теперь у нас полноценный глитч, который можно настраивать параметрами!
Смотрите полный код в репе
#android #compose #ui #shaders #agsl #graphics #glitch
🔥15👍5
Вышла классная статья на Хабре от Андрея Гришанова (Dodo Engineering) про Perfetto.
Всем, кто хочет изучать лаги, junk-фреймы и прочее — вэлкам!
Что в статье:
- что такое Perfetto и чем он отличается от Profiler
- как писать свои trace-секции в коде
- как снимать трейсы через Studio / System Tracing / CLI
- как потом анализировать это всё через SQL
- и как это автоматизировать и прикрутить в CI
На мой взгляд Perfetto супер крутая штука. Когда первый раз попробовал, то больше не понимал, зачем пользоваться профайлером студии :)
💬
У кого были интересные кейсы по работе с Perfetto? Расскажите, мне очень интересно! Может вы гоняете на CI и находили реальную деградацию? Или может вы его на проде запускаете? Или просто историю, как поймали неуловимый баг?
#android #performance #perfetto #profiling #mobiledev #engineering
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥5❤3🤝1