netlib.narod.ru< Назад | Оглавление | Далее >

Что должен уметь делать ваш движок?

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

Благодаря XNA Framework, дающей вам много вспомогательных классов и простые способы для управления окном игры, игровыми компонентами и файлами содержимого (моделями, текстурами, шейдерами и звуковыми файлами), вам не надо беспокоиться о собственных форматах файлов, написании собственного класса управления окном или о том, как загрузить модель в ваш движок, а потом получить ее на экране. Если вы раньше работали с DirectX или OpenGL, то возможно сталкивались с тем, что примеры и уроки не кажутся трудными, но как только вы захотите использовать в движке собственные текстуры, трехмерные модели или звуковые файлы, то сталкиваетесь с такими проблемами, как неподдерживаемые форматы файлов, отсутствие поддержки трехмерных моделей для вашего редактора или невозможность воспроизведения ваших звуковых файлов. Потом вы либо продолжаете поиск, либо пишете собственные классы для поддержки этих возможностей. Классы для текстур и моделей существуют, но это не значит, что они совершенны. Иногда вам требуется дополнительная функциональность или более простой способ загрузки и доступа к текстурам и моделям. Поэтому вы пишете новые классы для управления текстурами и моделями, которые внутри по-прежнему используют классы XNA, но делают их проще для работы с текстурами и моделями, а в дальнейшем и с материалами и шейдерами также.

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

Чтобы помочь вам в последующих тестах модулей и разработке пользовательского интерфейса вам нужна возможность визуализировать линии в двухмерном и трехмерном пространстве, помогающие в процессе тестирования модулей и отладки определять правильные позиции различных элементов. Вы можете сказать «Эй, в визуализации линий нет ничего особенного, разве XNA не умеет делать это, например, как в OpenGL, с помощью метода glLine?». Это было бы прекрасно, но создатели XNA полностью удалили функции фиксированного конвейера DirectX, и, хотя вы по-прежнему можете нарисовать несколько линий с помощью буфера вершин и последующей визуализации примитивов линий, вариант оказывается слишком сложным. Помните, что вы сначала просто пишете тестовые модули, и не думаете о том, как будут рисоваться линии; вы просто хотите нарисовать линию от (0, 50) до (150, 50), или, в трехмерном пространстве, от (–100, 0, 0) до (100, 0, 0). Помимо этого, производительность вашего приложения действительно ухудшится, если вы будете визуализировать каждую линию по отдельности, создавая новый буфер вершин или массив вершин для нее, а затем избавляясь от него; особенно это будет заметно в тестовых модулях, которые вы будете делать в конце этой книги с тысячами отдельных линий, визуализируемых в каждом кадре.

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

Тестирование модулей движка

Перед тем, как погрузиться в детали реализации и проблемы трехмерного графического движка, напишите тестовые модули для конца этой главы и таким образом определите, что ваш движок должен уметь делать. Пожалуйста, обратите внимание, что начальные версии тестовых модулей слегка отличались от показанных здесь; абсолютно нормально немного изменять тесты, если вы что-то забыли или если обнаружились какие-нибудь зависимости, которые не использовались в тестовом модуле, но имеют смысл в движке и должны применяться. В общем случае вы должны пытаться заставить работать тестовый модуль не меняя его; я первоначально написал в тестовом модуле только testModel.Render(), чтобы визуализировать тестовую модель, но оказалось, что имеет смысл определить матрицу или вектор, сообщающие, в каком месте трехмерного мира будет визуализирована модель.

Достаточно разговоров, взгляните на код:

/// <summary>
/// Тест визуализации нашего нового графического движка
/// </summary>
public static void TestRenderOurNewGraphicEngine()
{
  Texture backgroundTexture = null;
  Model   rocketModel       = null;

  TestGame.Start("TestRenderOurNewGraphicEngine",
    delegate
    {
      // Загрузка фона и ракеты
      backgroundTexture = new Texture("SpaceBackground");
      rocketModel = new Model("Rocket");
    },
    delegate
    {
      // Отображение фона
      backgroundTexture.RenderOnScreen(
                           BaseGame.ResolutionRect);
      SpriteHelper.DrawSprites(width, height);

      // Визуализация модели в центре
      BaseGame.Device.RenderState.DepthBufferEnable = true;
      rocketModel.Render(Matrix.CreateScale(10));

      // Рисование линии в трехмерном пространстве
      BaseGame.DrawLine(new Vector3(-100, 0, 0),
                        new Vector3(+100, 0, 0),
                        Color.Red);

      // Рисуем прямоугольник безопасной области для Xbox 360,
      // поддержка для старых мониторов
      Point upperLeft  = new Point(width / 15, height / 15);
      Point upperRight = new Point(width * 14 / 15, height / 15);
      Point lowerRight = new Point(width * 14 / 15, height * 14 / 15);
      Point lowerLeft  = new Point(width / 15, height * 14 / 15);
      BaseGame.DrawLine(upperLeft, upperRight);
      BaseGame.DrawLine(upperRight, lowerRight);
      BaseGame.DrawLine(lowerRight, lowerLeft);
      BaseGame.DrawLine(lowerLeft, upperLeft);

      // И, наконец, небольшой текст
      TextureFont.WriteText(upperLeft.X + 15, upperLeft.Y + 15,
                            "TestRenderOurNewGraphicEngine");
    });
} // TestRenderOurNewGraphicEngine()

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

Вы можете недоумевать, почему я вызываю метод RenderOnScreen класса Texture или метод Render класса Model, когда эти методы не существуют. Тестовый модуль также использует много свойств и методов BaseGame не существующих на данный момент, но это не значит, что вы не можете позже написать их таким образом для работы. Например, вы можете решить, что следующая строка слишком длинна и сложна для написания:

BaseGame.Device.RenderState.DepthBufferEnable = true;

Тогда напишите более простой вариант, используя новый воображаемый метод BaseGame.EnableDepthBuffer, а позже побеспокойтесь о его реализации. Причина использования в тестовом модуле классов Texture и Model и наличия в них методов, подобных Render, очень проста: это мой способ размышления над проблемой. Конечно, я знаю, что в XNA нет таких методов для меня, но ничто не мешает мне написать собственные классы Texture и Model для выполнения поставленной задачи. Если вы хотите еще больше упростить тестовый модуль или сделать другие дополнения, свободно редактируйте код тестового модуля, а затем реализуйте возможности по мере чтения главы.

Небольшое замечание, перед тем как вы начнете: я хочу упомянуть, что есть некоторые дополнения в класс BaseClass и новые игровые компоненты, такие как класс камеры, которые используются в этом тестовом модуле, но не видны в форме кода, поскольку исполняются автоматически. Вы узнаете об этих дополнениях, новых классах и усовершенствованиях по ходу чтения главы.

Трехмерные модели

Перед тем, как вы начнете визуализировать трехмерные модели, вам нужна идея о том, на что должна быть похожа трехмерная модель, или, что еще лучше, какая-нибудь готовая трехмерная модель для использования. Если вы не настоящий профессионал в 3D Studio Max, Maya или Softimage, и у вас нет знакомого художника, который мог бы быстренько создать для вас какую-нибудь модель, не начинайте создавать ее самостоятельно. Вместо этого используйте свободно доступные файлы из примеров и уроков или файлы из предыдущих проектов. В этой главе используется модель Rocket из игры Rocket Commander, написанной мной в прошлом году (рис. 5.1).


Рис. 5.1

Рис. 5.1


Модель Rocket состоит из данных трехмерной геометрии (векторов, нормалей, тангенсов и координат текстуры), шейдера ParallaxMapping.fx и текстур для этого шейдера:

Все это создано художником трехмерных моделей, который также определяет, как должна выглядеть ракета, устанавливая дополнительные шейдеры и параметры материала для фонового, рассеиваемого и отражаемого света, а также другие параметры, определенные в шейдере или материале, которые могут быть установлены в 3D Studio. Этот файл модели сохраняется как файл .max, во внутреннем формате, используемом только 3D Studio Max (или в формате другой программы моделирования, которую используете вы или ваш художник). Чтобы получить все это в вашей игре вам потребовался бы импортер формата .max, который не существует ни для DirectX ни в XNA, а, кроме того, файлы .max могут быть очень большими, и не имеет смысла иметь в игре файл размером 100 Мб со всеми высокополигональными объектами, когда нужна только низкополигональная модель ракеты, могущая поместиться в 100 Кб. Вам требуется экспортер, позволяющий получить те данные модели, которые нужны вашей игре. Есть много доступных форматов, так почему я говорю об этом так долго? Ни один из стандартных форматов экспорта в 3D Studio Max не поддерживает параметры шейдеров, которые нужны для вашей игры. А XNA позволяет импортировать только файлы .x и .fbx. Настоящее свинство.

Вы, вероятно, могли бы просто экспортировать файл .x или использовать формат .fbx и жить с тем фактом, что не экспортируются параметры шейдеров, данные тангенсов и прочие вещи, необходимые для выполнения наложения нормалей, наложения параллакса или ваших собственных шейдеров, которые вы пожелаете использовать в игре. Но тогда в игровом движке придется выполнять много дополнительной работы, чтобы исправить ситуацию для каждой отдельной модели. В качестве альтернативы вы можете написать свой собственный экспортер для 3D Studio Max, что приемлемо для больших студий, но от одиночки потребует чересчур много времени и денег. Другим вариантом могло бы быть наличие некоего особого XML-файла, в котором хранились бы все параметры шейдеров для каждой модели и последующий его импорт и объединение с другими данными в движке игры. Но это снова связано с большим количеством работы, надо реализовать не только импорт и объединение, но и сохранение актуальных данных в XML-файле с параметрами. Я видел использование всех этих подходов, и вы вольны делать то, что вам нравится, но я настоятельно рекомендую выбирать максимально безболезненные варианты.

В рассматриваемом случае модель ракеты может быть с помощью разрабатываемого сообществом экспортера Panda Explorer для 3D Studio Max (рис. 5.2) экспортирована в файл .x, который, в свою очередь, может быть импортирован в XNA Framework. Он не поддерживает все возможности (отсутствуют данные тангенсов) и наличие достаточно сложных объектов с костями и данными обтягивания текстурой может вызвать проблемы, но все это пока не нужно для ваших игр. Хорошей альтернативой является новый формат Collada, который экспортирует данные из многих программ трехмерного моделирования в ясный и простой для использования XML-файл данных, импортирование которого в вашу игру будет значительно проще, чем написание собственного импортера и экспортера для данных моделей. Для скелетных моделей (это означает, что модель использует скелет из костей и у каждой вершины есть несколько коэффициентов облегания для применения к ней любых перемещений костей; такой подход часто используется в играх с видом от первого лица, когда должны отображаться различные, перемещающиеся вокруг персонажи) я могу порекомендовать формат .fbx (формат .x также работает в XNA) или просто поискать импортер для моделей .md3, .md5 или подобных форматов, которые широко распространены и используются в сериях игр Quake и Doom.


Рис. 5.2

Рис. 5.2


Экспортированный файл rocket.x теперь может использоваться в вашем движке XNA точно так же, как вы используете текстуры или другие файлы содержимого. Просто переташите файл содержимого в ваш проект и автоматически будут подхвачены все используемые шейдеры и текстуры, и будет выдано предупреждение, если они отсутствуют. Вам не надо добавлять используемые текстуры и шейдеры; обработчик моделей .x в XNA сделает это за вас автоматически. Вы просто должны гарантировать, что зависимые файлы будут найдены в том же каталоге; в нашем случае это должны быть файлы Rocket.dds, RocketNormal.dds, RocketHeight.dds и ParallaxMapping.fx. Вопросы реализации класса Model и как визуализировать модель ракеты на экране, обсуждаются в следующих разделах этой главы.

Визуализация текстур

До сих пор вы использовали класс Texture XNA Framework для загрузки текстур, а затем использовали класс SpriteHelper, который написали в главе 3, для визуализации спрайтов на экране при помощи класса SpriteBatch. Это замечательно работает для пары первых игр, но не слишком полезно для использования текстур в трехмерном окружении и остается достаточно сложным для использования со всеми этими вспомогательными классами спрайтов. Как вы уже видели в тестовом модуле для этой главы, должен быть новый метод с именем RenderOnScreen, для визуализации всей текстуры или ее части на экране. На нижнем уровне вы продолжаете использовать класс SpriteHelper и весь код, который вы писали для визуализации спрайтов, но использование текстур становится более простым и вам вообще не надо создавать экземпляр класса SpriteHelper. В дополнение вы можете позже усовершенствовать способ визуализации спрайтов и, возможно, даже реализовать свои собственные шейдеры, чтобы достигнуть этого без изменения какого-нибудь кода или тестового модуля.

На рис. 5.3 показана структура нового класса Texture; код достаточно прямолинеен и в предыдущих главах уже была описана большая часть его функциональности. Важно только не забывать, что у вас есть два класса текстуры — один из XNA Framework, а другой из вашего собственного движка. Уверен, вы сможете переименовать ваш собственный класс во что-нибудь невообразимое, но кто захочет писать все время MyOwnTextureClass, когда можно написать просто Texture? Вместо этого вы переименуете использование класса Texture из XNA, который больше не понадобится в ваших проектах нигде, за исключением вашего нового класса Texture, который внутри по-прежнему будет использовать текстуры XNA. Для этого напишите следующую строку кода в регионе с директивами using:

using Texture = XnaGraphicEngine.Graphics.Texture;

Или такой код, если вам нужен доступ к классу текстуры XNA:

using XnaTexture = Microsoft.Xna.Framework.Graphics.Texture;

Рис. 5.3

Рис. 5.3


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

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

/// <summary>
/// Тест визуализации текстур
/// </summary>
public static void TestRenderTexture()
{
  Texture testTexture = null;
  TestGame.Start("TestTextures",
    delegate
    {
      testTexture = new Texture("SpaceBackground");
    },
    delegate
    {
      testTexture.RenderOnScreen(
           new Rectangle(100, 100, 256, 256),
           testTexture.GfxRectangle);
    });
} // TestTextures()

Сейчас для вас наиболее важен метод RenderOnScreen. Свойство Valid указывает, что загрузка текстуры прошла успешно и вы можете использовать внутреннее свойство XnaTexture. Свойства Width, Height и GfxRectangle дают вам размеры текстуры, а другие свойства для вас пока не важны. Они будут использоваться позже, при визуализации материалов с шейдерами.

/// <summary>
/// Визуализация на экране
/// </summary>
/// <param name="renderRect">Визуализируемый прямоугольник</param>
public void RenderOnScreen(Rectangle renderRect)
{
  SpriteHelper.AddSpriteToRender(this,
                 renderRect, GfxRectangle);
} // RenderOnScreen(renderRect)

Выглядит исключительно просто, правда? Есть несколько перегрузок и все они добавляют спрайт к классу SpriteHelper, использующему код уже написанный вами в главе 3. Изменен только метод AddSpriteToRender — теперь он статический и получает параметр GfxRectangle вместо создания нового экземпляра класса SpriteHelper:

/// <summary>
/// Добавляем спрайт для визуализации
/// </summary>
/// <param name="texture">Текстура
/// Прямоугольник
/// Gfx прямоугольник
public static void AddSpriteToRender(
        Texture texture, Rectangle rect, Rectangle gfxRect)
{
  sprites.Add(new SpriteToRender(texture, rect, gfxRect, Color.White));
} // AddSpriteToRender(texture, rect, gfxRect)

Вы также используете класс TextureFont. Он перемещен в пространство имен графики, теперь использует новый класс Texture вместо класса Texture из XNA, и получил новые тестовые модули для визуализации текста. Остальное в классе осталось неизменным; вы используете его чтобы писать текст непосредственно на экране с помощью статического метода WriteText (для него добавлены несколько перегрузок):

/// <summary>
/// Написание текста на экране
/// </summary>
/// <param name="pos">Местоположение</param>
/// <param name="text">Текст</param>
public static void WriteText(Point pos, string text)
{
  remTexts.Add(new FontToRender(pos.X, pos.Y, text, Color.White));
} // WriteText(pos, text)

Визуализация линий

Чтобы визуализировать линии в XNA вы не можете просто воспользоваться встроенным методом, и вам потребуется собственный способ. Как я говорил ранее, есть несколько способов, и один из наиболее эффективных — создание большого списка вершин и визуализация всех линий сразу, после того, как вы соберете все требуемое для кадра (рис. 5.4).


Рис. 5.4

Рис. 5.4


Дополнительно для рисования линий вы используете очень простой шейдер. Вы также можете использовать класс BasicEffect из XNA, но гораздо быстрее будет работать ваш собственный шейдер, который просто формирует цвета вершин для каждой линии. Поскольку визуализация линий в двухмерном и трехмерном пространствах практически одинакова, просто слегка по-разному выглядят вершинные шейдеры, в данном разделе мы поговорим только о визуализации линий в двухмерном пространстве. Класс LineManager3D будет работать почти также (рис. 5.5).


Рис. 5.5

Рис. 5.5


Чтобы понять, как использовать эти классы, просто взгляните на тестовый модуль TestRenderLines:

/// <summary>
/// Тест визуализации
/// </summary>
public static void TestRenderLines()
{
  TestGame.Start(delegate
    {
      BaseGame.DrawLine(new Point(-100, 0), new Point(50, 100),
                        Color.White);
      BaseGame.DrawLine(new Point(0, 100), new Point(100, 0),
                        Color.Gray);
      BaseGame.DrawLine(new Point(400, 0), new Point(100, 0),
                        Color.Red);
      BaseGame.DrawLine(new Point(10, 50), new Point(100, +150),
                        new Color(255, 0, 0, 64));
    });
} // TestRenderLines()

Итак, все, что вам надо сделать — это вызвать метод BaseGame.DrawLine, который принимает как двухмерные точки, так и трехмерные векторы для поддержки линий в двухмерном и трехмерном пространствах. Оба класса LineManager работают как показано на рис. 5.6; просто вызовите метод DrawLine класса BaseGame несколько раз и позвольте классу LineManager позаботиться об остальном.


Рис. 5.6

Рис. 5.6


Интересный код находится в методе Render, который визуализирует все линии, которые вы накопили в текущем кадре. Код сбора линий и их добавления к вашему списку линий не сложен; если хотите познакомиться с ним поближе, разбирайтесь самостоятельно. Метод Render использует структуру VertexPositionColor для всех вершин, и массив вершин, созданный в методе UpdateVertexBuffer. Вот основная часть метода:

// Установка всех линий
for (int lineNum = 0; lineNum < numOfLines; lineNum++)
{
  Line line = (Line)lines[lineNum];
  lineVertices[lineNum * 2 + 0] = new VertexPositionColor(
    new Vector3(
    -1.0f + 2.0f * line.startPoint.X / BaseGame.Width,
    -(-1.0f + 2.0f * line.startPoint.Y / BaseGame.Height), 0),
    line.color);
  lineVertices[lineNum * 2 + 1] = new VertexPositionColor(
    new Vector3(
    -1.0f + 2.0f * line.endPoint.X / BaseGame.Width,
    -(-1.0f + 2.0f * line.endPoint.Y / BaseGame.Height), 0),
    line.color);
} // for (lineNum)

Сперва вы можете удивляться, почему координаты начальной и конечной точек делятся на ширину и высоту текущего разрешения вашей игры, затем умножаются на два, после чего из результата еще единица. Эта формула используется для преобразования всех двухмерных точек из экранных координат в ваше пространство вида, которое располагается от -1 до +1 по осям X и Y. В классе LineManager3D это не требуется, потому что у вас уже есть корректные координаты точек в трехмерном пространстве. Для каждой линии вы сохраняете две точки (начало и конец) и каждая вершина хранит местоположение и цвет, почему и используется структура VertexPositionColored из XNA Framework.

В методе Render вы просто перебираете все примитивы, которые добавили (они являются линиями, и их будет ровно половина от количества имеющихся точек) и визуализируете их с помощью шейдера LineRendering.fx:

// Визуализация линий, если они есть
if (numOfPrimitives > 0)
{
  BaseGame.AlphaBlending = true;
  BaseGame.WorldMatrix = Matrix.Identity;
  BaseGame.Device.VertexDeclaration = decl;
  ShaderEffect.lineRendering.Render(
    "LineRendering2D",
    delegate
    {
      BaseGame.Device.DrawUserPrimitives<VertexPositionColor>(
        PrimitiveType.LineList, lineVertices, 0, numOfPrimitives);
    });
} // if

Свойство AlphaBlending класса BaseGame позволяет вам визуализировать линии с альфа-смешиванием, чтобы они смешивались между собой. WorldMatrix обсуждается позже; пока убедитесь только, что мировая матрица сброшена для использования оригинальных значений местоположения из списка lineVertices. Это особенно важно для LineManager3D.

Затем устанавливается объявление вершин, сообщающее, что вы сейчас будете визуализировать вершины VertexPositionColored (так поступает DirectX, и во многом похожа работа XNA, только вы по-прежнему можете пользоваться преимуществами использования обобщенных типов и прочей замечательной функциональностью .NET):

decl = new VertexDeclaration(
  BaseGame.Device, VertexPositionColor.VertexElements);

Выглядит просто, не так ли? Так почему надо беспокоиться об объявлениях; разве не может XNA сделать это за нас автоматически? Да, может, но помните, что вы также можете создавать свои собственные объявления вершин если вам нужен особый формат, что имеет смысл, если вы пишете собственные особые шейдеры. О том, как это делается, смотрите в главе 7 в обсуждении TangentVertexFormat.

Последняя вещь, которую вы должны выяснить, чтобы разобраться с визуализацией линий, это визуализация данных с шейдерами. Да, я знаю, что нарисовать несколько линий — невелика работа, но так или иначе, чтобы сделать что-нибудь в трехмерном пространстве вам потребуется вся функциональность шейдеров. Повторяю: вы ничего не можете нарисовать в XNA без шейдеров! Вы можете сейчас сказать: «А как же класс SpriteBatch, который мы использовали для визуализации двухмерной графики и ничего не говорили о шейдерах?». Верно; вы можете ничего не знать о шейдерах, если хотите всего лишь создавать простые двухмерные игры. Но это не значит, что внутри класса SpriteBatch не используются шейдеры. Другой класс, скрывающий возможности шейдеров, — это класс BasicEffect, позволяющий визуализировать трехмерные данные без предварительного написания собственных шейдерных эффектов. Внутри он использует базовый шейдер для имитации функциональности фиксированного конвейера функций, но, подобно фиксированному конвейеру функций, он не такой быстрый, как ваши собственные специализированные шейдеры и не поддерживает ничего особенного.

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

Шейдеры будут подробно обсуждаться в главе 6. Перед тем, как взглянуть на шейдер LineRendering.fx, который переносит линии на экран, вы должны посмотреть класс ShaderEffect, позволяющий визуализировать трехмерные данные для шейдеров с помощью RenderDelegate:

/// <summary>
/// Визуализация
/// </summary>
/// <param name="techniqueName">Имя техники</param>
/// <param name="renderDelegate">Делегат визуализации</param>
public void Render(string techniqueName,
  BaseGame.RenderDelegate renderDelegate)
{
  SetParameters();

  // Начало шейдера
  effect.CurrentTechnique = effect.Techniques[techniqueName];
  effect.Begin(SaveStateMode.None);

  // Визуализация всех проходов (обычно только один)
  //foreach (EffectPass pass in effect.CurrentTechnique.Passes)
  for (int num = 0; num < effect.CurrentTechnique.Passes.Count; num++)
  {
    EffectPass pass = effect.CurrentTechnique.Passes[num];

    pass.Begin();
    renderDelegate();
    pass.End();
  } // foreach (pass)

  // Конец шейдера
  effect.End();
} // Render(passName, renderDelegate)

Класс ShaderEffect использует экземпляр эффекта для загрузки шейдера из конвейера содержимого. Затем вы можете использовать этот эффект для визуализации трехмерных данных. Это выполняется путем первоначальной выборки техники по имени или индексу и последующего запуска шейдера с вызовами Begin и End для каждого прохода визуализации. В большинстве шейдеров из этой книги и тех шейдеров, с которыми вы будете сталкиваться, только один проход, и это значит, что вы будете вызывать Begin и End только один раз для первого прохода. Обычно только шейдеры постэкранной обработки более сложны, но вы можете писать шейдеры с многими проходами, что может быть полезно для шейдеров меха или природных вещей, состоящих из нескольких слоев. Между Begin и End визуализируются трехмерные данные с помощью RenderDelegate, использованного ранее для вызова DrawUserPrimitives с массивом lineVertices.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz