- PVSM.RU - https://www.pvsm.ru -

Как было реализовано пламя в Doom на Playstation

Как было реализовано пламя в Doom на Playstation - 1

Целая глава моей книги Game Engine Black Book: DOOM [1] посвящена консольным портам DOOM и сложностям, с которыми сталкивались их разработчики. Можно долго рассказывать о полном провале на 3DO, о сложностях на Saturn из-за аффинного наложения текстур и о потрясающем «реверс-инжиниринге с нуля», выполненном Рэнди Линденом для Super Nintendo.

Изначально двинувшись в направлении, ведущем к катастрофе[1], разработчики порта под Playstation 1 (PSX) в дальнейшем сменить курс и создать порт, завоевавший успех у критиков и рынка. Final DOOM [2] был первым истинным портом, сравнимым с PC-версией. Цветовые сектора с альфа-смешением не только усовершенствовали визуальное качество, но и улучшили геймплей благодаря индикации ключа нужного цвета. Также благодаря эффектам реверберации Audio Processing Unit консоли PSX был улучшен звук.

Команда разработчиков выполнила настолько качественную работу, что у неё осталось ещё немного свободных циклов ЦП, которые они решили использовать для генерации анимированного огня в интро [3] и геймплее [4]. Меня это настолько привело в благоговейный трепет, что я решил разобраться, как был реализован эффект. Когда первые поиски не дали ответа, я приготовился уже сдувать пыль с книги по MIPS для взлома исполняемого файла, но Сэмюэль Вильяреал вовремя ответил в Twitter, что он уже выполнил обратную разработку версии для Nintendo 64[2]. Мне достаточно было просто немного её подчистить, упростить и оптимизировать.

Было интересно заново обнаружить этот классический эффект демосцены; лежащая в его основе идея похожа на первую водную рябь [5], которая входила в обязательный набор программ многих разработчиков 90-х. Эффект огня стал живым свидетелем того времени, когда тому, что сочетание тщательно подобранной цветовой палитры и простого трюка были единственным способом добиться желаемого.

Базовая идея


В своей основе эффект огня использует простую карту высот. Массив размером с экран заполняется 37 значениями в интервале от 0 до 36. Каждое значение связывается с цветом от белого до чёрного, и захватывает по дороге между ними жёлтый, оранжевый и красный. Идея заключается в моделировании температуры частицы пламени, которая поднимается вверх и постепенно охлаждается.

Как было реализовано пламя в Doom на Playstation - 2

Буфер кадра инициализируется полностью чёрным (заполненным нулями) с единственной белой строкой белых пикселей внизу (36), которая является «источником» пламени.

Как было реализовано пламя в Doom на Playstation - 3

При каждом обновлении экрана «тепло» поднимается вверх. Для каждого пикселя в буфере кадра вычисляется новое значение. Каждый пиксель обновляется с учётом значения, расположенного непосредственно под ним. В коде нижний левый угол это нулевой индекс массива, а верхний правый угол имеет индекс FIRE_HEIGHT * FIRE_WIDTH — 1.

function doFire() {
    for(x=0 ; x < FIRE_WIDTH; x++) {
        for (y = 1; y < FIRE_HEIGHT; y++) {
            spreadFire(y * FIRE_WIDTH + x);
        }
    }
 }

 function spreadFire(src) {
    firePixels[src - FIRE_WIDTH] = firePixels[src] - 1;
 }

Заметьте, что строка 0 никогда не обновляется (итерация по y начинается не с 0, а с 1). Эта заполненная нулями строка является «генератором» огня. Простая версия с линейным охлаждением (-=1) даёт нам скучные равномерные выходные данные.

Как было реализовано пламя в Doom на Playstation - 4

Мы можем немного изменить функцию spreadFire() и модифицировать скорость затухания значений теплоты. Вполне подойдёт добавление случайности.

 function spreadFire(src) {
    var rand = Math.round(Math.random() * 3.0) & 3;
    firePixels[src - FIRE_WIDTH ] = pixel - (rand & 1);
 }

Как было реализовано пламя в Doom на Playstation - 5
Так уже лучше. Чтобы усовершенствовать иллюзию, можно случайным образом распространять не только вверх, но также влево и вправо.

 function spreadFire(src) {
    var rand = Math.round(Math.random() * 3.0) & 3;
    var dst = src - rand + 1;
    firePixels[dst - FIRE_WIDTH ] = firePixels[src] - (rand & 1);
 }

[Прим. пер.: Youtube ужасно пережимает видео, лучше смотреть демо на Javascript в оригинале статьи [6].]

Вуаля! Заметьте, что изменяя процесс распространения пламени можно также симулировать ветер. Я оставлю это в качестве упражнения для читателей, которым удалось дочитать статью.

Полный исходный код


Версия [7] Сэмюэля (логически) выглядела больше похожей на ассемблерную. Если хотите взглянуть на неё, то здесь [8] есть подчищенная и упрощённая версия.

Справочные материалы


[1] Источник: Полная история подробно рассказана в книге Game Engine Black Book: DOOM

[2] Источник: пост в Twitter за 25 марта 2018 года [9]

Автор: PatientZero

Источник [10]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/doom/304258

Ссылки в тексте:

[1] Game Engine Black Book: DOOM: http://fabiensanglard.net/gebbdoom

[2] Final DOOM: https://www.youtube.com/watch?v=p85npx14HUI

[3] интро: https://www.youtube.com/watch?v=YJB0gfP-GRY&feature=youtu.be&t=10

[4] геймплее: http://fabiensanglard.net/doom_fire_psx/psx_screen_sky.png

[5] водную рябь: http://agilerepose.weebly.com/water-ripple.html

[6] оригинале статьи: http://fabiensanglard.net/doom_fire_psx

[7] Версия: https://codepen.io/svkaiser/pen/xXmOvY

[8] здесь: https://github.com/fabiensanglard/DoomFirePSX/blob/master/flames.html

[9] пост в Twitter за 25 марта 2018 года: https://twitter.com/fabynou/status/936329655308632064

[10] Источник: https://habr.com/post/435122/?utm_source=habrahabr&utm_medium=rss&utm_campaign=435122