Метод · «Практика» · 3D/VFX · прямой цикл
Живая иллюстрация:
прямой бесшовный цикл
Как готовая сгенерированная иллюстрация для приложения «Практика» превращается в 45-секундную анимацию, которая повторяется без видимого стыка. Водопад всё время падает вниз, облака и туман живут своей жизнью, а скалы, кристалл и архитектура остаются ровно такими, какими их нарисовали. Ниже — весь путь от исходного изображения до видеофайла: инструменты, отвергнутые варианты, арифметика цикла и ограничения метода.

Куда смотреть
Движение намеренно спокойное — его легко пропустить, если ждать «кино». Сравните видео со статичным исходником:
- Большой водопад слева — струи ускоряются вниз, у подножия поднимается облако брызг и вспухает, когда до низа долетает очередная «волна» воды.
- Облака и туман — движение внутри облачных масс; силуэты гор при этом неподвижны.
- Озеро и малые водопады — мягкая рябь на воде; по малым водопадам скользят вниз блики и капли.
- Большая планета — медленно поворачивается рисунок поверхности, а силуэт и освещение остаются прежними.
- Скалы, кристалл, колонны, цветы — неподвижны намеренно: это «жёсткая» геометрия исходника.
Стек
Чем это сделано
Генерация исходного арта, движение воды и воздуха, трёхмерные эффекты и сборка видео — четыре разные задачи. Для каждой подобран свой инструмент.
OpenAI image_gen
Широкая сцена 2:1 по текстовому заданию и референсу стиля. Оригинал хранится без изменений.
Text2Cinemagraph
Предобученный генератор видео из исследования «Text-Guided Synthesis of Eulerian Cinemagraphs» (SIGGRAPH Asia 2023): переносит признаки изображения по полю движения и декодирует кадры.
Модель в коде
Класс networks.SPADEUnet4SoftmaxSplating и опубликованные веса video_net_G.pth из официального репозитория, коммит 57a8159. Из исходного конвейера взят только этот генератор: создание картинки через Stable Diffusion, сегментация ODISE и предсказание движения по тексту в маршрут не входят.
Blender 5.2.3 LTS
Процедурное водяное полотно, объёмное облако брызг, частицы на Geometry Nodes.
Python: NumPy, OpenCV, Pillow
Маски и поля движения, изолированный вход для нейросети, вращение планеты, сборка слоёв в линейном свете, измерения.
FFmpeg 8.0
WebP для приложения, кодирование H.264, замедление по временным меткам, полное декодирование при проверке.
Нейросинтез и рендер выполнялись на одной видеокарте NVIDIA RTX PRO 6000 Blackwell (96 ГБ). Арт-направление и требования к движению — авторские; код масок, сцены Blender, сборки и проверок написан с помощью ИИ-ассистентов для программирования Codex и Claude. Сами кадры языковые модели не рисуют: их дают нейросетевой генератор и рендер Blender.
Задача
Зачем оживлять сцену и в чём трудность
В приложении «Практика» у программ и наборов упражнений есть свои сцены — широкие панорамы в духе детализированной фэнтези-игры. Всего таких сцен 38. Статичная картинка работает, но живая сцена сильнее передаёт атмосферу: вода течёт, облака дышат, мир ощущается настоящим. Задача формулируется просто — оживить готовую иллюстрацию.
Сложность в том, что одновременно должны выполняться три требования:
- Сохранить картинку. Композиция, детали, цвет и свет арта не должны «поплыть». Скала не может стать резиновой, кристалл — изменить форму, планета — сдвинуться с места.
- Заметное, но спокойное движение. Вода течёт вниз, туман и облака меняются изнутри, и всё это выглядит естественно, а не как дрожание пикселей.
- Долгий повтор без шва. Сцена может оставаться на экране минутами, поэтому конец ролика должен незаметно переходить в начало — без скачка, вспышки или «перемотки назад».
Самым трудным оказалось третье требование. Именно оно определило весь метод.
Исходное изображение
Как появился исходный арт
Сцену создал генератор изображений OpenAI image_gen. Генерация шла по референсу: на вход вместе с заданием подавалась одна из уже готовых сцен приложения — только как образец качества и палитры, а не как картинка для перерисовки. Так новые сцены остаются в одном визуальном мире.
Промпт написан не как «волшебная фраза», а как техническое задание на кадр. В нём есть назначение сцены — декоративная панорама для программы духовного роста, а не значок прогресса. Есть описание стиля, который нужно взять у референса: реалистичная фэнтези-графика, глубина, бирюза и изумруд, тёплое золото света, космическое небо. Описано содержание: круглое каменное святилище над зеркальным бирюзовым озером, светящийся кристалл над центральным бассейном, скалы и цветы на переднем плане, тонкие водопады между дальними террасами, луны и их отражения. Задан формат 2:1 с условием, что главный объект читается на экране шириной 390 пикселей. И перечислены запреты: никаких людей, текста, интерфейса и религиозных символов.
Фрагмент реального промпта (на английском)
Промпт приведён без изменений, кроме сокращений, отмеченных многоточием.
One polished wide cinematic environment illustration for a mobile spiritual-growth program. Input reference STYLE/QUALITY ONLY: detailed realistic fantasy 3D game rendering with immense atmospheric depth, warm amber and jade-turquoise tones, beautiful material detail and a cosmic sky. New scene: serene circular stone sanctuary floating just above a mirror-still turquoise lake, a subtle luminous crystal suspended above its open central pool, soft rays illuminating mist, graceful cliffs and flowering plants frame foreground, delicate waterfalls connect distant terraces. Cosmic moons reflected in water, natural nonreligious sanctuary… Wide 2:1 panorama, visual focal pool/crystal readable on 390px screen… No text, no UI, no religious emblems, no people; decorative program art, not rank progression.Из вариантов выбирают вручную: читается ли кристалл на маленьком экране, нет ли артефактов, согласуются ли отражения со светом, совпадает ли стиль с соседними сценами. Выбранный кадр становится эталоном для всего, что идёт дальше.
Исходный PNG размером 1774×887 пикселей хранится без изменений. Для приложения из него через FFmpeg сделана копия в WebP — именно её пользователь видит как статичную сцену. Анимация строится от этой же копии: её пиксели декодируются, фиксируются контрольной суммой и приводятся к рабочей сетке 1280×640. Так живая сцена совпадает со статичной, а оригинал остаётся нетронутым.
Генерация изображения и анимация — разные этапы с разными инструментами. Генератор изображений не делает видео, а видеомодель не перерисовывает сцену.
Отвергнутые пути
Что не сработало и почему
До финального маршрута было несколько попыток. Каждая что-то дала, но ни одна не выполнила все три требования сразу. Это выводы для этой задачи, а не приговор инструментам.
Нейросеть оживляет весь кадр
Первым маршрутом была модель Kandinsky 5.0 I2V Pro (SFT): на входе картинка и описание движения, на выходе нативный 5-секундный клип. Сцены действительно оживали — шевелилась листва, менялись облака и вода.
Вывод: естественное движение внутри клипа ещё не делает его циклом. Последний кадр не совпадает с первым, и на повторе остаётся жёсткий стык. Искусственно растягивать короткий клип мы не стали.
Вперёд, а потом назад
Классический способ спрятать шов — склеить клип с его обратной копией. Для облаков такое иногда терпимо.
Вывод: вода начинает течь вверх. Для водопада это недопустимо, поэтому реверс, пинг-понг и обратное вращение полностью исключены из метода.
Растворить конец в начале
Последние секунды можно наложить на первые с плавным переходом, и формально шов исчезает.
Вывод: глобальный кроссфейд даёт двойную экспозицию и «дыхание» резкости, и глаз замечает пульсацию через равные промежутки. Затуханием всей сцены шов здесь не маскируется: плавно появляются и исчезают только отдельные частицы.
Сдвигать пиксели по полю движения
Ранние попытки двигали содержимое всего кадра по рассчитанному полю движения.
Вывод: вместе с водой «уезжают» соседние пиксели. Стволы и куски скал проступают призраками в облаках, а планеты становятся упругими.
Морфинг и покачивание целиком
Соблазнительно слегка покачивать или деформировать и твёрдые объекты — кроны, скалы, планеты, — чтобы сцена казалась живее.
Вывод: у нарисованной скалы нет объёма и обратной стороны, поэтому любое её движение читается как деформация, а крона, качающаяся целиком, выглядит резиновой. Твёрдое остаётся неподвижным; живут только жидкости и воздух.
Гибрид вместо новой волшебной модели
Решение дала не смена нейросети, а разделение сцены на части. Что-то остаётся исходником, что-то синтезирует нейросеть, что-то строится как 3D/VFX, и всё собирается с общим периодом. К такому гибриду мы пришли итерациями, а не одной удачной генерацией.
Схема метода
Конвейер целиком
Восемь этапов — от изображения до файла. Нейросеть и рендер работают только там, где без них не обойтись, а результат каждого этапа можно проверить отдельно.
Исходный арт
Техническое задание на кадр, генерация по референсу стиля, ручной отбор.
Фиксация исходника
PNG хранится без изменений; анимация строится от WebP-копии приложения, пиксели которой зафиксированы контрольной суммой.
Маски и поля движения
Маски под эту сцену: вода, воздух, планета, жёсткий фон, окклюдеры; для жидкостей — направленные поля движения.
Нейросинтез воды и воздуха
Генератор получает изолированные облака и туман или озеро вместе с заданным полем движения и выдаёт замкнутые слои: вода — 6 с, воздух — 10 с.
3D/VFX водопада
Процедурное водяное полотно, объёмное облако брызг и частицы, рендер на GPU.
Сборка слоёв
Композитинг в линейном свете; передний план и твёрдые объекты сохраняют пиксели исходника.
Цикл и темп
Общий период — 30 с, 750 кадров; замедление в 1,5 раза только временными метками.
Проверка
Повторы и стык глазами; полное декодирование, счёт кадров, направление потока, совпадение пикселей вне зоны эффекта.
Подготовка сцены
Маски и неподвижная камера
Главное правило метода: исходная иллюстрация — неподвижный задний план. Камера не двигается, кадр не панорамируется и не приближается. Всё, что в реальности твёрдое, — скалы, колонны святилища, кристалл, стволы, цветы — остаётся пикселями исходника.
Для этого сцену размечают семантическими масками: поверхность озера, облака и туман, падающая вода, большая планета и передний план, который перекрывает воду, — листья, камни, цветы. Важен порядок перекрытий: брызги водопада должны уходить за камни переднего плана, а не рисоваться поверх них.
Маски подгоняются под конкретную картинку. Это внимательная ручная работа, а не универсальный автоматический сегментатор: у другой сцены будут свои водопады, свои окклюдеры и свой свет.
Изолированный вход для нейросети
Если отдать модели кадр целиком, она «видит» рядом с облаком ствол дерева и вполне может унести его кусок в атмосферу. Поэтому для облаков и тумана вход готовится отдельно: облачные области вырезаются, а всё вокруг них заполняется цветом ближайшего облачного пикселя. В таком входе нет ни скал, ни стволов, ни планет, и утащить твёрдое модели просто неоткуда. Это вспомогательный слой — в итоговый кадр он не попадает.
С озером задача проще: поле движения отлично от нуля только на воде, а в итоговый кадр из этого слоя возвращается только маска озера. Малые водопады нейросеть не анимирует вовсе — по ним скользят вниз частицы с конечной жизнью поверх неподвижного рисунка.
Нейросинтез · Text2Cinemagraph
Вода и воздух
Для поверхности озера, облаков и тумана используется генератор видео из исследовательского проекта Text2Cinemagraph. В оригинале это полный конвейер: он сам рисует картинку диффузионной моделью, сам её сегментирует и сам предсказывает движение по текстовому описанию. Здесь нужна только последняя, анимационная часть: картинка уже есть, маски сделаны под неё, а движение задано вручную.
Генератор работает по принципу «эйлеровых» синемаграфов. Движение описывается одним неподвижным полем скоростей: в каждой точке воды или воздуха указано, куда и с какой скоростью там всё течёт. Это поле мы не поручали угадывать модели, а нарисовали по маскам: вода в озере слегка дрейфует, от точек падения расходятся кольца, облака плывут вбок, туман чуть поднимается.
- Признаки. Кодировщик превращает входное изображение в многоканальную карту признаков и для каждой точки оценивает её «вес» — насколько она важна, если в одну точку попадут несколько.
- Смещение. Чтобы получить кадр номер t, поле скоростей пошагово интегрируется t раз: так каждая точка узнаёт, куда она уехала к этому моменту.
- Перенос вперёд и назад. Признаки переносятся из исходника вперёд по полю и одновременно «из конца периода» назад, против поля. Перенос сделан мягким (softmax splatting): если несколько точек попадают в одно место, их вклады смешиваются пропорционально весам, и точка с большим весом преобладает.
- Смешивание и декодирование. Два переноса смешиваются с весом, который линейно меняется от начала периода к концу, и декодер превращает признаки обратно в изображение.
Хитрость цикла в том, что «начало» и «конец» здесь — одно и то же изображение. В первом кадре работает только перенос вперёд на ноль шагов, в последнем — только перенос назад на ноль шагов, поэтому оба дают один и тот же кадр. Видимое движение при этом всё время направлено вперёд: вода не возвращается назад, а к концу периода просто приходит в исходное состояние. Всё это происходит внутри масок воды и воздуха — это не два куска видео, наложенных на весь кадр, поэтому текстура воды и облаков движется изнутри, а не сдвигается целым куском.
У каждого слоя свой замкнутый период: поверхность воды — 6 секунд (150 кадров), атмосфера — 10 секунд (250 кадров). Нейросеть считает каждый период один раз; повторы в сцене берутся из уже готовых кадров.
Подробнее: разрешение и цвет
- Сеть работает на сетке 1024×512. Готовые кадры слоя масштабируются к размеру исходного арта и вклеиваются по маскам с мягкими краями.
- Для воды из результата сети берётся только яркость, а цвет — из исходника, с плавной калибровкой по окрестности. Для воздуха калибруется весь цвет. Так нейросеть добавляет движение, но не меняет палитру.
- Вес смешивания переносов вперёд и назад для кадра t равен α = 1 − t / N, где N — длина периода в кадрах.
Планета вращается, но не движется
Большая планета анимирована отдельно, без нейросети. Изображение её поверхности спроецировано на сферу, и сфера медленно поворачивается вокруг оси. Силуэт, положение центра и освещение остаются как на исходнике — меняется только рисунок поверхности. Слой планеты замыкается за 30 секунд. Малые планеты оставлены неподвижными.
Почему этого оказалось мало
Слоистая версия — нейросинтез воздуха и воды плюс частицы на водопадах — уже выглядела живой. Но главный водопад в ней оставался почти нарисованным: двигались блики и капли, а не сама вода. Поэтому его построили заново средствами 3D/VFX.
3D/VFX
Водопад, построенный в Blender
Самый заметный объект сцены — большой водопад слева. Его не «оживляли» нейросетью, а построили заново как стилизованный визуальный эффект в Blender и отрендерили в Cycles на видеокарте.
Камера ортографическая и зафиксирована так, что пиксель рендера совпадает с пикселем рабочей сетки. Рендерится только область водопада, с запасом разрешения.
Водяное полотно
Вода — процедурный материал на геометрической «карточке» по форме нарисованного водопада. Текстурная координата — баллистическое время полёта от кромки: d = v₀·τ + g·τ²/2. Поэтому у кромки струи плотные и медленные, а ниже ускоряются и вытягиваются, как у настоящей падающей воды. Начальную скорость и ускорение подбирают под рисунок конкретного водопада.
Как заставить материал двигаться только вниз и при этом через 10 секунд вернуться точно в исходное состояние? Координата материала p = τ − t, то есть время полёта минус текущее время, превращается в угол и укладывается на окружность в двух измерениях четырёхмерного шума. Вдоль окружности шум периодичен, а сама координата всё время смещается вниз. Через период окружность замыкается, и рисунок струй совпадает с первым кадром — без кроссфейда, пинг-понга и обратного хода.
По полотну бегут «волны» — периодические утолщения потока, заданные несколькими гармониками с целым числом колебаний за период. Базовый цвет берётся из самого нарисованного водопада, сглаженного вдоль падения, поэтому новая вода сохраняет палитру исходника.
Облако брызг
У подножия — объёмное облако: лучи трассируются через процедурную плотность, с самозатенением и контровым солнцем. Клубы расходятся от линии удара, а плотность облака повторяет те же «волны» воды с задержкой ровно на время падения. Когда утолщение потока долетает до низа, облако вспухает.
Брызги и морось
Три набора частиц на Geometry Nodes, каждый с конечной жизнью: капли, срывающиеся по фронту водопада; выбросы, которые взлетают от удара и падают обратно; дрейфующая морось. У частиц целое число жизненных циклов за период, поэтому плавно появляются и исчезают только они сами — затухать всей сцене не нужно.
Параметры водопада в этой сцене
- начальная скорость у кромки v₀ — 20 пикселей в секунду;
- ускорение g — 59,5 пикселя в секунду²;
- падение от кромки до основания — 1,7 секунды;
- период — 10 секунд, 250 кадров при 25 кадрах в секунду;
- рендер области водопада с двукратным запасом разрешения и последующим уменьшением, около 5 ГБ видеопамяти.
Значения подобраны под рисунок этой иллюстрации; у другого водопада они будут другими.
Это стилизованный VFX, а не гидродинамический расчёт: никаких уравнений Навье — Стокса и никакой полной 3D-реконструкции картины. Цель — правдоподобное, направленное и замкнутое движение в стиле исходного арта.

Сборка
Гибрид, а не кадр из Blender
Финальный кадр сделан не целиком в Blender. Слои собирает короткий Python-скрипт на NumPy и OpenCV:
- базой служит слоистая сцена — с анимированным воздухом, водой озера, частицами малых водопадов и вращением планеты;
- отрендеренное полотно воды заменяет только маску тела большого водопада;
- облако брызг и частицы накладываются в линейном световом пространстве, а не в гамма-кодированном, поэтому полупрозрачный туман смешивается с фоном правдоподобно;
- окклюдеры переднего плана — камни, цветы, листья — сохраняют пиксели базового кадра, и брызги уходят за них;
- за пределами зоны эффекта каждый пиксель всех 750 кадров совпадает с базовым кадром бит в бит; сама зона занимает около 1,2 % площади кадра.
В итоговой сцене сочетаются нейросетевое движение озера и облаков, процедурные эффекты водопадов и вращение поверхности планеты. Слои собираются в единую композицию.
Бесшовность
Арифметика прямого цикла
У каждого слоя свой период. Сцена замыкается, только когда все слои одновременно возвращаются в исходное состояние, то есть через наименьшее общее кратное их периодов.
НОК(6, 10, 10, 30) = 30 секунд, или 750 кадров при 25 кадрах в секунду. Компонент водопада с периодом 10 секунд укладывается в сцену ровно три раза. При этом 10-секундный фрагмент сам по себе — не замкнутая сцена: вода озера за это время проходит 1⅔ своих цикла, планета — треть периода, и на стыке был бы скачок. Стык проверяется только на полном 30-секундном периоде.
Замедление без новых кадров
Собранный цикл длится 30 секунд. Чтобы фоновая сцена выглядела спокойнее, темп уменьшен в полтора раза. Промежуточные кадры не генерировались, и видео не перекодировалось. FFmpeg копирует закодированный поток как есть и только умножает временные метки каждого кадра на 3/2. Те же 750 кадров показываются теперь 45 секунд, примерно 16,7 кадра в секунду. Данные всех кадров идентичны исходным, порядок сохранён, а метки растянуты ровно в 1,5 раза.
ffmpeg -i scene30s.mp4 -map 0:v -c copy -video_track_timescale 90000 \
-bsf:v "setts=pts=PTS*3/2:dts=DTS*3/2:duration=DURATION*3/2" scene45s.mp4
После такой операции стоит сверить кадры: старая встроенная сборка FFmpeg с обобщённой формулой для меток теряла два кадра в 10-секундном фрагменте из 250 кадров. Поэтому после ретайминга проверяются число, порядок и содержимое всех кадров.
На странице стоит ровно один 45-секундный цикл, а повторяет его сам браузер. Переход, который вы видите на повторе, — настоящий стык последнего кадра с первым.
Контроль качества
Как проверить цикл
Метрики нужны, но красоту и естественность они не доказывают. Поэтому проверка идёт в два слоя.
Глазами
- полный цикл и несколько повторов подряд, особенно момент перехода конца в начало;
- направление: вода течёт только вниз, в том числе на стыке, а удар и брызги отвечают на поток;
- края окклюдеров: брызги не залезают на камни и листья;
- сохранность исходника: геометрия, детали и цвет на месте, нет призраков, «дыхания» неба и резиновых форм;
- крупные планы и раскадровки по времени.
Инструментами
- полное декодирование каждого файла без ошибок, счёт кадров и проверка длительности;
- оптический поток по телу водопада: движение направлено вниз во всех кадрах, включая переход через стык;
- разница кадров на стыке сопоставима с обычной разницей между соседними кадрами;
- побитовое совпадение пикселей за пределами зоны эффекта;
- контрольные суммы файлов и сохранённые параметры сборки, чтобы результат можно было воспроизвести.

Границы метода
Что метод умеет и чего нет
Это способ работы с конкретной иллюстрацией, а не автоматическая фабрика анимаций.
- Что воспроизводимоНеподвижная камера, исходник как жёсткий фон, изолированный нейросинтез жидкостей, процедурный VFX для главной воды, сборка в линейном свете, общий период и замедление без новых кадров. Код, маски и параметры сохраняются вместе с результатом.
- Ручная подгонкаМаски, поля движения, кинематику водопадов и порядок перекрытий приходится подбирать под каждую иллюстрацию. Автоматического переноса на любую картинку нет.
- Стилизация, а не физикаВода читается как стилизованная процедурная 3D-вода, а не как кистевой рисунок; струи мягче нарисованных. Гидродинамика не рассчитывается.
- Компромиссы этой сценыОблако брызг приглушает нижнюю треть водопада и немного утепляет его цвет, у краёв листьев слева заметны слабые ореолы тумана. Остальные водопады анимированы проще — бликами и каплями поверх рисунка; малые планеты неподвижны.
- Оценка — визуальнаяМетрики отсекают технический брак, но естественность движения оценивается глазами, на полном цикле и повторах.
Обсудить задачу
Нужна живая сцена или визуальный конвейер?
Разберём исходный арт, требования к движению и формат показа и честно оценим, где подойдёт этот метод, а где лучше другой путь.