Программируем графику на Direct3D 11 в среде .NET

в 9:13, , рубрики: .net, direct3d 11, DirectX, game development, Gamedev, метки: , , , ,

Этой статьей начинаю серию уроков по программированию 3D графики с использованием Direct3D 11 в среде .NET.

В этой части (пусть это будет часть 0) предыстория. План на следующие части такой:

  1. Теория: фундаментальные понятия и термины в 3D графике
  2. Теория: Game loop, различные подходы к организации цикла рендера и обработки логики в играх
  3. Практика: «Hello world» в мире 3D-графики или разноцветный вращающийся кубик на DirectX 11
  4. Практика: пишем плагин для импорта из 3dsmax
  5. ???

Статьи рассчитаны на подготовленного программиста, знакомого с платформой .NET в общем и языком C# в частности. Объяснения будут даваться только по специфичным для 3D графики концепциям.

1. Пролог

Если покажется слишком скучно или слишком пафосно — переходите сразу к параграфу 2.

Играл я в детстве по-долгу и погружался в процесс с головой. Dendy, SEGA MegaDrive, затем и PC. Бессонные ночи перестрелок и крошилова, бессонные ночи брождения в квестах, прилипчивые и затягивающие аркады… Так и получилось, что программированием я увлекся благодаря играм. Первая книга по программированию — Керниган и Ричи, была зачитана чуть ли не до дыр, в далеком 1998. Почему именно эта? Банально — первой под руку подвернулась из библиотеки родителей. Сначала примитивные текстовые угадайки, затем крестики-нолики, тетрис на паскале под DOS был написан (не без багов, конечно) за несколько часов еще в 8 классе школы, была даже попытка сделать текстовый квест, на который, впрочем, опыта и развитости абстрактного мышления не хватило. В итоге после всего этого возникло желание сделать что-то более технологичное.

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

Незадолго после этого, примерно в 2004 году, вышел еще один фреймворк-компилятор от того же разработчика под названием BlitzMax. 3D в нем не поддерживалось, хотя 2D было реализовано именно через 3D, без всякого DirectDraw, со всеми вытекающими плюсами. Кроме того, он был полноценным компилятором на основе GCC и FASM, что делало полученный код существенно более производительным, хотя и потребовало существенного изменения синтаксиса «бейсика», который стал уже практически объекто-ориентированным. Рендер работал посредством Direct3D или OpenGL, на выбор, код программы при этом никак не менялся. С помощью этого инструмента был за пару месяцев написан снова тетрис, но уже с «продвинутой» графической составляющей, звуками и музыкой, таблицей рекордов.

В то время я как раз решил бросить университет и пойти работать по призванию. То есть — программистом (чему, собственно, тогда нигде не учили, разве что клепанию окошечек в Delphi). Подвернувшаяся работа была весьма далека от игр — веб, но тогда еще интересная, и как следствие, я начал про них постепенно забывать.

Прошло много (по крайней мере так кажется) лет… Веб кажется невыносимо скучным и однообразным, разработка баз данных, толстых и тонких клиентов, бизнес-приложений — чуть веселее, но уже не вдохновляет на подвиги, и решил я некоторое время своего досуга посвятить тому, что всегда было интересным и загадочным, а главное — передовым и технологичным: играм и 3D графике.

После некоторых раздумий, было решено, что в 2011 году глупо изучать низкоуровневые технологии, чтобы сделать казуальную игру типа арканоида или тетриса, достаточно взять Flash или любую другую технологию. Но арканоид и тетрис делать было уже совершенно неинтересно — во-первых, в них уже давно наигрался, а во-вторых, что нового я для себя открою, кроме еще одного ненужного языка? И было решено — полезть в дебри и попробовать разобраться с самыми основами. Выбор пал на DirectX 11, к тому времени я как раз купил новый ноутбук с весьма продвинутым Nvidia GTX460M взамен старому с интегрированным Intel, и глупо было бы не воспользоваться ситуацией.

Кто-то спросит, почему не OpenGL? Все просто — я не нашел смысла делать игру для какой-либо платформы, отличной от Windows (ну, кроме PS и Xbox — но у первого своё API, а у второго, опять же, DirectX).

Тут надо сделать сноску, что я хоть и играю сейчас довольно мало относительно всего времени, проводимого за компьютером, но геймер я весьма хардкорный. То есть всякие фермы, и прочие собиралки-кликалки неинтересны мне как класс, а делать игру, в которую не стал бы играть сам — зачем? Что касается мобильных игр — сама идея игр на мобильных платформах мне непонятна (зачем играть в примитивные игры на маленьком мобильнике, когда есть мощный PC и большой экран), а альтернативные ОС среди хардкорных геймеров я не встречал вообще. Вследствие всего этого кроссплатформенность, являющаяся основным преимуществом OpenGL, оказалась ненужной.

2. От слов — к делу

Скачан последний DirectX SDK, смотрим в примеры. Примеры на C++. А мой любимый язык — C#. Нестыковка. Несколько мучительных запросов к поисковикам показали, что для .NET существуют два враппера над DirectX: SlimDX и SharpDX. Сравнив принципы генерирования кода оных врапперов, сам код, и погоняв простенькие тесты, убедился что SharpDX в части взаимодействия с Unmanaged кодом проработано глубже и оверхеда (а так же багов и утечек) имеет существенно меньше, вследствие чего и было выбрано. Кроме того, SharpDX собирается под Any CPU, что позволяет делать один билд как для x86 так и для x64 архитектур. Фактически этот выбор на код почти не влияет — это не более чем врапперы, пишем мы по прежнему под голый DirectX, разница разве что в именовании — по другому названы пространства имен, и где-то другой регистр, где-то чуть по-другому именуются классы. Судя по заявлениям авторов обоих врапперов, функционал Direct3D 11 покрыт полностью и работает практически идеально. Поверим им на слово. Тем более, что оба вполне успешно используются в играх (хотя и не слишком навороченных).

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

Использовать будем API 11 версии. Direct3D 11 хорош еще и тем, что позволяет установить в коде уровень требуемой аппаратной поддержки — так называемый Feature level. Он может находится в пределах от D3D9_1 до D3D11_0, включая все промежуточные версии. То есть мы используем один единственный апи 11 версии, но игра запускаться сможет на любом железе, удовлетворяющем выставленному нами Feature Level'у, при этом совершенно неважно, какую видеокарту использует разработчик: если мы выставляем в коде Feature level 9.1 и у нас игра работает — мы можем быть уверенными, что она заработает везде, где 9.1 поддерживается (ну, пожалуй кроме клинических случаев, таких как Intel GMA — это разговор отдельный. Хотя простые вещи без навороченных шейдеров и на ней запускаются). При этом, само собой, DirectX рантайм должен быть установлен всегда 11 версии.

Итак, у нас есть DirectX SDK, с ним примеры на C++, .NET враппер, полностью поддерживающий Direct3D 11. Но начисто отсутствует какая-либо вменяемая документация, как всем этим добром пользоваться.

Для начала выясним, в чем же отличие рендера Direct3D 10 и старше от классического рендера, и почему использовать их значительно проще, чем Direct3D 9, а также как вообще устроены современные видеокарты и как с ними работать.

Продолжение следует.

Автор: lucas_iv

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js