Як сьогодні створюються 64k intro: занурення в Immersion

У грудні минулого року ми нарешті закінчили наш проект. У цьому відео показано наша остання робота — чотирихвилинна анімація «Immersion». Точніше, це запис того, що зазвичай називається 64k-інтро. Але детально про це трохи пізніше.

Робота над проектом зайняла найкращі вільні години останніх двох років життя. Все це почалося під час проведення Revision 2015, великого заходу, влаштовується щороку в Німеччині під час великодніх канікул. Ми вдвох балакали по дорозі з готелю до місця проведення заходу. Попереднім ввечері рівень конкуренції в галузі 64kB intro виявився високим. Дуже високим. Досвідчена і добре відома угорська група Conspiracy нарешті повернулася з серйозною, приголомшливою роботою. Наш кращий ворог Approximate ідеально встиг за часом із завершенням циклу випуску і показав значні поліпшення в сторителлинге. Продуктивна група Mercury знайшла власний зрілий стиль дизайну в інтро, яке не залишало сумнівів у своїй перемозі.

У той рік ми прийшли з порожніми руками і не брали участь у змаганнях, але, звичайно ж, хотіли повернутися якомога раніше. Однак після демонстрації цих якісних інтро ми гадали: красива графіка, відмінний сюжет, чудовий дизайн — як ми можемо піднятися до цього рівня? Я не міг придумати концепцію, яка навіть при ідеальній реалізації змогла б перемогти всіх цих трьох конкурентів. Не кажучи вже про те, що наші технічні навички були нижче, ніж у кожної з груп. І так ми йшли по Гогенцоллернштрассе, перекидаючись ідеями, поки одна з них не «вистрілила». Місто, що виростає з моря. Ця концепція при правильній реалізації, можливо, змогла б змагатися на тому рівні, якого досягла субкультура інтро. Revision 2016, приготуйся, ми йдемо!

Revision 2016 стрімко промчала повз нас, чи нам вдасться встигнути до Revision 2017? На жаль, ми не впоралися і до цього нового дедлайну. Коли нас питали на заході, як йдуть справи, ми відповідали ухильно: «На першу частину у нас пішов рік. Впевнений, що другу частину встигнемо зробити за 24 години». Але ми не встигли. Тим не менш, ми випустили реліз, але друга частина робилася поспіхом, і це було помітно. Настільки, що ми навіть не змогли наблизитися до сцени з переможцями. Ми продовжували працювати, і вкладали всю необхідну любов, а потім, нарешті, випустили показану вище остаточну версію.

Що таке 64k-інтро?
Демо — це творіння цифрового мистецтва, що стоять на перехресті короткометражних фільмів, музичних відео і відеоігор. Хоча вони є неинтерактивными і часто залежать від музики, відеокліпів, але рендеряться в реальному часі, як відеоігри.

64-килобайтные інтро, або 64кб для стислості, схожі на демо, але в них додано обмеження за розміром: інтро повністю повинні уміщатися в один двійковий файл розміром не більше 65536 байт. Ніяких додаткових ресурсів, ніякої мережі, ніяких зайвих бібліотек: звичайна вимога полягає в тому, щоб його можна було запустити на PC з тільки що встановленої Windows і останніми версіями драйверів.

Наскільки великий цей обсяг? Ось вам опорні точки для порівняння.

У файлі розміром 64КБ можна зберігати:

  • 400 мілісекунд звуку у форматі WAV з CD-якістю, або
  • 3 секунди mp3 з 192Кбит/с, або
  • RGB-зображення 200×100 у форматі .bmp, або
  • JPEG-зображення середнього розміру і середньої якості, наприклад, такого як цей скріншот 800×450 з інтро:


JPEG-зображення розміром 65595 байт, на 59 байт більше обмеження в 64КБ.

Так, все вірно: це відео, показане на початку посту, повністю вміщується в один файл, який займає менше місця, ніж сам скріншот відео.

Коли бачиш такі числа, то здається складним вмістити в двійковий файл зображення і звуки, які точно повинні бути необхідні. Ми вже говорили про деякі компроміси, на які нам довелося піти, і про деякі хитрощі, які ми використовували, щоб вмістити все в такий маленький розмір. Але цього недостатньо.

Насправді, з-за таких надзвичайних обмежень неможливо використовувати звичайні техніки та інструменти. Ми написали власний тулчейн — ця задача цікава сама по собі: ми створили текстури, 3D-моделі, анімації, шляхи камери, музику і т. д. завдяки алгоритмам, процедурної генерації і стисненню. Скоро ми розповімо про це.

Деякі з чисел
Ось, на що ми витратили доступні нам 64КБ:

  • Музика: 12,4 КБ
  • 3D-меши: 12,5 КБ
  • Текстури: 4,8 КБ
  • Дані камери: 1,3 КБ
  • Шейдери: 6,2 КБ, 5 тисяч рядків коду
  • Движок: 12,9 КБ, з 20 тисяч рядків коду
  • Саме інтро: 12 тисяч рядків коду
  • Витрачений час: довгі години, можливо, навіть тисячу годин


На цьому графіку показано, як 64КБ включена різними типами контенту після стиснення.


На цьому графіку показано зміну двійкового розміру (не включаючи приблизно 2КБ распаковщика) до остаточного релізу.

Дизайн та джерела натхнення
Домовившись, що центральною темою буде занурений у воду місто, ми поставили собі перше питання: як повинен виглядати це місто? Де він розташований, чому затоплений, яка у нього архітектура? Один простий відповідь на всі ці питання: це може бути легендарний загублений місто Атландида. Це також пояснить його появу: з волі богів (в буквальному сенсі deus ex machina). На цьому ми й порішили.


Рання концепція затопленого міста. Показані в статті художні роботи створені Бенуа Моленда (Benoît Molenda).

При прийнятті дизайнерських рішень ми керувалися двома книгами: «Тимей» і «Критій», в яких Платон описував Атлантиду і її долю. Зокрема, в «Критии», він докладно описує подробиці структури міста, його кольору, достаток дорогоцінного орихалка (який став суттєвим елементом сцени з храмом), загальну форму міста і головний храм, присвячений Посейдону і Клейто. Оскільки Платон, очевидно, засновує свої описи відомих йому країнах, тобто суміші грецької, єгипетської та вавілонської стилів, ми вирішили дотримуватися цього.

Читайте також  80 безкоштовних ресурсів в допомогу дизайнеру



Однак без достатніх знань тематики створення переконливої античної архітектури здавалося складним завданням. Тому ми вирішили відтворити вже існуючі будівлі:

  • Одне з них — репродукція афінського Парфенона.
  • Інше — спрощена версія дельфского Толоса.
  • При створенні віадука ми ґрунтувалися на Пон-дю-Гар і акведуке у Сеговії.
  • Храм по суті є храмом Артеміди в Ефесі, одного з Семи чудес світу античного світу.

Пошуки довідкових матеріалів по храму Артеміди (Артемисиону) виявилися несподіваним, таких, що збагатили нас досвідом. Спочатку ми шукали тільки фотографії, схеми або карти. Але коли ми дізналися ім’я Джона Тертл-Вуда, все знайшло велику глибину. Вуд був тим самим чоловіком, який займався пошуком місця розташування храму і в результаті знайшов його. Сподіваючись на те, що пошук по його імені дасть нам більше результатів, ніж просто «Артемисион», ми відразу ж натрапили на його книгу, написану в 1877 році, в якій він не тільки призводить описання і замальовки храму, але й описує свою одинадцятирічну мандрівка до загубленого пам’ятника, переговори з Британським музеєм про фінансування, взаємовідносини з місцевими робітниками і дипломатичні переговори, без яких неможливо було проводити розкопки в довільних місцях.

Ці книги були дуже важливі для прийняття нами дизайнерських рішень, але насамперед їх читання змусило нас, як особистостей, набагато сильніше цінувати роботу над проектом.





І до речі, як повинна виглядати дах? У деяких ескізах, у тому числі і Вуда, у неї був отвір, в інших же воно було відсутнє; тут очевидно є якесь протиріччя. Ми вирішили вибрати модель з відкритим дахом, яка дозволить нам висвітлити інтер’єр замку променем світла. На показаних вище ілюстраціях показано архітектурний план і поперечні розрізи будівлі з книги Discoveries at Ephesus, які можна порівняти з нашою робочою моделлю храму.

Як досягти потрібного зовнішнього вигляду
З самого початку ми знали, що критично важливим у цьому інтро буде зовнішній вигляд води. Тому ми витратили на неї багато часу, почавши з перегляду довідкових матеріалів, щоб зрозуміти невід’ємні елементи підводної графіки. Як ви можете здогадатися, ми надихалися «Безоднею» і «Титаніка» Джеймса Кемерона, 3DMark 11, а освітлення вивчали в «Біжить по лезу» Рідлі Скотта.

Щоб досягти правильного відчуття перебування під водою, недостатньо було реалізувати і включити якусь епічну функцію MakeBeautifulWater(). Це було поєднання безлічі ефектів, які при правильному налаштуванні могли переконати нас, глядачів, в ілюзії і змусити відчути, що ми знаходимося під водою. Але достатньо однієї помилки, щоб зруйнувати ілюзію; цей урок ми засвоїли занадто пізно, коли у коментарях після первісного релізу нам показали, де ілюзія зникає.



Як видно на ілюстраціях, ми також досліджували різні нереалістичні і іноді надмірні палітри, але не знали, як досягти такого зовнішнього вигляду, так що в результаті повернулися до класичної колірній схемі.

Поверхня води


Рендеринг поверхні води передбачає відображення від плоскої поверхні. Відбиття і заломлення першими рендеряться на окремих текстурах з допомогою однієї камери, що знаходиться збоку, а другий, розташованої над площиною води. В основному проході повехності води рендерится як меш з матеріалом, що поєднує в собі відображення і заломлення на підставі вектора нормалі і погляду. Хитрість полягає в зміщенні координат текстур на підставі нормалі поверхні води в екранному просторі. Ця техніка є класичною і добре задокументована.

На середньому масштабі це працює добре, наприклад, під час сцени з човном, але при великому масштабі, наприклад, у фінальній сцені з появою храму з води, результат виглядає штучним. Щоб зробити його переконливим, ми використовували художній трюк, що полягає у застосуванні до проміжним текстурам гаусового шуму. Розмиття текстури заломлення надає воді похмурий і більше відчуття глибини. Розмиття текстури відображення допомагає моря виглядати більш хвилястим. Крім того, застосування більшої розмиття у вертикальному напрямку імітує вертикальні сліди, які можна очікувати від поверхні води.


Розмите зображення храму відображається в поверхні води.

Анімація виконана з допомогою простих хвиль Герстнера в вершинном шейдере складанням восьми хвиль з випадковими напрямами і амплітудою (в заданому діапазоні). Більш дрібні деталі виконуються в фрагментном шейдере, містить ще 16 хвильових функцій. Штучний ефект зворотного розсіювання, заснований на нормалі і висоті, робить вершини хвиль більш світлими, видимими на наведеному вище зображенні як невеликі зелені плями. Під час сцени запуску додано декілька додаткових ефектів, наприклад, шейдер дощових крапель.


Ілюстрація шейдера. Натисніть на зображення, щоб перейти до шейдеру в Shadertoy.

Об’ємне освітлення

Одним з перших технічних питань став «Як зробити так, щоб стовпи світла занурювалися в воду?». Можливо, підійде просвічуючий білборд з красивим шейдером? Одного разу ми почали експериментувати з наївним ray marching крізь середовище. Ми з радістю спостерігали, як навіть у ранньому грубому тесті рендеринга, незважаючи на погано підібрані кольори і відсутність гідної фазової функції, об’ємне освітлення відразу стало переконливим. В цей момент ми відмовилися від первісної ідеї з биллбордом і більше ніколи до неї не поверталися.

Завдяки цій простій техніці ми отримали ефекти, про які навіть не наважувалися думати. Коли ми додали фазову функцію і поекспериментували з нею, відчуття стали походити на реальні. З кінематографічної точки зору це відкривало безліч можливостей. Але проблема була в швидкості.


Стовпи світла надають цій сцені зовнішній вигляд, на який нас надихнув «той, що Біжить по лезу».

Читайте також  10 корисних порад щодо реалізації Pixel Perfect дизайну під Front-end розробці (на прикладі роботи з редактором Sketch)

Настав час перетворити цей прототип в реальний ефект, тому ми прочитали туторіал Себастьяна Хіллара, його презентацію DICE і вивчили інші підходи, наприклад, з эпиполярными координатами. У результаті ми зупинилися на більш простий техніці, близької до тієї, що використовувалася в Killzone Shadow Fall (відео) з деякими відмінностями. Ефект виконується одним повноекранним шейдером при половинному розмірі:

  1. Для кожного пікселя випускається промінь і його взаємодії з кожним конусом світла вирішуються анатилически.Математичні розрахунки описані тут. З точки зору продуктивності, ймовірно, буде більш ефективно використовувати обмежує меш обсягу світла, але для 64КБ нам здалося простіше застосувати аналітичний підхід. Очевидно, що промені поширюються не далі глибини в буфері глибин.
  2. У разі перетину променя для обсягу всередині конуса виконується ray marching.Кількість кроків обмежена з міркувань швидкості і до них додаються випадкові зміщення, щоб уникнути смуг. Це типовий випадок усунення смуг для шуму, менше сумнівний візуально.
  3. На кожному кроці виходить карта тіней, відповідна світла, а вплив світла накопичується у відповідності з простою фазової функцією Хені – Грінштейна.На відміну від підходу на основі эпиполярных координат, за допомогою цієї техніки можна мати різнорідну щільність середовища, що додає варіативності, але ми не реалізували такий ефект.
  4. Дозвіл отриманого зображення збільшується з допомогою двухпроходного двонаправленого гаусового фільтра і додається поверх основного буфера рендеринга. На відміну від техніки з туториала Себастьяна, ми не використовуємо тимчасове повторне проектування; ми тільки використовуємо досить велику кількість кроків для зниження видимих артефактів (8 кроків при налаштуваннях низької якості, 32 кроків при налаштуваннях високої якості).


Об’ємне освітлення дозволяє надати потрібний настрій і виразний кінематографічний зовнішній вигляд, який складно реалізувати іншим чином.

Поглинання світла

Миттєво впізнаваний аспект підводного зображення — поглинання. При віддаленні об’єкта він стає менш помітним, його кольору зливаються з фоном, поки він повністю не зникає. Аналогічно, обсяг, на який впливають джерела світла, теж зменшується, тому що світло швидко поглинається водним середовищем.

Цей ефект має чудовий потенціал для створення кінематографічного відчуття, а змоделювати його дуже просто. Він створюється двома етапами шейдере. Перший етап застосовує до яскравості світла просту функцію поглинання при накопиченні джерел освітлення, що впливають на об’єкт, змінюючи таким чином колір і яскравість світла при досягненні ним поверхонь. Другий етап застосовує ту ж функцію поглинання до остаточного кольору самого об’єкта, таким чином змінюючи сприймається колір залежно від відстані до камери.

Код має приблизно таку логіку:

vec3 lightAbsorption = pow(mediumColor, vec3(mediumDensity * lightDistance));
vec3 lightIntensity = distanceAttenuation * lightColor * lightAbsorption;

vec3 surfaceAbsorption = pow(mediumColor, vec3(mediumDensity * surfaceDistance));
vec3 surfaceColor = LightEquation(E, N, material) * lightIntensity * surfaceAbsorption;


Тест поглинання світла у водному середовищі. Зауважте, як колір впливає відстань до камери і відстань від джерел світла.

Додавання рослинності

Ми абсолютно точно хотіли використовувати водорості. У списку бажаних типових елементів підводних сцен вони стояли на одному з перших місць, але їх реалізація здавалася ризикованою. Подібні органічні елементи можуть бути складними в реалізації, а неправильна реалізація може зруйнувати відчуття занурення в ілюзію. Вони повинні володіти переконливою формою, добре інтегруватися в оточення, а можливо, і вимагати додаткової моделі підповерхневого розсіювання.

Однак в один із днів ми відчули, що готові до експерименту. Почали з куба, отмасштабировали його і розташували випадкове кількість кубів по спіралі навколо уявного стебла: з досить великої відстані це могло зійти за довге рослина з безліччю маленьких гілок. Після додавання великої кількості шуму для деформування моделі водорості почали виглядати майже гідно.


Тестовий кадр з кількома рідкісними рослинами.

Однак коли ми намагалися додати ці рослини в сцену, то усвідомили, що із збільшенням кількості об’єктів швидкість швидко знижується. Тому ми могли додати занадто маленьке їх число, щоб це виглядало переконливо. Схоже, що наш новий неоптимізований движок вже натрапив на перше «пляшкове горлечко». Тому в останню хвилину ми реалізували грубе відсікання по піраміді видимості (в остаточній версії використовується правильне відсікання), що дозволило показати в демо щільні кущі.

З відповідною щільністю і розмірами (ділянки з нормальним розподілом), і коли подробиці приховані тьмяним освітленням, картина починає виглядати цікавою. При подальших експериментах ми спробували анімувати водорості: функція шуму для модуляції сили уявного підводну течію, зворотній експоненціальна функція для того, щоб рослини згиналися, а також синусоїда, щоб їх кінчики закручувалося в потоці. В процесі експериментів ми натрапили на справжній скарб: випущення підводного світу крізь кущі, отрисовывающее на морському дні патерни тіней, зникають при видаленні від камери.


Рослинність, що відкидає патерни тіней на морське дно.

Додання обсягу з допомогою частинок

Останній легкий штрих — це частинки. Уважно подивіться на будь-яку підводну зйомку, і ви помітите всілякі зважені частинки. Перестаньте звертати на них увагу, і вони пропадуть. Ми налаштували частинки так, щоб вони були мало помітні й не траплялися на шляху. Тим не менш, вони надають відчуття об’єму, заповненого відчутною середовищем, і допомагають посилити ілюзію.

З технічної точки зору все досить просто: в інтро Immersion частинки — це просто примірники чотирикутників з просвічує матеріалом. Проблему порядку рендеринга, викликану просвічуванням, ми просто уникли, задавши позицію вздовж однієї осі у відповідності з ідентифікатором примірника. Завдяки цьому всі примірники завжди відмальовує вздовж цієї осі в правильному порядку. Для кожного кадру потім потрібно правильно зорієнтувати обсяг частинок. Насправді, для багатьох кадрів цього ми цього зовсім не робили, тому що розмір частинок і темрява сцени зробили помітні артефакти досить рідкісними.

Читайте також  10 питань програмісту fillpackart — «Я даремний дурень і хочу звільнитися»


На цьому кадрі частинки дають поняття про глибину і відчуття щільності при збільшенні глибини занурення.

Музика
Як умістити високоякісну музику приблизно в 16КБ? Ця задача не нова, і більшість 64k-інтро, написаних після .the .product 2000 року, використовують схожі концепції. Оригінальна серія статей досить стара, але не втрачає актуальності: The of Workings FR-08’s Sound System.

Якщо коротко, то ідея полягає в тому, що нам потрібна музична партитура і список інструментів. Кожен з інструментів — це функція, що генерує звук процедурно (див., наприклад, субтрактівний синтез і синтез фізичного моделювання). Музична партитура — це список використовуваних нот і ефектів. Вона зберігається у форматі, схожому на midi, з деякими змінами для зменшення розміру. Генерування музики відбувається під час виконання програми.

У синтезатора також є версія у вигляді плагіна (VSTi), який музикант може використовувати в улюбленій програмі написання музики. Написавши музику, композитор натискає на кнопку, котра експортує всі дані в файл. Ми вбудовуємо дані в демо.

При запуску демо воно запускає потік для генерування музики у величезний буфер. Синтезатор активно споживає ресурси ЦП і не обов’язково виконується в реальному часі. Тому ми запускаємо потік перед початком демо, коли генеруються текстури та інші дані.

Даніель Линдхолм склав музику за допомогою синтезатора 64klang, створеного Домініком Риесом.

Робочий процес
При створенні демо одним із найбільш важливих аспектів є час ітерації. Насправді, це відноситься до багатьох творчим процесам. Час ітерації — це найважливіше. Чим швидше ви можете виконувати ітерації, тим більше можете експериментувати, тим більше варіацій можете досліджувати, тим більше ви можете удосконалювати своє бачення і підвищувати якість в цілому. Тому ми максимально хочемо позбутися від усіх перешкод, пауз і невеликих тертя в процесі творчості. В ідеалі ми хочемо мати можливість змінювати що завгодно і коли завгодно, миттєво бачить результат і отримуючи безперервну зворотний зв’язок в процесі внесення змін.

Можливе рішення, що використовується багатьма демо-групами, полягає в збірці редактора і створення всього контенту всередині нього. Ми цього не зробили. Спочатку ми хотіли писати код на C++ і робити все всередині Visual C++. З часом ми розробили декілька технік, поліпшивши наш робочий процес і знизили час ітерації.

Гаряча перезавантаження всіх даних

Якщо б ми могли дати в цій статті лише одну пораду, то він був би таким: зробіть так, щоб всі ваші дані підтримували гарячу перезавантаження. Всі дані. Зробіть так, щоб ви могли виявляти зміна даних, завантажувати нові дані, коли це відбувається, і відповідним чином змінювати стан програми.

Мало-помалу ми забезпечили можливість гарячої перезавантаження всіх даних: шейдерів, камери, монтажу, всіх кривих, залежних від часу, і т. д. На практиці у нас зазвичай був редактор і занедбане додатково демо. При модифікації файлу зміни миттєво ставали видимими в демо.

В такому маленькому проекті, як демо, це реалізувати досить просто. Наш движок відстежує, звідки надходять дані, а невелика функція регулярно перевіряє зміна міток часу відповідних файлів. При їх зміні вона запускає перезавантаження відповідних даних.

Така система може бути набагато більш складною у великих проектах, у яких такі зміни можуть залежностями і легасі-структурою. Але вплив цього механізму на процес виробництва складно переоцінити, тому він повністю стоїть вкладених зусиль.

Настроювані значення

Перезавантаження даних — це, звичайно, добре, але як щодо самого коду? З ним все більш складно, тому нам довелося вирішувати цю задачу поетапно.

Першим кроком був хитрий трюк, який дозволив нам змінювати літерали констант. Джоел Девіс описав це у своєму пості: короткий макрос, що перетворює константу в змінну з фрагментом коду, який розпізнає подія зміни файлу исходника, і відповідним чином оновлює змінну. Очевидно, що кінцевому двійковому файлі цей допоміжний код відсутній і залишена тільки константа. Завдяки цьому компілятор здатний виконати всі оптимізації (наприклад, коли константі присвоєно значення 0).

Цей трюк можна застосовувати не у всіх випадках, але він дуже простий і може бути інтегрований в код за лічені хвилини. Більше того, хоча мається на увазі, що він повинен лише змінювати константи, його можна використовувати і для відладки: для зміни гілок коду або включення/відключення параметрів з умовами зразок if(_TV(1)).

Рекомпиляция C++

Нарешті, останнім кроком для забезпечення гнучкості коду стало включення в кодову базу інструменту Runtime Compiled C++. Він компілює код як динамічну бібліотеку і завантажує її, а також виконує серіалізацію, що дозволяє вносити зміни в код і спостерігати за результатами в процесі виконання, без необхідності перезавантаження програми (у нашому випадку — демо).

Цей інструмент поки не ідеальний: його API занадто впроваджується в код і обмежує його структуру (класи зобов’язані бути похідними від інтерфейсу), а компілювання і перезавантаження коду все одно займають кілька секунд. Тим не менш, можливість внесення змін у логіку коду всередині демо і бачити результат забезпечує широку творчу свободу. На поточний момент переваги інструменту використовуються тільки для генераторів текстур і мешей, але в майбутньому ми хочемо розширити його роботу на всю сукупність коду, що займається контентом.

Продовження слідує
Тут закінчується перша частина того, що задумано як серія статей, присвячених використаним у розробці H – Immersion технікам. Ми хотіли б подякувати Алана Вольфа за вичитку статті; в його блозі є безліч цікавих технічних статей. У наступних частинах ми детальніше розповімо про те, як створюються текстури і меші.

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *