Программист Terra Nova: Strike Force Centauri, System Shock 2 и серии Thief Шон Барретт рассказывает о том, что привело его к программированию трёхмерной графики и работе над играми.
Один из постов Майка Абраша начинается с рассуждений о том, как его письменные труды повлияли на его карьеру. Это напомнило мне о том, как его труды повлияли на мою карьеру. Не уверен, говорил ли я когда-нибудь ему о том, что, возможно, благодаря ему я попал в игровую индустрию. Я подумывал написать ему письмо, но потом решил изложить всё это публично и провести небольшое исследование в этой области.
Когда я был подростком, в 80-х у меня был компьютер Atari 800, на котором я занимался обычными вещами (немного игр, в основном BASIC). (Технически, по крайней мере, изначально, им владела моя семья, но я был единственным пользователем.) Хотя это было практически невозможно на 800, но я заинтересовался 3D-графикой. Я изучал Pascal и работу 3D одновременно с расшифровкой программы для рисования каркасной графики из журнала Byte; я предпринимал хакерские попытки воспроизвести тот же эффект на 800. (В конце концов мне удалось выполнить на 800 трассировку лучами простой сферы с освещением по Ламберту, а затем клетчатых и отражающих сфер на Atari ST моего друга.)
Глядя на игры Infocom, которые создавались одиночками, я думал, что хочу заниматься тем же, «когда вырасту», но в то время индустрия начала движение в сторону больших коллективов, а у Infocom начался спад, поэтому это больше не казалось мне интересным/оправданным, и когда я поступил в колледж, то больше никогда не думал о такой возможности.
Я продолжал интересоваться компьютерной графикой и в колледже, регулярно читал Computer Graphics SIGGRAPH и IEEE Computer Graphics and Applications. Но больше программированием графики реального времени я не занимался, даже несмотря на то, что у меня был персональный компьютер с возможностью отображения графики. И в компьютерные игры для PC я тоже не играл. Когда я занимался программированием, то в основном писал что-то вроде движков текстовых адвенчур и программ для IOCCC, а играл только в MUD.
Я выпустился из колледжа в 1992 году, когда мне было 25, и переехал в Техас, где начал работать в борющемся за жизнь стартапе, выпускавшем ПО для принтеров. (Этот опыт может казаться малоценным для игровой индустрии, но в августе 1993 года я стал (насколько помню) единственным программистом, работавшим над написанием нашего нового движка PostScript на C++; наш предыдущий движок был написан на Forth и языке ассемблера 68000, к тому же новый должен был поддерживать PostScript Level 2. Таким образом, я был единственным программистом в проекте интерпретатора языка и двухмерного графического движка, предназначенного для спецификации Adobe. Пятнадцать лет спустя, в 2008 году, я начал работать над Iggy, библиотекой UI на основе Flash, которая была в основном интерпретатором языка и двухмерным графическим движком, предназначенным для спецификации Adobe. (Однако я переключился обратно на C.)) Там я присоединился к паре своих друзей, Джону Прайсу и Джону Дэвису. Я называю их друзьями, но до переезда в Техас я знал их только по LPMud (Darker Realms), в которой мы вместе были администраторами. (Чтобы покончить с этими странными воспоминаниями о прошлых знакомствах, скажу, что мы создали эту LPMud, когда встретились в другой LPMud, Wintermute. Одним из администраторов Wintermute, которому мы подражали (и который несколько раз посетил наш Mud), был Тим Кейн.)
Джон, Джон и я были младшим персоналом компании и быстро создали небольшой клан. После завершения трудового дня мы играли на работе в Ultima Underworld, Ultima 7, Ultima Underworld 2 и Ultima 7 part 2 на самых мощных 386 и 486, которые были в офисе (кажется, в 1993 году самым мощным был 486 50). В то же время мы нашли в офисе колонки о программировании графики Майка Абраша 1992 года в сборнике журнала Dr. Dobb's Journal. Мы с Джоном Прайсом начали писать на PC рендереры 3D-полигонов, соревнуясь друг с другом (и с Абрашем) в том, кто добьётся большей скорости. Мой рендерер был довольно быстрым, пока я не попробовал реализовать наложение текстур — первая реализация требовала по две операции деления на пиксель, что было очень медленно.
После того, как в 1994 году стартап разорился, мы трое попали в игровую индустрию (но разными путями): страница Джона Прайса на MobyGames; страница Джона Дэвиса на MobyGames; моя страница на MobyGames и эта (и на самом деле, там у меня три страницы, однако там в основном не мои проекты, люди просто выражают «огромную благодарность» за вклад).
Что касалось меня, то поскольку и Origin, и я находились в Техасе, то я решил изучить вопрос трудоустройства в компанию, поэтому написал электронное письмо кому-то, работавшему в Looking Glass (я знал его только по Usenet-группе, посвящённой странным текстовым сочинениям), пытаясь найти контактную информацию Origin, потому что Origin издавала игры LGS. Вместо этого он пригласил меня на интервью в LGS, и на этом история о том, как я попал в индустрию игр, почти заканчивается (не считая ссылок на демки ниже).
Начал бы я заниматься программированием графики на PC и оказался бы в игровой индустрии, если бы вовремя не нашёл те колонки Абраша и не устроил себе соревнования по дизайну игр/программированию графики? (Моё с Джоном Прайсом третье соадминство отделилось от Wintermute, потому что у нас была твёрдая убеждённость в том, что хорошая MUD должна стремиться к развлечению игроков, а во многих LPMud это было не так. Так что наша группа не появилась совершенно спонтанно, и совсем не так уж странно то, что мы в результате стали программистами в игровой индустрии.) Возможно да, возможно нет. (И кстати, оказался ли бы я в LGS, если бы у меня не было случайного знакомства в LGS? Об этом судить сложнее — серия Underworld была моей любимой, так что, возможно, я бы всё равно нашёл бы способ связаться с ними.)
Размышляя над этим, я подумал о тех вдохновлённых Абрашем рендерерах 3D-полигонов, которые я написал. Мне стало интересно, смогу ли я их найти, поэтому я занялся археологией в моих старых заархивированных каталогах «home». Я не смог их найти. Однако мне удалось обнаружить zip с демками, которые я отправил в LGS, и они по-прежнему работают в DOSBox, поэтому я поделюсь ими. (Мне не удалось найти их исходников, так что ниже частично представлены мои догадки.)
Сначала для справки я хочу показать вам демо «Mars» Тима Кларка на 5 КБ, выпущенное в 1993 году. Простите за разрывы кадров: я не знаю, как заставить правильно работать Fraps с DOSBox.
Я решил самостоятельно её реализовать и добавить в неё новые возможности, поэтому написал собственную версию. Не помню точно, когда это было: EXE датирован 1994 годом.
Посмотрев в readme, я увидел, что забыл упомянуть о том, что это переделка демо Mars. Возможно, я предполагал, что людям из LGS она знакома. Как оказалось, это было не так, поэтому, возможно, они были более впечатлены, чем должны были! К тому же они занимались разработкой Terra Nova: Strike Force Centauri, поэтому это тоже могло повлиять на их интерес.
Заметьте, что моё демо расширяет возможности Mars, и имеет разные миры. В первом мире есть два разных цвета рельефа. Во втором (0:50) есть эффект теней облаков на земле, который мне не удавалось так же хорошо воспроизвести ни в какой другой написанной мной игре/движке. В третьем мире есть туман, а в четвёртом — странная модель освещения и клубящиеся облака. В пятом есть плазменное небо в стиле демосцены, освещающее рельеф (это просто инвертирование теней), а шестой пытается имитировать лунный ландшафт.
Наклоны в стороны — это имитация: я думаю, что в небе используется жёстко заданный в коде механизм отображения линий с постоянной z, а базовая линия рельефа наклоняется, но вертикальные элементы совсем не наклоняются. Однако небо и общий наклон делают этот эффект достаточно убедительным.
Небольшое мерцание при движении вперёд возникает потому, что движение вперёд квантуется по квадратам карты (но движение в стороны, кажется, нет). Это была просто ошибка проектирования. Другое мерцание возникает потому, что я начинаю пропускать строки карты, когда они отдаляются. Пропускаемые строки зависят от вашего положения, вместо того, чтобы, допустим, всегда пропускать нечётные строки вне зависимости от чётности строки, на которой в текущий момент находится игрок, что было бы логично. Ещё одна ошибка дизайна. Когда мне поручили заняться рендерером рельефа Terra Nova (я был уже третьим человеком, работавшим над этой задачей), я исправил в нём точно такую же ошибку.
Также я отправил LGS первый написанный мной движок с наложением текстур в реальном времени. Файл карты и текстуры из него датируются февралём 1993 года, то есть это было примерно через месяц после выхода Ultima Underworld 2, и за десять месяцев до выпуска DOOM.
В Underworld были наклонные полы и возможность смотреть вверх и вниз, поэтому им требовался корректное с точки зрения перспективы наложение текстур, но у них его не было. UW1 выполняла всё наложение текстур отрисовкой аффинных горизонтальных полос, так что на стенах коррекция перспективы отсутствовала, зато полы и потолки отрисовывались правильно. Я не разобрался в этом тогда, но UW2 разделяла четырёхугольники на треугольники и рисовала аффинные треугольники, поэтому требовавшие перспективы четырёхугольники больше не выглядели растянутыми и не искажались странным образом; сохранялись прямые линии, но с диагональным швом.
В представленном выше демо я самостоятельно (как мне кажется — в то время у меня были только колонки Абраша и Usenet — возможно, кто-то в Usenet это обсуждал?) изобрёл идею наложения текстур на стены/полы, которая позже была намного лучше продемонстрирована в DOOM. Думаю, я выполнял рендеринг спереди назад с буфером заполнения, чтобы снизить затраты на пиксельную скорость заполнения. Как вы видите, когда я поднимаюсь выше, чем высота стены, отсечение обратных граней не выполняется, но буфер заполнения всё равно позволял снижать затраты. В какой-то момент я попытался переписать буфер заполнения и добавил ошибку, которую можно заметить: полигоны иногда пропадают с правого ребра или сортируются неправильно, хотя в исходном движке такой проблемы не было.
Наконец, у меня было то, что можно назвать первым отложенным рендерером, хотя на самом деле это не отложенное затенение или отложенное освещение. Чуть подробнее я расскажу о нём ниже.
В этом демо я применил динамическое освещение реального времени в статичной сцене. Я воображал, что он может использоваться для игры с пререндеренной графикой, а-ля Alone in the Dark или BioForge. (BioForge была выпущена только в 1995 году, но я определённо видел её скриншоты до того, как устроился в LGS.)
Сначала оно рендерит сложную сцену с наложением текстур не в реальном времени (во время запуска) в заэкранный буфер (в g-буфер, если хотите) с использованием идеальной коррекции перспективы. Каждый g-буфер содержит diffuse albedo, отражающий цвет и испускаемый цвет, но все они упакованы только в 8 бит на пиксель (с помощью палитры, но отличающейся от 8-битной палитры отображения).
В реальном времени движок заново рендерит те же полигоны, но без текстурирования, только освещение. В вершинах полигонов вычисляется диффузное и отражённое освещение для белой поверхности, а затем (как я предполагаю) каждое из них отдельно интерполируется на весь полигон. В каждый пиксель загружается значение из g-буфера, а освещение вычисляется распаковкой диффузного и отражённого освещения в 8 бит (снова предполагаю), а затем выполняется поиск цвета отображения в таблице поиска 256x256.
Надо учесть, что поскольку освещение вычисляется только в вершинах, оно не очень качественное. Думаю, я понял, что мы можем динамически подразделять полигоны, требующие лучшего освещения, если дошёл до такого (часть этих полигонов заранее подразделена, чтобы улучшить в них освещение).
В LGS я в каком-то смысле вернулся к этой идее, создав что-то вроде 8-битного спрайта с наложением по карте нормалей; в каждом пикселе спрайта имелся 8-битный цвет и 8-битный «индекс освещения»; концептуально индекс освещения индексировался в палитру нормалей, но на практике индекс создавал направленное освещение, диффузное и отражённое, а также самозатенение. (Например, предварительно рендерим освещение с этими эффектами из N направлений освещения; теперь обрабатываем их как спрайт с N 8-битными каналами цвета, по одному цвету для освещения под каждым углом, а затем вычисляем 8-битную палитру для этого спрайта с «глубоким цветом». Например, сжатием векторным квантованием. Для рендеринга в реальном времени выполняем смешивание между k ближайшими источниками освещения. Детали высокого разрешения и 8-битные выходные данные скрывают большую часть артефактов.)
Однако после Terra Nova никогда не делал игр со спрайтовыми персонажами, поэтому это стало ещё одной моей дюжиной изобретений в графике реального времени, которые остались бесполезными.
Автор: PatientZero