Автор: Джон Кармак «Carmack»

Опубликовано: 27.03.2011

Изменено: 27.03.2011

Постоянная ссылка

Комментарии [4]

Выступление Джона Кармака на QuakeCon 2004





Страницы: 1 2

Поверхности

Так, будет еще одна важная вещь: много моделей поверхностей. Есть некоторые специфичные вещи, которые мы хотели бы сделать, добавляя такие возможности, как распространение света в материалах (subsurface scattering), чтобы цвет кожи выглядел лучше, частичная полупрозрачность, чтобы получить что-то вроде свечения через края частично прозрачных объектов, таких как уши, освещенные сзади. Приемы, чтобы сделать волосы лучше и т.п.

Прозрачность, не зависящая от порядка отрисовки

Я был удивлен, когда спросил Тима, что бы он хотел улучшить с точки зрения гейм дизайнера. Больше всего нужна полупрозрачность, не зависящая от порядка объектов (order independent translucency). В Doom´е нет хорошего решения для полупрозрачности, не зависящей от порядка объектов. Примерно такой же подход у нас был в Quake3, там различным материалам можно было присвоить значения для сортировки и маленькие значения всегда рисовались до больших значений. Есть ситуации, когда это никак не может работать, например, если у нас две поверхности с альфа блендингом и вы можете подойти с любой стороны, то есть объект A может располагаться перед объектом B, а может объект B располагаться перед объектом A. Мы не можем заставить текущий движок отобразить это корректно.

Можно делать всякие глупые вещи, такие как говорить где находится игрок и менять материалы на другие, с другим порядком сортировки. Но сейчас это будет выглядеть нормально с одной стороны, а на другой будет очевидно некорректный блендинг. У меня есть хорошая теория как это можно поправить, есть пара направлений, пара предположений, как это поправить.

Первый путь это сделать различные слои видимости, где в дополнении к тому, что находится в прямой видимости, можно иметь несколько слоев прозрачности, движок сможет распознать где они перекрываются и, если прозрачности перекрываются, будет создан новый буфер и движок расположит их там вместе как надо. Это никак не решит проблему с самопересечениями одной прозрачной поверхности, но я не думаю, что это важная проблема и что ее надо решать.

Недостатки этого подхода: потенциально он может сожрать много видеопамяти, если у вас получится три плана с полупрозрачностью и их нужно будет рендерить по-отдельности, на этом может уйти много-много мегабайт видеопамяти. Здесь может помочь виртуализированная видеопамять, потому что большая часть этого не займет весь экран, но вот такая проблема есть.

Еще один многообещающий путь, возможно это наше основное направление, работать с прозрачностью в одном буфере, но как бы иногда отделяя пикселы, которые прозрачны, чтобы они не перемешивались с другими пикселами, а потом использовать постобработку, чтобы все проблемы решить разом. Я пытался сделать это на ранних этапах разработки Doom´а, но тогда не было хороших возможностей сделать фильтры для постобработки. Получалось нечто совершенно неприемлемое, просто мешанина. Однако сейчас у нас есть некие общие возможности фильтрования, и чтобы улучшить различные моменты, я много работаю с фильтрованием на последней стадии обработки. Я сделал несколько демок по полупрозрачности, самый простой возможный вариант: например вам нужно, чтобы объект был прозрачен на 50 процентов. Вы используете для него отдельную пунктирную (stipple test) текстуру, у вас там используется только 50 процентов от пикселов и они совершенно непрозрачны, это важно для рендерера. Половина пикселов показывает сам объект, половина то, что находится за ним.

С точки зрения отрисовки все работает хорошо, освещение и затенение такие как надо и все работает, потому что это непрозрачная поверхность. А потом идет последний проход, который отрисовывает прозрачные объекты, по существу смешивает вместе четыре рядом стоящих пиксела.

Когда у вас фиксированная сетка пикселов, как, например, каждый второй или каждый четвертый пиксел, то есть маска регулярная, это выглядит отлично. Это идеальная полупрозрачность, на нее падают тени, на нее падает свет, из-за полупрозрачности видно то, что находится за ней, и работает все здорово. Дело в том, что на этом этапе мы можем еще улучшить то, что у нас есть. Если бы можно было для данного полупрозрачного объекта указать, какого вида пунктир будет, тогда имея объект A и объект B с непересекающимися пунктирами или пересекающимися только в определенных местах, вы бы получали полупрозрачность, не зависящую от сортировки и это работает замечательно. Гораздо хуже, если вам нужны произвольные уровни прозрачности.

Сейчас это можно сделать с помощью дизеринг-операций (dithering operations). Если вы используете дизер-маску размером 2x2 или 2x4, или маску пунктира, значения фиксированы, а затем, статически или случайным образом, сдвигаете полученное значение прозрачности, полученной либо из карты прозрачности, либо из альфа-интерполятора, из чего угодно, что вы для этого используете. Можно все это смешать и делать из этого случайные выборки, но я не доволен результатом, даже если я накладываю достаточно широкое ядро фильтра, и если выбирать случайным образом различные маски, это все еще визуально слишком зашумлено. У нас есть определенные уровни с хорошим качеством, это 25%, 50%, 75%, неважно, которые выглядят превосходно, а когда между ними интерполируешь, привносится все больше и больше шума. Это то направление, к которому я сейчас склоняюсь, но лишь позже, когда у нас будет больше результатов, мы сможем оценить все проблемы, которые возникают при этом подходе.

Разработка следующего движка

Итак, многие интересные графические технологии могут войти, а могут и не войти в следующий движок. Их много, потому что теперь у нас есть гибкий интерфейс для программирования, который можно использовать, особенно не затрагивая движок. Неинтерактивные поверхности и то, что определено с тем же окружением, что мы используем для обычного освещения, можно просто дать на вход программе. Искусство разработки движка заключается в выборе фундаментальных предположений, которые будут встроены в ядро движка.

Что будет выставлено как программируемые фичи? Как будут происходить процесс создания контента и использование движка? Сложно сказать, насколько важна каждая из этих вещей, там внутри много моментов, который я считаю упущениями в движке Doom´а. Например деформации поверхностей с помощью автоспрайта или с использованием каких-либо других деформаций, иногда происходят в не том месте конвейера и освещаются. Безусловно, мы хотим это поправить в следующем движке, где вся геометрия будет освещена и затенена абсолютно корректно.

Интересные моменты связаны с написания ядра рендерера Doom´а 3. Оно может рендерить практически те же самые картинки, что у нас есть сейчас. Это было 4 года назад, и я делал это на С. Я взял Quake3, удалил оттуда рендерер, написал совершенно новый рендерер на C, встроил и начал тестировать. Когда вся команда начала работать над Doom´ом мы решили все делать на C++. Все было подключено, начали писать новые куски кода. Вся остальная работа над отрисовщиком делалась на C++, но там все еще есть наследие C, которого не будет в новом движке. Там все будет построено на обмене сообщениями между объектами, а не на передаче структур. Я наполовину поменял это в Doom´е, когда вы взглянете на заголовки SDK, вы увидите симпатичные новые интерфейсы классов. Но есть еще сетап, где вы передаете указатели на элементы отрисовки и отрисовываете источники света вместе со структурами данных, а на самом деле там должен быть просто класс.

Интересно, что когда я начал работу над следующим движком пару месяцев назад, когда я сел и стал тестировать некоторые вещи, сделал некоторые реальные фичи для теста рендеринга, я заметил, что этот экспериментальный режим опять отбросил меня назад на функциональное C программирование. В конце концов я сделал класс, который инкапсулировал ужасный pbuffer и интерфейс отрисовки в текстуру. Когда же я просто хачил графику, как-то было более естественно для этого использовать функциональный интерфейс программирования. Любопытно, это верно только для меня, или так на самом деле работают с графикой?

Когда вы делаете реальный движок, который будет взаимодействовать с большим количеством различных вещей, здесь вне всяких вопросов ценен строгий объектно-ориентированный интерфейс. Внутри все у нас еще немного Сишное, даже с новыми возможностями.

Многие проблемы с дизайном движка отрисовки на самом деле не связаны с рисованием картинок, потому что все рисуют все одинаково, вне зависимости от того, что рисуется это приводит к одному: линкуем фрагментную программу, вертексную программу, устанавливаем параметры, подгружаем текстуры, рисуем пачку треугольников, внутри это все абсолютно одинаково, все так делают для 3D железа.

Так что в теории, все движки могут рисовать медию (media) всех других движков, потому что в основном они делают все то же самое. Все инновации и важные решения принимаются по поводу того, как именно вы определяете какая будет геометрия, какие будут текстуры, какие будут программы. На что я всегда ругался - это когда люди делают просмотровщики шейдеров и вещи типа этого и интегрируют шейдеры в утилиты типа 3D Studio and Maya, такие вот, не очень полезные вещи.

Да, это позволит вам взять программу и скормить ей геометрию, но все интересное, что случается в движке, идет от таких вещей как взаимодействие и передача параметров, и как игровой мир определяет параметры, которые используются для отрисовки, как движок отрисовки составляет вместе различные слои эффектов и разные части программ. Так что у вас не будет большого количества таких вещей как просто "вот здесь фрагментная программа". У вас это все сделано для спецэффектов, для всех эти артефактных эффектов. У нас есть марево, я включил его в игру на позднем этапе и используется оно повсюду в игре, только потому что людям нравятся такие вещи. Такие спецэффекты как эти, показывают какая есть польза от того, что "здесь фрагментная программа делает этот спецэффект". Но так много вещей являются динамическим сочетанием различных программ. Если у вас накладывается карта прозрачности, где вы должны определить какие области должны быть отрисованы, все это объединяется с разными взаимодействующими программами: с различными программами затенения, с деформациями поверхностей высокого уровня. Здесь несомненно будет происходить динамическое комбинирование различных программ.

Это одна из тех ситуаций, где я не уверен какое именно будет решение, так что, как всегда в таких ситуациях, я обычно реализую парочку различных подходов и просто смотрю что работает лучше всего. Можно пойти по многим направлениям.

Один из самых простых путей, который я возможно попробую самым первым, это добавить что-то вроде макро возможностей в фрагментные программы, чтобы можно было сказать "вычисли свет здесь, положи результат в регистр R0", и можно будет объединить свет двух проецируемых текстур или произвести вычисления, зависящие от реального расстояния, или использовать 3Д свет. Есть некоторое количество вещей, которые хочется иметь для шейдеров освещения, которые могут быть скомбинированы с различными шейдерами поверхности. В итоге хочется иметь возможность накладывать деформацию при отрисовке произвольной поверхности. Хочется иметь возможность сказать "Я хочу, чтобы деформация "трава, обдуваемая ветром" была наложена в нескольких различных местах". У нас есть ветки и трава и всякие разные вещи, которые могут использоваться на статических поверхностях, но также хочется, чтобы их можно было деформировать.

Приходится все усложнять, если у вас есть различные вычисления касательного пространства... В использовании глобальных карт перед локальными есть определенные преимущества даже для деформированных вещей, где вы деформируете по многим осям, а не просто сдвигаете вершину. И если у вас есть такая вещь, понятие о том, что такое карта нормалей, может быть различным.

У нас есть такие вещи как карты высот, которые могут быть включены в игру, даже хотя они гораздо хуже карт нормалей для характеристик поверхности, но карты высот могут быть использованы для других вещей, например: если у нас со временем появится возможность делать карты смещений (displacement mapping) в игре, потребуется карта высот, а не карта нормалей.

Есть вещи, которых можно добиться дешевыми хаками. Например, я пробовал сделать деформацию поверхности, основанную на карте высот, чтобы сымитировать карту смещений. Это не работало достаточно хорошо, можно сделать текстуры, где это будет выглядеть действительно круто, но если попытаться использовать это на всем подряд, проявляется слишком много мест, где все какое-то ободранное и покоробленное и выглядит не очень хорошо.

Это пример простого эффекта, мы можем использовать его для некоторых поверхностей со спецэффектами и прочих интересных вещей, но вообще он не очень пригоден к использованию. Карты высот также понадобятся, если мы будем делать вещи вроде перекрывания карт рельефа (bump map occlusion), так что получится самозатенение между неровностями различных областей, опять это это будет ценой одной дополнительной текстуры. Много здесь проблем.

У меня есть интересные мысли сделать возможным что-то вроде карты смещений пространства экрана, где мы рендерим различные смещения в экран, а потом идем назад и рендерим сцену, деформируя при этом объекты надлежащим образом. Это решит проблему с Т-пересечением (T-junction (cracking) problem), которая получается, если использовать настоящую карту смещения на поверхностях, где края не обязательно выровнены.

Мы можем здесь сделать много интересного. Мы начнем создание медии (media) с новым движком очень скоро. И в течение месяца или около того, я предполагаю, художники начнут использовать какие-то из новых фич, такие как карты яркости и строить сцены с мягкими тенями и все такое прочее. Я в какой-то степени жду поддержку со стороны продавцов железа, что они заставят буферы теней работать на полную мощность, это нам понадобится, чтобы на них перейти.

Я ожидаю, что к концу этого года мы, возможно, будем рендерить некоторые демо сцены, которые покажут, какую технологию мы в конце концов будем реализовывать. Чтобы довести движок понадобится еще один полный год. И с интерфейсами тоже: что за API будет и как медия для его программирования будет использоваться.

Я ожидаю, что в итоге появится гибкость и можно будет программировать такие вещи как уровень взаимодействия поверхностей, уровень освещения, деформации, уровень прозрачности. Если необходимо вы сможете включить это в полную программу и делать именно то, что захотите.

Сравнения с офлайновыми рендерерами

У нас будут практически те же возможности, что и у обычного построчного офлайнового рендерера и если захочется, можно будет взять игру и поставить значения повыше.

Например, можно будет использовать настолько большие текстуры, насколько захочется, во многих местах можно будет включить уровни сэмплинга повыше. Если захочется сделать очень-очень точное сияние света с высоким динамическим диапазоном (High Dynamic Range), можно, скажем, вместо того, чтобы три раза уменьшать размер сэмпла, делать их на уровне родного фрейм буфера, а вместо того, чтобы делать отдельный фильтр по Гауссу (Gaussian filter), взять и использовать настоящий фильтр размером 100х100, если вам действительно очень нужны идеальные линии сияния, исходящие от объектов.

И там будут области, где изменение данных позволит вам поднять производительность или качество ценой производительности и можно будет сделать рендеринг такого качества "как в кино" (film quality). Этот термин упоминается постоянно, с наступлением аппаратно ускоренной отрисовки (hardware accelerated). Многие люди упоминали этот термин говоря о Doom´е, но мы все еще живем с ограниченным набором возможностей рендерера и все еще есть огромное количество вещей, которые нужны для офлайнового рендеринга, но которые игровой движок не может делать.

Со следующим движком у вас не будет абсолютно всех возможностей, которые есть у офлайнового рендерера. Но вы сможете делать сцены, которые будут практических неотличимы от обычного офлайнового построчного рендерера, если вы подадите на вход соответствующие данные и будете избегать некоторых вещей, которые он не делает хорошо. Мы сейчас видим графику с аппаратным ускорением, особенно готовящуюся к выходу графику с многочиповыми, многокарточными опциями, где у вас будет возможность, имея одну систему, ваш обычный бежевый бокс, набить туда несколько PCI-express систем с видеокартами, подключенными друг к другу, и вы получите, с таким игровым движком и с таким железом, возможности отрисовки такие как у крупных студий, как у всей Пиксаровской фабрики, и все это будет в коробке стоимостью 10 000 долларов.

В итоге получится не только производительность фабрики по отрисовки. Если посмотреть на это в терминах общего числа возможных кадров в определенный промежуток времени, важным фактором является дробная задержка. Если отрендерить кадр такого качества как в фильме занимает 30 минут, мы можете на это бросить 1000 систем и рендерить кадры пачками, но первый кадр все равно отрендерится через 30 минут. Если получится убить задержку так, чтобы рендерить на самом деле за 1/1000 от этих 30 минут, это будет гораздо лучше с точки зрения креативности.

Я думаю, вокруг будут происходить интересные вещи. Уже есть студии работающие с отрисовкой с аппаратным ускорением. Они подходят под другим углом. Они смотрят так "как мы можем взять реальный офлайновый рендерер и начать использовать технологию GPU, чтобы его слегка ускорить?". В то время как мы подходим со стороны "как мы можем заставить игру, которая уже сдизайнена, использовать все имеющиеся возможности эффективнее и получить все эти фичи, что есть у офлайнового рендерера?".

Между этими подходами будет много интересных пересечений. Несколькими годами спустя будет казаться анахронизмом, если какие-нибудь студии решат работать только старым способом, использовать только огромные офлайновые рендереры. Все еще будут случаи с многомиллионными фильмами студий, где им надо получить совершенно определенные отражения именно так, как они хотят, фильтрацию именно такую как они хотят, но все, кто оценивает здраво, будут двигаться в направлении отрисовки в реальном времени, ускоренного GPU. Возможно, первым делом мы это увидим в шоу на телевидении, но не так уж и нескоро отрисовка с качеством как в кино или хотя бы с использованием ускорения GPU будет в классических рендерерах и возможно в нескольких случаях будет эффективно использовать для отрисовки игровые движки.

Опасность излишнего усложнения движка на примере звука в Doom 3

Интересную вещь можно отметить о технологии движков в целом. У нас здесь есть хороший пример. Doom пожалуй больше выигрывает по качеству звука, чем по качеству графики. Из этого следует извлечь несколько уроков. Я взялся за работу над звуковым движком в прошлом году, после того как Грэхэм ушел, и мы внесли крупные изменения в то, что Doom делает со звуком.

Когда мы начинали, мы знали, что у нас намного больше мощности CPU и мы можем делать более сложные вещи с аудио. Итак, изначальный аудио движок Doom´а имел моделирование головы, моделирование комнаты и весь этот обычный для DSP набор вещей, который есть когда вы думаете о виртуальных пространствах и эмуляциях. Это вроде как работало, но у нас были опции где можно было сказать "здесь использовать простой звук". И если звуковым дизайнером не нравилось как что звучит, потому что движок что-то не так делает со звуком, звук можно было поставить обычным.

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

Кода осталось меньше половины с тех пор как я за него взялся. Сейчас он хороший, добротный, предсказуемый и делает то, что звуковые дизайнеры от него хотят. Так что в этом случае кажется, что звук у нас феноменальный, и при этом все сделано очень просто. Дизайнерам для работы было предоставлено хорошее полотно, они знают что каким образом будет звучать. Они могут их сделать слегка потише, в зависимости от того где вы находитесь. У нас есть возможность дать им проигрывать нелокализованные стереозвуки, урезать звуки. У нас есть несколько базовых фич, вроде "Вы хотите, чтобы это было заглушено?" "Вы хотите сделать цепочку порталов?". Но в целом все что делается, это берутся звуки, домножаются на текущий коэффициент затухания и складываются вместе. Здесь всегда есть опасность излишне усложнить движок, и, я думаю, мы избежали этого со звуком, вернули все и сделали именно то, что нам было нужно.

Это всегда вызывает беспокойство в связи с графическими технологиями, где можно делать очень сложные вещи, которые будут корректны, особенно с перемещением света: мы точно знает как работает свет, мы его можем очень точно имитировать. Если мы хотим потратить на это время, то можно сделать трассировку фотонов (photon tracing) и излучательность (radiosity) и все такие вещи. Но во многих случаях выходит, что это не только не нужно, но во многих случаях это даже не то, что вы хотите сделать с точки зрения дизайна игры. Например, прямо сейчас, когда идет эта видеозапись, несколько источников света установлены таким образом, чтобы было лучше видно то, что записывается на видео, не нужно натуральное освещение комнаты, в которой я нахожусь. И в офлайновых рендерерах свет устанавливается таким образом, что он не ведет себя точно так как ведут себя настоящие источники света: либо игнорируют поверхности, либо не отбрасывают тени, свет, который падает только на определенные вещи.

Утилиты, использовавшиеся при разработке Doom 3

Я всегда думал, что важно было предоставить утилиты, которые ведут себя так, как того хотят дизайнеры. Так что если вы даете что-то квалифицированному специалисту, а вы знаете, что у вас талантливые люди создают медию, если вы даете им утилиты, которые они понимают и которые работают так как они хотят, и, будем надеяться, работают с короткой задержкой, которая позволит им быстро просматривать то, над чем они работают, это самая правильная вещь, которую вы можете сделать в движке игры.

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

Сделать быструю подгрузку медиа тоже было большим шагом. Уйдя от сложной офлайновой обработки, которая у нас была в серии Quake, мы получили вместо 30-ти минутного перерасчета освещения немедленный эффект, просто надо подвинуть свет или изменить его цвет и это сразу везде видно. У нас есть некоторые вещи, которые мы собираемся улучшить в следующем движке, чтобы увеличить интеграцию между редактированием игры и разработкой уровней.

В течение долгого периода времени я был сторонником отдельных утилит, и я все еще думаю, что у меня было достаточно на это оснований в то время. В то время как некоторые люди встраивали редакторы уровней в игры, мы использовали отдельные программы, потому что мы использовали тогда для этого отдельное железо. У нас были современные рабочие станции, мы на них могли все запускать, а некоторые люди редактировали игры на тех платформах, на каких в них предполагалось играть.

Но сейчас эта разница исчезла, Doom сделал Правильную Вещь, редактор уровней теперь интегрированный. Но все еще много вещей, которые мы можем сделать, чтобы получить выигрыш от этой интеграции, которых у нас еще нет. Такие вещи как иметь возможность играть в игру и динамически что-либо менять. У нас есть редактор звуков, встроенный в игру, где аудио дизайнеры могут запускать и менять звуки буквально во время игры. Очевидно, мы должны редактировать свет таким же образом. И есть еще несколько вещей, которые мы сделаем так же. Они потребуют чуть больше усилий с точки зрения программирования. Например, мы должны уметь сбрасывать позиции объектов прямо в процессе игры, нужна возможность в произвольный момент просто вернуть все на изначальные позиции и условно перестартовать уровень. Нас ждет много работы по дизайну архитектуры.

Следующая игра

Основную обеспокоенность у нас вызывает то, что мы не хотим, чтобы следующая игра заняла у нас так много времени как Doom. Так что мы собираемся разумно оценить внесение изменений. Я уверен, что рендерер займет меньше года, останется много времени, чтобы все сделать и все отшлифовать. Все остальные изменения в системе... Мы попытаемся все сделать таким образом, чтобы не заставлять дизайнеров уровней работать со всем сломанным в течение года или более, перед тем как они смогут начать работать по-нормальному.

Мы все очень взволнованы тем, что собираемся делать со следующей игрой. Мы пока об этом много не говорим, но я думаю, это на самом деле довольно хороший план, когда вы продвигаете такую новую технологию, как новый движок Doom´а. Люди ждут, что первая однопользовательская версия будет чуть медленнее и при таких условиях вы можете это допустить.

Потом, когда дополнения, сиквелы и вещи вроде того выйдут, вы можете использовать ту же технологию, а железо уже развивалось еще год или два, и неожиданно то, что было на грани по скорости на старом железе, превращается опять в 60 кадров в секунду на новом. Это лучше для многопользовательской игры. Потому что в многопользовательских системах выигрыш от добавления крутой новой графики не такой большой. На самом деле в большинстве популярных многопользовательских игр графика не такая уж и хорошая, они популярны, потому что интересны.

Мы можем делать интересные игры. Мы думаем, что делаем хороший дизайн игр. Но если id Software хочет сыграть на наших сильных сторонах как компания, а у нас есть замечательная технология и медиа в дополнении к дизайну игры и геймплею, мы выпустим другую игру, добротную игру с погружением для одного игрока, с минималистским мультрплеером. Опять же, уровень Doom´а, где все это есть, это базис, если люди хотят больше этого, пускай, и потом у нас будут компании-партнеры, возможно они будут работать над тем, чтобы поднять это на супер высокий уровень совершенства, что сегодня непросто для многопользовательских игр.

Я думал показать немного фрагментов и сцен из новых технологий, над которыми я здесь работаю. Но мы решили, что программерские демки это не лучший шаг вперед, а я ненавижу, когда какой-то размытый скриншот, который кто-то снял отсюда, выкладывают на всех вебсайтах как новую технологию от id Software. А на нем показана квадратная комната с каким-то персонажем и какой-то дым, который должен был быть очень крутым. В следующем году, когда дизайнеры смогут построить новую медию, которая будет использовать новые возможности, мы покажем что-нибудь действительно крутое.

 

Текст выступления на английском языке: carmack_quakecon2004_eng.txt

 


 

Перевод:
Елена Сагалаева
http://alenacpp.blogspot.com

 

Страницы: 1 2