• Авторизация


DirectX 10 и дальше 02-01-2008 19:22 к комментариям - к полной версии - понравилось!


Выход на рынок консолей нового поколения, с одной стороны, и DirectX 10 – с другой, поднял волну – нет, не волну, а настоящее цунами интереса к вопросам графики в играх. Люди под микроскопом сравнивают скриншоты с PC, Xbox 360 и PS3 или с DX9/DX10, стараясь найти там 10 отличий и понять, в чем же все-таки сила.

Не пытаясь объять необъятное, сегодня мы сконцентрируемся на вопросе, почему DirectX 10 – это хорошо, почему DirectX 11 – это еще лучше, и что ждет PC-игроков в ближайшие (а владельцев консолей – в несколько более отдаленные) годы.

Немного истории

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

Фокус в том, что DX10 позволяет рисовать не столько «лучше», сколько «больше, быстрее и разнообразнее». Чтобы понять смысл этой загадочной фразы в стиле гегелевской диалектики, нам нужно сделать небольшой экскурс в архитектуру современных графических API.

Десять лет назад, когда пиксели были большими, зарплаты маленькими, а графические ускорители только-только стали появляться на рынке, хороший графический движок можно было очень легко отличить от плохого. Хороший движок – это тот, который умел эффективно отбрасывать невидимую геометрию и рисовать только те полигоны, которые действительно попадают в поле зрения и не заслоняются другими объектами. Все стадии графического конвейера: преобразование координат, расчет освещения, растеризация – выполнялись на центральном процессоре и поэтому были очень-очень «дорогими». Так что практически любая оптимизация, сокращавшая количество графических операций с вертексами, пикселями и текселями, была оправданна. Именно поэтому большая часть литературы по оптимизации движков в то время была посвящена выбору наиболее подходящего алгоритма удаления скрытых поверхностей (Hidden Surface Removal – HSR), а в оставшейся части обсуждалось, как лучше адаптировать алгоритмы текстурной выборки к особенностям CPU. Первое поколение графических акселераторов не слишком изменило ситуацию: центральный процессор освободили от жутко трудоемкой растеризации, но операции по расчету освещения, преобразованию треугольников, отсечению невидимых граней по-прежнему остались на нем, хотя и были теперь уже скрыты от программистов в недрах glide, OpenGL или Direct3D.

Переломным моментом стало появление GPU c аппаратной поддержкой расчетов освещения и преобразования координат (Transform & Lighting – TnL). Уже самые ранние и примитивные по сегодняшним меркам модели GeForce в несколько раз превосходили процессоры 1999-2000 года по количеству треугольников, которые они могли обрабатывать в единицу времени. Сложность моделей персонажей и уровней стала расти намного быстрее, чем раньше.

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

Как работал типичный игровой движок прошлого? CPU определял, какие элементы окружения видимы, производил над ними разнообразные трансформации и «скармливал» треугольники растеризаторам видеокарты, затем анимировал (то есть изменял по определенным алгоритмам координаты вершин в моделях) персонажей и все, что должно двигаться, трансформировал в нужную систему координат и тоже отправлял видеокарте. Помимо очевидных неприятностей, связанных с тем, что по шине между системой и видеокартой в каждом кадре приходилось перегонять очень много данных, проявила себя «во весь рост» и не слишком очевидная раньше проблема – высокие «накладные расходы» при каждом вызове драйвера, который должен передать информацию или команды графическому процессору.

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

Batch! Batch! Batch!

Так развернулась борьба за то, чтобы делать как можно больше с помощью как можно меньшего количества вызовов драйвера отрисовки. Каждый такой вызов передает GPU целый пакет (batch) треугольников, и чем таких пакетов меньше (а треугольников в них, соответственно, больше) – тем лучше. Особенности пакетной обработки раскрыты в ставшей уже классической презентации Nvidia под названием Batch! Batch! Batch!, где было наглядно продемонстрировано, что львиная доля процессорного времени уходит только на обработку вызовов драйвера (фактически, всего лишь тысяча вызовов в кадре «съедает» треть производительности Pentium IV 3 ГГц), и даны рекомендации, как эту долю понизить.

Эта борьба перенесла фокус оптимизации движка с удаления скрытых поверхностей на уменьшение количества обращений к драйверу и сокращение объема передаваемой от CPU к GPU информации. Конечно, HSR по-прежнему имеет большое значение, и народная мудрость «самые быстрые полигоны – те, что не отрисовываются» не потеряла своей актуальности и сегодня. Однако теперь зачастую правильнее отправить «оптом» сотню-другую лишних треугольников, чем тратить время CPU на их выявление и удаление, заставляя GPU в это время курить и разглядывать журналы в абсолютном бездействии.

Как движки работают теперь? Во-первых, центральный процессор сейчас уже очень редко изменяет геометрию – он лишь двигает «кости», а за то, чтобы «плоть» объектов двигалась вслед за костями, отвечает исключительно видеокарта, запрограммированная с помощью соответствующих вершинных шейдеров (vertex shaders). Это позволяет не только разгрузить CPU от этой несвойственной ему «глупой» работы, но и передавать геометрию персонажей в память видеокарты всего один раз при загрузке уровня, что очень благотворно влияет на производительность.

Во-вторых, разработчики все активнее стараются использовать механизм instancing, позволяющий отрисовывать множество идентичных объектов всего за один вызов драйвера. Именно благодаря instancing мы можем любоваться травой и густыми лесами, которые созданы путем тысячекратного повторения нескольких шаблонных деревьев или кустиков травы, причем для отображения этих тысяч зеленых насаждений необходимо всего несколько (а иногда даже один) вызовов рендеринга. Ясное дело, при этом мы рискуем отправить на отрисовку несколько десятков лишних кустов, которые будут заведомо не видны, но это не скажется отрицательно на производительности – тем более что современные GPU обладают развитыми средствами удаления невидимой геометрии и до «дорогостоящего» этапа растеризации лишние объекты, скорее всего, даже не дойдут.

В теории все звучит замечательно, но если применять такой подход на практике под DirectX 9, то быстро обнаружится несколько подводных камней. Главная проблема в том, что все эти идентичные объекты, которые мы выводим на экран «одним махом», – они такие… идентичные. Возможности придания им индивидуальности крайне ограничены.

Само собой, о том, чтобы выводить таким способом сложно анимированные объекты – каждый со своим скелетом и со своей фазой анимации в данном кадре, – не может быть и речи. Недостаточная гибкость instancing – одна из причин, по которой этот механизм сейчас используется недостаточно, и вместо сотни-другой вызовов рендеринга получаются тысячи. Еще более серьезная причина – нежелание оптимизировать мультиплатформенные движки под особенности PC. В результате, Rainbow Six: Vegas , Need for Speed Carbon или next-gen-режим Tomb Raider: Legend «радуют» нас дикими тормозами на сравнительно простых сценах, где ничто, казалось бы, не предвещает… Но не будем о грустном.

DirectX 10 хорош тем, что дает программистам такие широкие возможности в применении instancing, о которых раньше можно было только мечтать. Фактически, теперь движок может всего один раз загрузить в память GPU множество вариантов формы, массив текстур для объектов и соответствующий вершинный шейдер. После этого в каждом кадре нужно лишь передавать видеокарте через бутылочное горлышко драйвера отмашку «Давай!» со списком положений и желаемых особенностей объектов – в результате одним вызовом можно рисовать тысячи не похожих друг на друга камней или деревьев.

Более того, благодаря возможностям DX10 эти объекты можно будет сделать анимированными. Достигается это следующим образом: в текстуру записываются позиции для каждой из костей объекта (это может быть, к примеру, второстепенный персонаж или дерево, ветви которого качаются на ветру) в каждой фазе анимации. После этого движку достаточно передать драйверу номер фазы анимации объекта вместе с его позицией и номером используемой текстуры – и вот мы получаем уникального анимированного бойца почти без затрат времени на стороне центрального процессора. Используя такой механизм, можно отрисовать всю армию Саурона буквально за один вызов рендеринга.

Почему это важно для нас, обычных пользователей? Минимизация вызовов рендеринга полезна на любой игровой платформе, но на PC c его сложными механизмами межпроцессного взаимодействия, защиты ОС и необходимостью передачи команд и данных через довольно длинную цепочку, прежде чем они достигнут «железа» GPU, она просто жизненно важна для обеспечения высокой производительности.

DirectX 10 позволит движкам работать на PC практически так же эффективно, как на консолях. Известно, что игровые PC, как правило, производительнее, чем приставки, но раньше превосходство достигалось «грубой силой», через все более мощные процессоры и видеокарты. Теперь PC сможет воспользоваться всей этой мощью гораздо эффективнее, не давая львиной ее доле «вылетать в трубу». Это значит, что топовые процессоры в комбинации с топовыми DX10-видеокартами смогут выводить в кадре просто умопомрачительное количество деталей, не достижимое в ближайшие 5 лет на других платформах. Это значит, что даже очень недорогие процессоры в комбинации с очень недорогими DX10-видеокартами будут демонстрировать весьма и весьма впечатляющие результаты, которые сделают «персоналки» значительно более конкурентоспособными в плане «цена/удовольствие от игры».

Разумеется, так будет не сразу. Скорее всего, ранние DX10-движки будут даже более медленными, чем их DX9-собратья. Должно выйти второе поколение «железа» (AMD R700 и Nvidia G90), должны быть доведены до ума драйверы этого железа, должны наработать новые подходы программисты – то есть пройдет еще минимум год, прежде чем новая PC-графика по-настоящему проявит свои преимущества.

Принципиально новые возможности

Но если бы DirectX 10 лишь повышал производительность, вряд ли он был бы достоин развернувшегося вокруг него ажиотажа. На самом же деле новый API содержит и революционные нововведения – такие, например, как геометрические шейдеры и потоковый вывод.

«Геометрия», с которой приходится работать системе рендеринга, состоит из двух компонентов: вершин и связей между ними (ребер, граней). Вершинные шейдеры, как ясно из их названия, работают исключительно с вершинами, с их координатами, нормалями, цветом, но они не имеют абсолютно никакой информации о том, к каким треугольникам принадлежит данная вершина и какие вершины являются ее соседями. Кроме того, вершинный шейдер не может удалить вверенную ему в этом вызове вершину или добавить новую, он может только изменять ее позицию и другие атрибуты.

Вершинные шейдеры, разумеется, есть и в DirectX 10 – как и раньше, они занимаются здесь преимущественно скелетной анимацией, для которой о связях между вершинами ничего не нужно знать. После того как вершинные шейдеры отработают свое и все объекты и персонажи окажутся в нужных местах и нужных позах, DX9 приступал к рисованию, а DX10 передает результат геометрическому шейдеру, который, в отличие от вершинного, оперирует ребрами и треугольниками.

Это открывает массу интересных возможностей. Самое простое, что сразу же приходит в голову, – это определение контуров объекта. Действительно, если у нас есть информация о ребре и двух прилежащих к нему треугольниках, то мы легко можем определить, лежит ли это ребро на контуре объекта или нет: если один из треугольников соседей «смотрит» на нас, а второй «не смотрит», то ребро лежит на контуре, если нет – то нет. Зачем вообще может понадобиться определение контура? Хотя бы для того, чтобы нарисовать мех.

Аналогичным образом геометрические шейдеры могут использоваться для построения «теневых объемов». На сегодняшний день существуют два основных метода создания теней: с помощью карт теней и с помощью теневых объемов. Каждый из них имеет свои достоинства и недостатки. Карты теней, к примеру, не боятся самых сложных сцен с очень большим количеством деталей и самозатенением объектов, зато они нередко являются причиной дефектов изображения и хуже работают на больших расстояниях, чем теневые объемы.

Идея метода теневых объемов состоит в том, чтобы взять плоскую фигуру – контур затеняющего объекта – и «выдавливанием» превратить его в трехмерную (это, собственно, и будет «теневой объем»). Затем, определяя места пересечения теневого объема и других объектов, можно определять, какие пиксели на экране будут затенены.

Теневые объемы, к примеру, применяются в Doom 3 и играх на основе его движка – его характерной особенностью являются очень ровные и резкие края теней, хотя в последнее время их и научились искусственно сглаживать. Вплоть до появления DirectX 10 использование теневых объемов было достаточно проблематичным из-за того, что большую часть обсчета теней приходилось осуществлять на центральном процессоре. А это, помимо прочего, означает, что CPU приходилось еще и лишний раз делать скиннинг моделей, то есть дублировать работу, выполняемую вершинными шейдерами на GPU. Разумеется, такое затенение было ресурсоемким и недостаточно качественным: чтобы сильно не напрягать процессор, для скиннинга и последующего создания теневых объемов персонажей использовали упрощенные модели. Теперь, с переносом всех вычислений на графическую карту, все будет намного эффективнее, логичнее и удобнее.

Тем разработчикам, которые предпочитают использовать вместо теневых объемов карты теней, геометрические шейдеры также могут помочь. Благодаря возможности создания нескольких треугольников из одного теперь можно будет рендерить по нескольку теневых карт (в том числе и кубические текстуры) за один проход.

Кубическая текстура – это шесть текстур, как бы «сложенных» в виде кубика. Такие карты часто используются в качестве теневых, однако еще чаще их применяют для симуляции отражающих поверхностей. Скажем, когда вы едете на автомобиле в Need for Speed, игра несколько раз в секунду (частота зависит от настроек качества) рендерит упрощенную версию окружения в кубическую текстуру, а потом накладывает ее на поверхность автомобиля, создавая эффект глянцевой поверхности, отражающей все вокруг. Благодаря геометрическим шейдерам кубические текстуры можно будет рендерить намного быстрее, эффективнее и с большим количеством деталей, это значит, что отражение теперь не будет меняться рывками, а количество объектов увеличится (сейчас это обычно только здания и огни, но не машины конкурентов).

Еще одна новая возможность DirectX 10, которую можно использовать как с геометрическими шейдерами, так и с вершинными, называется «потоковый вывод». Этот термин означает, что теперь, вместо того чтобы передавать данные о графических примитивах (вершинах, ребрах, треугольниках) дальше по конвейеру рендеринга для визуализации, их можно сохранять в памяти видеокарты и использовать много раз. Это полезно, к примеру, если нужно нарисовать персонажа со сложными тенями и сложным освещением, – в этом случае может понадобиться несколько проходов. Раньше, в DirectX 9, при каждом проходе нужно было заново прогонять вершинные шейдеры и заново вычислять координаты всех вершин персонажа. Теперь это можно сделать всего один раз, сохранив результаты в буфере, а затем сделать столько проходов, сколько нужно, серьезно экономя ресурсы GPU.

Выгоды от хранения обработанной вертексными шейдерами геометрии в буфере видеокарты не исчерпываются этим примитивным случаем. В комбинации с геометрическими шейдерами этот механизм дает возможность вытворять куда более интересные штуки. Например, можно на ходу делать тесселяцию – добавлять вершины, делая объект более гладким. Особенно интересной и полезной является возможность создания систем частиц целиком на GPU. В DirectX 9.0 частицами занимается центральный процессор. Это очень затратно, неэффективно и создает ряд серьезных ограничений.

Например, чтобы «частицы», управляющие поведением дыма, реагировали на проход рядом с ними объекта (попробуйте провести через облако дыма руку, чтобы понять, о чем идет речь) или чтобы капли воды стекали по телу персонажа, процессор должен проделать массу работы: определить положение соответствующих объектов, лишний раз осуществить пресловутый скиннинг и выяснить, где находятся частицы относительно геометрии объекта. Разумеется, из-за огромных затрат ресурсов так никто не делал. В DX10 все эти фокусы для центрального процессора почти ничего не стоят. Однократно трансформированная и обработанная на этапе вертексных шейдеров геометрия находится в буфере видеокарты, а поведение частиц, находящихся в непосредственной близости от объекта, можно смоделировать с помощью геометрических шейдеров, затратив на это очень небольшую часть ресурсов GPU и мизерную долю времени CPU

Потоковый вывод и буферизация также позволяют использовать в качестве частиц не только «бестелесные» спрайты, но и геометрические объекты: камешки, обломки, осколки стекла, брызги, – что делает эффекты еще более реалистичными. Отдельно следует отметить симуляцию тканей и одежды, которую геометрические шейдеры могут осуществлять с ранее недостижимой степенью достоверности.

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

Светлое будущее

Миграция на DirectX 10 – это дело ближайшего года. А что ожидает нас в следующие пять лет, с приходом DirectX 11?

Много интересного. Прежде всего, нужно отметить, что CPU и GPU стремительно сближаются друг с другом по функциональности. С одной стороны, центральные процессоры получают все больше ядер: нынешний «стандарт» – два ядра, в недалекой перспективе – выход на массовый рынок четырех- и восьмиядерных систем.

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

Разработчики из Nvidia и AMD/ATI уже многое сделали для того, чтобы снять подобные ограничения, и DirectX 10 – очередной шаг на этом пути. В частности, впервые программисты GPU получили возможность работать с целыми числами и даже с отдельными битами. Это позволит им реализовать собственные схемы сжатия данных и более эффективные и «умные» алгоритмы, чем раньше, когда можно было оперировать только числами с плавающей запятой. Что еще более важно, в новых GPU появились средства коммуникации между потоками – теперь отдельный поток, выполняющий вычисления, может сохранять свои промежуточные результаты в специальной области локальной памяти, и другие потоки могут из него читать.

Такие возможности чрезвычайно полезны для реализации продвинутых алгоритмов рендеринга, которые сделают изображения еще боле реалистичными. Речь идет о трассировке лучей (ray-tracing) и ее «дочерних» алгоритмах: глобальном освещении (global illumination), картах фотонов (photon mapping) и т.д. Суть этих методов состоит в том, что освещенность каждой конкретной точки сцены определяется не только воздействующими на нее первичными источниками света (лампы, огонь, Солнце), но и вторичным светом, отраженным от окружающей обстановки. Кроме того, ray-tracing позволяет достаточно естественным образом рассчитывать тени, не прибегая к сложным и не очень удобным приближениям вроде теневых карт и теневых объемов.

Ray tracing и global illumination обычно считаются «CPU-алгоритмами», не приспособленными для использования на GPU. Тем не менее уже на DX9-картах с SM 3.0 могут быть реализованы некоторые из подобных методов рендеринга – правда, они либо «тормозят», либо дают очень упрощенный результат, либо то и другое вместе, поэтому на практике (то есть в играх) они пока не применяются.

Иногда подобие трассировки лучей пытаются реализовать с помощью тандема «CPU–GPU», заставив CPU динамически генерировать вторичные источники света на ярко освещенных поверхностях, а затем передавать их GPU для обычного рендеринга. Такой механизм используется в S.T.A.L.K.E.R., однако по умолчанию он выключен, и включить его можно только с консоли – ценой примерно двукратного падения и без того не очень большого FPS.

Как бы то ни было, трассировка лучей на современных GPU относится пока что к разряду курьезов, тем более что львиную долю парка видеокарт составляют бюджетные модели, которым дай бог справиться с обычным рендерингом в приличном разрешении и с 4x MSAA. Но уже через пару лет ситуация должна измениться. Известно, что сейчас наиболее активным «двигателем» трассировки лучей в игровой графике является Intel, собирающаяся выпустить в 2009 году свой высокопроизводительный процессор Larrabee, одним из важнейших достоинств которого является как раз поддержка полноценного ray tracing.

Сообщая о планах Intel, мы выразили опасение, что компания собирается навязать рынку разработку, которая идет вразрез с основными тенденциями развития графических процессоров за последние десять лет и потому способна вызвать определенную сумятицу. Создателям пришлось бы тогда поддерживать две принципиально несовместимых архитектуры, основанных на диаметрально противоположных подходах, а это чреватом огромным количеством проблем и трудностей – куда более значительных, чем технически сложное портирование игр с Xbox 360 на PS3 и обратно.

Но, как выяснилось, наши опасения были напрасными: Intel не собирается «изобретать велосипед» и активно занимается лицензированием GPU-технологий у Nvidia. Это значит, что Larrabee будет в значительной степени похож на традиционный GPU, благодаря чему на нем можно будет использовать как обычные алгоритмы растеризации, так и ray-tracing – в зависимости от конкретных потребностей игры.

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

Компания AMD, поглотившая ATI, тоже не сидит сложа руки – там разрабатывают процессор Fusion, который будет сочетать в себе ядра CPU и GPU в одном корпусе или даже на одном кристалле, – и его премьера также состоится в ближайшие пару лет. Таким образом, уже до конца этого десятилетия мы сможем увидеть на PC несколько интереснейших архитектур с принципиально новыми возможностями, ну, а к 2012-2013 году технологическая революция докатится и до консольного рынка.

автор: Вадим Бобров
вверх^ к полной версии понравилось! в evernote
Комментарии (2):
Angel_Of_Fate 03-01-2008-03:04 удалить
дочитал до конца, интересная статья =)


Комментарии (2): вверх^

Вы сейчас не можете прокомментировать это сообщение.

Дневник DirectX 10 и дальше | -killy- - Loading... | Лента друзей -killy- / Полная версия Добавить в друзья Страницы: раньше»