Открытый мир, который целиком считается в браузере телефона: город, трафик, толпа, полиция, самолёты и катера. Ниже — то, что мешало, то, что выручило, и то, что стоит знать заранее. Все числа в тексте из замеров, а не по памяти.
Стек без единой сборочной команды: в браузер уезжают те же файлы, что лежат в папке.
Шесть сервисов и программ, каждая на своём шаге.
Пять шагов от строчки текста до файла, который открывается по ссылке.
Каждый объект начинается с текста: «молодая женщина в красном платье, волосы собраны в хвост, А-поза, ровный свет, серый фон». Ограничения в промпте — не про красоту, а про то, переживёт ли картинка следующий шаг.
Meshy строит по концепту модель на 20–30 тысяч треугольников с текстурой. Это ещё не то, что поедет в браузер: один такой файл весит больше, чем весь город.
Той же модели автоматически ставится скелет, а движения берутся из библиотеки по номеру: ходьба, покой, бег, удар, прыжок. У каждого типа прохожего свой рисунок шага, иначе толпа выглядит копиями одного человека.
Blender сшивает разошедшиеся швы, режет фигуру на голову и тело, прореживает их разными бюджетами и сшивает обратно. Модель худеет в десять раз и остаётся собой.
Клипы сшиваются в один файл на персонажа, страница и адреса штампуются версией, стенд меряет кадр, и всё уезжает в облако одной командой. Правка, не прошедшая замер, не выкладывается.
Девять мест, где время ушло впустую, пока я не начал мерить.
Meshy отдаёт машину в 24 тысячи треугольников, а в браузер помещается пара тысяч. Прореживание съедает не детали, а форму: отваливаются колёса, борта сворачиваются внутрь. По числам этого не видно — счётчик треугольников доволен, а машина уже не машина.
пикап при 1400 треугольниках — лоскут без колёс, при 2500 — целая машина
Прореживание режет фигуру равномерно, а лицо — самая мелкая геометрия в ней. При общем бюджете 1600 треугольников голове доставалось 450, и нос с глазами просто исчезали. Фигура при этом выглядела нормально — пока не подойдёшь.
голова 450 из 1600 — лица нет; 1200 из 2200 — лицо есть
У девушки длинные распущенные волосы падали на щёку. Генератор строит такие пряди отдельной тонкой оболочкой поверх лица, и при любом прореживании она рвётся в крупные плоские осколки. Ни один способ облегчения это не чинит — я перебрал четыре.
осколки видны уже в сыром файле на 20 тысячах треугольников
Потолок — 90 тысяч треугольников и меньше сотни вызовов отрисовки на кадр. Каждое улучшение приходится оплачивать: подняли качество лиц — сняли модель с самой дальней машины и с шестого прохожего.
60 кадров в секунду, 85–86 тысяч треугольников, 89–91 вызов
Загрузчик моделей прохожих отвергал файлы, не влезающие в его предел, и писал об этом предупреждением. Когда бюджет подняли до 2200, а предел остался 1800, отвергнуты оказались все семь типов. Ошибок ноль, игра работает — просто весь город ходит дешёвыми заготовками.
ноль прохожих со скелетом из шестидесяти — поймано замером, не глазами
Удар о кромку тротуара должен срабатывать один раз при съезде. Условия в коде не было, и он отрабатывал каждый кадр — шестьдесят раз в секунду снимал три четверти скорости. На экране это невидимая стена вдоль дороги.
54 км/ч на асфальте и 3.7 км/ч через секунду после съезда
Клипы анимации несли собственный масштаб скелета — герой уменьшался на ходу и вырастал на месте. А поза покоя таза оказалась в сотню раз крупнее ожидаемой: запись «правильной» высоты загоняла героя по пояс в асфальт.
рост 1.567 м на ходу против 1.176 м стоя; таз: ожидалось 0.954, в файле 90.141
В альбоме 62 градуса, в портрете до 80. Салон, собранный под один угол, в другом расползается. Первая попытка растянуть узел целиком не дала ничего: равномерный масштаб вокруг начала координат камеры проекцию не меняет вовсе.
панель на y = −0.30 при z = −0.70 ложится на 86% высоты кадра — под нижний край
Каждый кусок трейлера плавно уходил в ноль, а следующий начинался с нуля — и ровно на стыке оба оказывались прозрачными. Один пустой кадр восемнадцать раз за минуту.
на выборке по секунде поймалось три пустых кадра из тридцати семи
Семь решений, которые сняли причину, а не симптом.
Фигура делится по линии шеи, обе части прореживаются своими бюджетами и сшиваются обратно. Половина бюджета уходит голове. Тело на игровой дистанции теряет незаметно, а лицо появляется.
та же цена в треугольниках, лицо читается
Весь трафик рисуется одним InstancedMesh. Битая машина не получает свою геометрию: направление и сила удара едут в атрибут экземпляра, вершины сдвигает шейдер.
вмятины стоят ноль вызовов отрисовки
Честная геометрия уводила панель под нижний край. Салон описан в долях экрана: линия панели — две трети сверху, приборы над ней, руль под ней. Высота в метрах считается из доли и глубины.
дорога занимает верхние две трети кадра при любом угле обзора
Машины из пробок переселяются по городу. Радиус «не трогать ближе 70 метров» запрещал разбирать пробку, вставшую в 65 метрах прямо по курсу. Проверка по направлению камеры отвечает на нужный вопрос.
стоящих машин 0–4 из 59 вместо растущих до 17
Коробка фары посчитана по плоскому носу процедурного кузова. У модели нос скруглён, и та же коробка висит в воздухе перед бампером. Держим второй набор, утопленный внутрь, и выбираем по способу отрисовки.
шесть матриц на тип, ноль стоимости в кадре
Темнело в шесть вечера. Вместо переписывания световой таблицы день растянут отдельной функцией: часы на экране остаются честными, по растянутой шкале идёт только солнце.
восход 4:30, закат 22:30; в 20:00 ещё полный день
Никакого отдельного рендера: камера отключается от героя, план описывается функцией от времени, кадры снимаются по одному и склеиваются. Пролёты дрона и погоня — это та же игра, что открывается по ссылке.
семнадцать планов, всё снято на той же сборке, что и выложена
Восемь правил. Каждое я нарушил в этом же проекте.
Когда счётчик и картинка спорят, прав рендер. Половина времени здесь ушла на проверки, которые я строил, чтобы перестать угадывать: показать модель крупно, посчитать стоящие машины, спроецировать точку и посмотреть, куда она легла.
Генератор пропускал шаг, если файл уже существовал. Установка времени суток молча ничего не делала, потому что за свойством стоял только геттер. Обе «сработали» без единой ошибки.
Предупреждение — не ошибка. Ищите не отсутствие ошибок, а присутствие ожидаемого: сколько прохожих получили модель, сколько машин нарисовано моделью, доехала ли машина до точки.
Бюджет в инструменте сборки и предел в загрузчике — это одно число. Разъехались на четыреста треугольников — и город остался без единой модели человека.
«Поезжай и посмотри» даёт разный ответ каждый раз. Поставьте машину на открытую площадку по проверенному направлению, дайте газ и снимите скорость по секундам — вот тогда число значит ровно то, что значит.
Комментарий «сшить швы» бесполезен через неделю. Комментарий «две трети рёбер без второй грани, поэтому прореживание рвёт сетку в лоскуты» спасает следующего.
Красивая картинка не значит пригодную модель. Волосы, падающие на лицо, дают осколки после прореживания. Промпт с собранными волосами снял проблему, которую не решала ни одна настройка облегчения.
Улучшение всегда за чей-то счёт. Заведите потолок, меряйте его на каждой правке и решайте, у кого забрать: у дальней машины, у шестого прохожего или у тени.