Единое окно доступа к образовательным ресурсам

Введение в XNA

Голосов: 5

Книга адресована студентам и начинающим разработчикам, которые хотят использовать в своих проектах высокопроизводительную графику. Книга рассчитана на читателей, уже знакомых с основами C# и платформы .NET. Первая глава знакомит читателя с XNA Framework. Вторая глава посвящена визуализации базовых примитивов XNA Framework. В третьей главе рассматриваются более сложные вопросы визуализации: использование полноэкранного режима, плавная анимация примитивов и имитация прозрачности. В четвертой главе весь ранее изученный материал сводится воедино на примере создания полноценного хранителя экрана с дистрибутивом, при необходимости автоматически инсталлирующим на компьютер пользователя XNA Framework. В пятой, заключительной главе затрагивается тема программируемого графического конвейера, но уже на значительно более глубоком уровне; рассматриваются язык HLSL, основы ассемблеро-подобного языка Vertex Shader 1.1 и интегрированная среда разработки FX Composer 2.0, значительно облегчающая разработку и отладку шейдеров. Данная книга входит в состав <a target=_blank href="http://www.microsoft.com/Rus/Msdnaa/Curricula/">"Библиотеки учебных курсов"</a>, формирование которой ведется в рамках программы академического сотрудничества <a target=_blank href="http://www.microsoft.com/rus/msdnaa/">MSDN Academic Alliance (MSDN AA)</a>.

Приведенный ниже текст получен путем автоматического извлечения из оригинального PDF-документа и предназначен для предварительного просмотра.
Изображения (картинки, формулы, графики) отсутствуют.
                    if (WindowState != FormWindowState.Minimized)
                {
                     presentParams.BackBufferWidth = ClientSize.Width;
                     presentParams.BackBufferHeight = ClientSize.Height;
                     device.Reset(presentParams);
                }
           }


           private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
           {
                if (device != null)
                {
                     device.Dispose();
                     device = null;
                }
           }
     }
}


Обратите внимание на применение псевдонимов для пространств имен Microsoft.Xna.Framework и
Microsoft.Xna.Framework.Graphics,       позволившие    упростить  обращение    к    структурам
Microsoft.Xna.Framework.Color и         Microsoft.Xna.Framework.Graphics.Rectangle. Обычная
директива using прошлых версий C# в подобных ситуация оказывалась бессильной из-за конфликта с
одноименными структурами из пространства имен System.Drawing.


Практическое упражнение №1.1.
Создайте приложение, рисующее на экране 10 вложенных разноцветных прямоугольников (рисунок 1.15).
Самый крупный прямоугольник должен иметь синий цвет, а самый маленький зелѐный. Остальные
прямоугольники имеют промежуточные цвета, образуя плавный переход от синего к зелѐному. Приложение
должно корректно реагировать на изменение размера экрана.
         П р им еч а н ие
         Для вычисления промежуточных значений цвета воспользуйтесь конструктором public Color(byte r, byte g, byte
         b), создающим структуру Color на основе значений красного, зеленого и синих цветов. Значения цветов
         находятся в диапазоне 0…255 (0 – минимальная яркость, 255 – максимальная яркость).




Рисунок 1.14. Иллюстрация к практическому упражнению №1.1.

Если у вас возникнут трудности при выполнении этого упражнения, вы можете посмотреть исходный текст
готового приложения, которое находится на CD с книгой в каталоге Ch01\Ex04.


1.2.4. Восстановление работоспособности программы после
потери устройства.
Операционная система Windows является многозадачной операционной системой, поэтому параллельно с
вашим XNA–приложением могут выполняться десятки других приложений использующих графическую
подсистему компьютера. При этом не исключены конфликтные ситуации. Например, какая-нибудь
программа может неожиданно изменить разрешение экрана, что вызовет перераспределение видеопамяти,
что в свою очередь приведѐт к частичной потере информации, хранящейся в видеопамяти. В результате с
большой долей вероятности класс GraphicsDevice вашего приложения потеряет информацию в
видеопамяти и не сможет продолжать работу. В XNA Framework и DirectX это явление называется потерей
устройства (Device Lost).
Для начала нам надо научиться воспроизводить потерю устройства, иначе мы не сможем проверить, как
наша программа поведѐт себя в случае потери устройства. Я обнаружил, что потеря устройства всегда 19
происходит при переключении консольного приложения в полноэкранный режим при помощи клавиш ALT
+ Enter. Следовательно, для форсирования потери устройства вам необходимо:
1. Запустить приложение, использующее DirectX.
2. Запустить консольное приложение. Я предпочитаю пользовать FAR, но если у вас он не установлен,
   вполне подойдѐт и обычная командная строка (Start | All Programs | Accessories | Command
   Prompt)
3. Переключиться в полноэкранный режим и обратно в оконный (два раза нажать Alt + Enter).
Если вы проделаете эти операции над примером Ex03 из предыдущего раздел, то он аварийно завершится
из-за не перехваченного исключением Microsoft.Xna.Framework.Graphics.DeviceLostException,
название которого ясно говорит о причине краха приложения.
Для определения текущего состояния устройства в классе GraphicsDevice имеется свойство
GraphicsDeviceStatus:
public GraphicsDeviceStatus GraphicsDeviceStatus { get; }
Свойство возвращает перечислимый тип GraphicsDeviceStatus, который может принимать одно из
следующих значений:
 Normal – устройство функционирует нормально
 NotReset – устройство потеряно, но может быть восстановлено методом Reset                       класса
  GraphicsDevice.
 Lost – устройство потеряно и пока не может быть восстановлено.
Если устройство находится в состоянии NotReset, то его необходимо восстановить, вызвав метод
GraphicsDevice.Reset, после чего приложение может продолжать работу, как ни в чѐм не бывало. Если
же устройство находится в состояние Lost, то тут нечего не поделать – остаѐтся лишь выйти из обработчика
события Paint и ждать лучших времѐн.
Потеря устройства является очень коварной проблемой, которая она может произойти не только до вызова
обработчика Paint, но и внутри него. В этом случае XNA Framework в зависимости от ситуации
сгенерирует исключения Direct3D.DeviceNotResetException или Direct3D.DeviceLostException.
Если сгенерировано исключение Direct3D.DeviceNotResetException, то приложение должно
восстановить устройство методом GraphicsDevice.Reset() и снова перерисовать изображение путѐм
вызова метода Form.Invalidate(), в противном случае просто выйти из обработчика события Paint.
После всего вышесказанного мы сможем легко научить нашу программу корректно реагировать на потерю
устройства. Исходный код обновлѐнного обработчика события Paint приведѐн в листинге 1.8.

 Листинг 1.8.

// Полный текст приложения находится в каталоге Examples\Ch01\Ex05
private void MainForm_Paint(object sender, PaintEventArgs e)
{
    try
    {
// Если устройство нельзя восстановить, генерируем исключение DeviceLostException (мы его
// перехватим в блоке catch)

19
     Я не встречал ни одной видеокарты, на которой в этом случае не происходила бы потеря устройства.


         if (device.GraphicsDeviceStatus == GraphicsDeviceStatus.Lost)
             throw new DeviceLostException();

// Если устройство можно восстановить, восстанавливаем его
        if (device.GraphicsDeviceStatus == GraphicsDeviceStatus.NotReset)
            device.Reset();

// Рисуем шахматную доску

         device.Clear(XnaGraphics.Color.WhiteSmoke);

       Xna.Rectangle[] rects = new Xna.Rectangle[32];
       int k = 0;
       for (int j = 0; j < 8; j++)
           for (int i = j % 2; i < 8; i += 2)
           {
               rects[k] = new Xna.Rectangle(i * ClientSize.Width / 8,
 j * ClientSize.Height / 8, ClientSize.Width / 8, ClientSize.Height / 8);
               k++;
           }

         device.Clear(ClearOptions.Target, XnaGraphics.Color.Brown, 0.0f, 0, rects);

          device.Present();
      }
//   Если произошло исключение DeviceNotResetException, перерисовываем экран. Восстанавливать
//   устройство не имеет смысла – наш обработчик события Paint сделает это автоматически
      catch (DeviceNotResetException)
      {
          Invalidate();
      }
//   Если произошло исключение DeviceLostException, то нам остаѐтся только ждать до лучших
//   времѐн
      catch (DeviceLostException)
      {
      }
}
Практическое упражнение №1.2
“Обучите” программу, которую вы создали в упражнении №1.1, корректно реагировать на потерю
устройства.


Заключение
XNA Framework – это высокопроизводительная мультимедийная библиотека для разработки приложений,
требовательных к производительности видеоподсистемы и аудиосистемы компьютера, а так же подсистеме
ввода-вывода. Одной из причин появления XNA Framework являются некоторые трудности использования
Microsoft DirectX на платформе .NET. В отличие от компонентов “обычного” DirectX, сборки
Microsoft.Xna.Framework.dll и              Microsoft.Xna.Framework.Game.dll соответствуют всем
требованиям платформы .NET 2.0 и не привязаны к конкретному языку программирования. В результате эта
сборка с одинаковой лѐгкостью могут использоваться в любой .NET-совместимой среде программирования
вроде Microsoft Visual Basic, Microsoft Visual C# , Microsoft Visual J# или Iron Python. Кроме того, XNA
Framework является платформо-независимый библиотекой, что открывает дорогу для переноса приложений
на другие платформы, такие как, XBOX 360.
Все компоненты XNA Framework условно можно разделить на четыре уровня абстракции: Platform, Core
Framework, Extended Framework и Game. В этой главе мы познакомились с компонентом Graphics (один из
компонентов уровня Core Framework), предназначенным для работы с графической подсистемой
компьютера на относительно низком уровне. Плюсом подобного низкоуровневого подхода является высокая
производительность приложения, минусом – необходимость учѐта разнообразных нюансов вроде коррекции
размера вспомогательного буфера при изменении размера окна или и возможности неожиданной потери


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


Глава 2. Визуализация примитивов.
Процесс визуализации изображений в XNA Framework ощутимо отличаются от подхода, используемого в
“классических” двухмерных библиотеках вроде GDI/GDI+. В XNA Framework все графические построения
осуществляются с использованием простых фигур, называемых графическими примитивами. XNA
Framework поддерживает три вида графических примитивов: точки, отрезки и треугольники. Эти
примитивы очень просты в отображении, поэтому все современные графические ускорители могут рисовать
их аппаратно с очень высокой скоростью. Например, видеокарта NVIDIA GeForce 8800 GTX может
визуализировать порядка 400 миллионов треугольников в секунду 20 [С.7].
Каждый примитив задаѐтся набором вершин: точка – одной в центре точки, отрезок двумя вершинами на
концах отрезка, а треугольник – тремя вершинами в углах треугольника. В XNA Framework координаты
вершин обычно задаются тремя координатами x, y и z. Центр используемой системы координат расположен
в центре клиентской области формы, ось положительное направление оси X направлено вправо, ось Y –
вверх, а ось Z – из экрана монитора на наблюдателя (рисунок 2.1). Левый нижний угол формы имеет
координаты (-1, -1, 0), верхний правый (+1, +1, 0).

         (-1, 1, 0)                                                (1, 1, 0)
                                          +Y




                                          (0, 0, 0)          +X




        (-1, -1, 0)                                               (1, -1, 0)

Рисунок 2.1. Система координат клиентской области формы при использовании XNA Framework.


2.1. Работа с вершинами примитивов.
В пространстве имен Microsoft.Xna.Framework.Graphics имеется ряд структур для хранения информации о
вершинах примитива. В настоявшее время для нас наиболее интересна структура VertexPositionColor,
инкапсулирующая информацию о координатах и цвете вершины. Начнем рассмотрение этой структуры с
конструктора:
public VertexPositionColor(Vector3 position, Color color);
где
     position – координаты вершины;
     color – цвет вершины.
В процессе создания новой вершины ширина и высота автоматически заносятся конструктором в поля
Position и Color структуры VertexPositionColor:
public Color Color;
public Vector3 Position;
Здесь мы впервые встречаемся с новой для нас структурой Microsoft.XNA.Framework.Vector3,
инкапсулирующей трехмерный вектор. Наряду с Vector3 в XNA Framework определены структуры
Vector2 и Vector4, которые, как нетрудно догадаться, предназначены для работы с двухмерными и

20
  Приведена производительность в реальных задачах. Вообще производители обожают указывать в
описании видеокарты теоретическую пиковую производительность, практически не достижимую в
реальных приложениях. В целом, пиковая производительность видеокарты аналогична максимальной
скорости автомобилей или локомотивов. К примеру, электропоезд TGV-A теоретически можно разогнать до
513 км/ч [15], однако на практике его средняя скорость не превышает 350 км/ч.


четырехмерными векторами. Структуры Vector2, Vector3, Vector4 широко используются в XNA
Framework для хранения координат вершин, а так же выполнения различных математических векторных
операций. Так как большая часть функциональности данных структур нам пока не нужна, мы отложим их
подробное изучение до шестой главы. В конец концов, с точки зрения логики работы наших первых
приложений структуры Vector2, Vector3 и Vector4 представляют собой всего лишь расширенную версию
структуры System.Drawing.PointF.
Информация обо всех вершинах примитива хранится в массиве. Например:
// Вершин примитива
VertexPositionColor[] vertices;
Казалось бы, всѐ должно быть очень просто, если бы не один нюанс. Дело в том, что при визуализации
примитивов информация о вершинах напрямую передается в графический процессор видеокарты (GPU –
Graphics Processor Unit), который не имеет ни малейшего понятия об управляемом коде и, соответственно,
формате структуры. Для разъяснения графическому процессору формата отдельных полей структуры
применяются декларации формата вершины. В XNA Framework декларация вершины инкапсулируется
классом VertexDeclaration, конструктор которого приведен ниже:
public VertexDeclaration(GraphicsDevice graphicsDevice, VertexElement[] elements);
где
     graphicsDevice – графическое устройство, используемое для работы с вершинами
     elements – массив элементов (структур VertexElement) c описанием формата структуры.
Описание формата структуры задается массивом elements, каждый элемент которого описывает одно поле
структуры. Соответственно, количество элементов в массиве elements всегда равно количеству полей
структуры. Какая информация содержится в каждом элементе массива elements? Это:
4. Адрес описываемого поля структуры (смещение от начала структуры).
5. Тип поля структуры (скаляр, вектор, упакованный вектор 21).
6. Информация, содержащаяся в данном поле (координаты вершины, цвет вершины, текстурные
   координаты22 и т.п.).
7. Некоторая другая служебная информация.
Создание массива, описывающего структуру, является довольно монотонной и утомительной операцией. К
счастью, разработчики XNA Framework встроили в структуру VertexPositionColor (а так же во все
аналогичные структуры) статическое поле только для чтения, содержащее массив с описанием этой
структуры:
public static readonly VertexElement[] VertexElements;
Соответственно, для создания декларации вершины приложению достаточно лишь передать это поле в
качестве второго параметра конструктора класса VertexDeclaration.

2.2. Основы визуализации примитивов.
Рассмотрим основные этапы визуализации примитивов, информация о вершинах которых хранится в
массиве структур VertexPositionColor. Сначала приложение должно создать декларацию вершины на основе
описания, содержащегося в нашей структуре VertexTransformedPositionColor:
VertexDeclaration decl;
…
decl = new VertexDeclaration(device, VertexTransformedPositionColor.vertexElements);
Эту операцию достаточно выполнять один при запуске приложения, например, где-нибудь в обработчике
события Load формы.
Код визуализации примитива следует поместить в обработчик события Paint. Перед тем, как приступить к
визуализации примитивов, необходимо задать формат вершин примитива, присвоив свойству
VertexDeclaration класса Device декларацию формата вершины, созданную в обработчике события
Load.
Собственно визуализация примитивов выполняется методом DrawUserPrimitives:
DrawUserPrimitives<T>(PrimitiveType       primitiveType,    T[]   vertexData,   int   vertexOffset,   int
primitiveCount);

21
   Примером упакованного вектора является 32-х битное число, содержащее информацию о яркости
красного, синего и зеленого компонентов цвета.
22
   Текстурные координаты будут рассмотрены в разделе 5.X.


где
     primitiveType – тип примитива, задаваемый с использованием перечислимого типа PrimitiveType.
      Различные типы примитивов будут подробно рассмотрены в разделах 1.2.1, 1.2.2 и 1.2.3. Пока же
      отметим, что в XNA Framework поддерживает шесть типов примитивов: список точек
      (PrimitiveType.PointList),   список   линий   (PrimitiveType.LineList),       полоса    линий
      (PrimitiveType.LineStrip), список треугольников (PrimitiveType.TriangleList), полоса
      треугольников      (PrimitiveType.TriangleStrip)         и         веер         треугольников
      (PrimitiveType.TriangleFan).
     vertexData – массив вершин примитива.
     vertexOffset – смещение от начала массива. Данный параметр обычно равен нулю. Ненулевые значения
      применяется, когда визуализируемый примитив использует примитивы лишь из части массива
      (например, вершины разных примитивов хранятся в одном большом общем массиве).
     primitiveCount – количество примитивов, которые будут визуализированы.
Резюмируем всѐ вышесказанное. Для визуализации примитива приложение должно выполнить следующие
шаги:
      1.   Создать массив с информацией о вершинах примитива.
      2.   Создать декларацию формата вершины. В большинстве случаев первые два шага логичнее всего
           выполнять один раз при запуске приложения, например, в обработчике события Load.
      3.   В   обработчике   события     Paint   первым   делом   необходимо   очисть   экран   методом
           GraphicsDevice.Clear.
      4.   Поместить в массив координаты и цвет вершин (если координаты вершин не меняются в процессе
           выполнения приложения, эту операцию разумно будет вынести в метод Load).
      5.   Указать декларацию формата вершины, присвоив свойству GraphicsDevice. VertexDeclaration
           декларацию, созданную на втором этапе.
      6.   Нарисовать набор примитивов вызовом метода GraphicsDevice.DrawUserPrimitives.
      7.   Показать полученное изображение на экране, переключив буферы методом Device.Present.
Однако, это ещѐ не всѐ. Дело в том, что все современные GPU содержан специализированные векторные
процессоры, используемые для трансформации вершин и закраске примитивов. Так как эти процессоры
принимают участие при визуализации при любых примитивов, приложение должно запрограммировать их
на выполнения требуемых преобразований. Если этого не сделать, результат вызова метода
DrawUserPrimitives будет не предсказуемым23. Программирование вершинных и пиксельных процессоров
будет подробно рассмотрено в следующем разделе.

2.3. Введение в HLSL
В этой разделе мы познакомимся с языком High Level Shader Language (язык высокого уровня для
программирования шейдеров), или сокращѐнно HLSL. HLSL используется для программирования
вершинных и пиксельных процессоров графического ускорителя. Программа для вершинного процессора
называется вершинным шейдером, а для пиксельного процессора – пиксельным шейдером. Поддержка
шейдеров впервые появилась в 8-й версии DirectX. Правда шейдеры DirectX 8 имели множество
ограничений и программировались на низкоуровневом ассемблеро-подобном языке, однако в 9-й версии
DirectX возможности шейдеров значительно возросли, что привело появлению к подробности в языках
высокого уровня. Было создано несколько языков высокого уровня для написания шейдеров DirectX24,
однако стандартом де-факто стал язык HLSL, входящий в состав DirectX 9. В XNA Framework шейдеры так
же пишутся языке HLSL, а сам XNA Framework при работе с шейдерами на платформе Windows в
значительной степени опирается на функциональность DirectX.
Так язык HLSL тесно связан с архитектурой графического процессора, мы начнѐм этот раздел с знакомства с
основами архитектуры современного графического процессора.




23
  Вызов метода DrawUserPrimitives без явного задания вершинных и пиксельных шейдеров привет к
генерации исключения System.InvalidOperationException с сообщением Both a valid vertex
shader and pixel shader (or valid effect) must be set on the device before draw
operations may be performed.
24
     Например, NVIDIA Cg [К.16], [С.3]


2.3.1. Графический конвейер
В разделе 2.2 вы получили представлении о визуализации примитивов средствами XNA Framework. При
этом собственно процесс визуализации изображения (метод GraphicsDevice.DrawUserPrimitives)
оставался для нас чѐрным ящиком. Настало время наверстать упущенное. И так, при вызове метода
GraphicsDevice.DrawUserPrimitives вершины из графического буфера поступают на обработку в
графический конвейер XNA Framework, представляющий собой последовательность ступеней (простых
операций), выполняемых над вершинами в определѐнном порядке (рисунок 2.2). Рассмотрим эти ступени в
порядке выполнения:



                                                                                         Треугольники,
           Приложение                                  Вершины
                                                                                         отрезки, точки




                                             Массив вершинных процессоров
                                                                                         Растеризация
                                                  (вершинный шейдер)




                                                 Трансформированные
                                                                                       Массив пикселей
                                                      вершины




                                               Преобразование в оконные          Массив пиксельных процессоров
                                                     координтаты                      (пиксельный шейдер)




                                                 Трансформированные
                                                  вершины в оконных                 Постобработка пикселей
                                                      коордиатах




                                                  Сборка примитивов                     Кадровый буфер




Рисунок 2.2. Упрощенная схема графического конвейера

8. Вначале вершины обрабатываются вершинным процессором по программе, называемой вершинным
   шейдером. На выходе из вершинного процессора получаются так называемые трансформированные
   (преобразованные) вершины. К вершинам могут быть “привязаны” различные параметры: цвет вершины,
   текстурные координаты25 и так далее. Координаты трансформированных вершин задаются в логической
   системе однородных координат, называемой clip space. Однородные координаты вершины определяются
   четырьмя числами: (x, y, z, w). Перевод однородных координат в обычные геометрические
                                                                                                    x y z
      осуществляется путѐм деления первых трех компонентов на четвертый компонент w:               ( , , ).
                                                                                                    w w w
      Например, вершине с однородными координатами (1, 2, 3, 4) в трѐхмерном пространстве соответствует
                               1 2 3
      точка с координатами    ( , , )  (0.25,0.5,0.75) Использование четвертого компонента обусловлено
                               4 4 4
      рядом особенностей алгоритмов визуализации трехмерных изображений, используемых в 3D графике.
      При визуализации двухмерных изображений компонент w обычно полагают равным 1. В этом случае
      нижнему левому углу клиентской области формы соответствует точка с координатами (-1, -1, 0, 1),
      правому верхнему углу клиентской области – (1, 1, 0, 1), а центру клиентской области – соответственно
      (0, 0, 0, 1).

25
     Текстурные координаты будут рассмотрены в разделе 5.x.


9. На следующей ступени графического конвейера видеокарта производит преобразование координат
   вершины из логической системы координат в оконную. По-умолчанию координаты трансформируются
   таким образом, чтобы растянуть изображение на всю поверхность элемента управления. В большинстве
   случаев этот процесс полностью прозрачен для приложения.
      П р им еч а н ие
      DirectX позволяет программисту задавать координаты вершин в оконных координатах. В этом случае, при
      вызове метода Device.DrawUserPrimitives вершины сразу поступают на третью стадию графического конвейера,
      минуя первую и вторую стадии. Managed DirectX и XNA Framework Beta 1 позволяют задавать координаты в
      оконной системе координат, однако начиная с XNA Framework Beta 2 эта функциональность почему-то пропала.
      По видимости, это обусловлено стремлением сделать XNA Framework как можно более платформо-независимый.

10. Далее идѐт сборка примитивов. На этой стадии вершины объединяются в примитивы. Тип примитивов
    определяется первым параметром метода GraphicsDevice.DrawUserPrimitives. Так при
    использовании параметра PrimitiveType.TriangleStrip вершины трактуются, как опорные точки
    (вершины) полосы треугольников. При этом каждый треугольник из полосы является независимым
    примитивов и обрабатывается независимо от других треугольников этой полосы. Полосы треугольников
    подробно будут рассмотрены в разделе 2.6.3.
11. Затем происходит растеризация примитивов – преобразование каждого примитива в набор пикселей
    экрана. Параметры внутренних пикселей примитива (например, цвет) определяются путѐм интерполяции
    соответствующих параметров вершин вдоль поверхности примитива. Как мы увидим в следующих
    разделах, благодаря этой интерполяции при закраске треугольника с разноцветными вершинами
    образуются красивые цветовые переходы.
12. Следующий этап – обработка пикселей пиксельным процессором с использованием программы,
    называемой пиксельным шейдером. На вход пиксельному процессору подаются параметры пикселя
    (цвет, текстурные координаты и т.д.), полученные путѐм интерполяции соответствующих вершинных
    параметров вдоль поверхности примитива. После обработки входных параметров, пиксельный процессор
    возвращает цвет пикселя.
      Тех н ич е ск ие по др о бно ст и
      В современных графических процессорах имеется массив вершинных и пиксельных процессоров, что позволяет
      им одновременно обрабатывать несколько вершин и пикселей. Так графический процессор NV40 корпорации
      NVIDIA, используемый в видеокартах семейства GeForce 6800, имеет 6 вершинных и 16 пиксельных
      процессоров, соответственно, он может параллельно обрабатывать до 6-ми вершин и 16-ми пикселей. [С.8]

13. Полученные цвета пикселей заносятся в кадровый буфер. При этом возможно выполнение некоторой
    простой постобработки изображения вроде смешивания цветов при эффекте полупрозрачности.
В заключении стоит отметить, что этот логический конвейер DirectX не обязательно соответствует
физической организации видеокарты. К примеру, видеокарта NVIDIA GeForce 8800 GTX, основанная на
GPU G80, содержит 8 универсальных блоков, которые могут выполняться как вершинные, так и пиксельные
шейдеры [С.7].
После прочтения этого раздела у вас, возможно, сложились несколько сумбурные представления
графическом конвейере. Ничего страшного – в следующем разделе вы познакомитесь с языком HLSL и
напишете несколько шейдеров, после чего всѐ встанет на свои места.


Дополнительная информация

Все современные графические подсистемы построены по принципу конвейера. Идея конвейера, впервые
реализованная Генри Фордом, заключается в следующем: если сложный процесс разбить на
последовательность простых операций (конвейер), то на выходе конвейера мы получим производительность
равную производительности самой медленной операции в этой цепочке. В качестве примера конвейера
рассмотрим процесс производства популярных процессоров AMD Athlon . Создание одного процессора
занимает около двух месяцев. Соответственно, при классической организации производственного процесса
одно подразделение может выпускать около шести процессоров в год. Однако на современных фабриках
AMD производственный процесс разбивается на 400 стадий. В процессе производства каждый будущий
процессор проходит через все эти 400 стадий. Как только процессор проходит текущую стадию
производства, на его место приходит следующий. В итоге, на различных стадиях производства
одновременно может находиться до 400 процессоров. В итоге, на выходе конвейера получается
производительность труда порядка 400 процессоров в два месяца или 2400 процессоров в год. Иными
словами производительность труда вырастает примерно в 400 раз.
Стороннему наблюдателю может показаться, что за день один конвейер производит около 7-ми процессоров
(400/60). Но в реальности, между поступлением заготовки процессора и выходом готового процессора по-
прежнему проходит два месяца. Это явление получило название латентность конвейера. При нормальном


функционировании конвейера на это обстоятельство можно не обращать внимания; однако в случае
неполадок латентность конвейера не замедлит проявиться. Предположим, что была обнаружена и
исправлена очень опасная ошибка в архитектуре процессора, после чего исправленная версия процессора
немедленно поступила в производство. Но, не смотря на всю оперативность исправления ошибки, первые
исправленные образцы процессоров выйдут с конвейера лишь через два месяца. А ведь подобная задержка
может принести фирме заметные убытки…
Другое следствие латентности – низкая эффективность конвейера при производстве небольших партий
процессоров. К примеру, при производстве одного процессора темп производства будет равен 0.017
процессоров в день (один процессор за 60 дней), при производстве 28 процессоров – 0.44 процессора в день,
при 100 процессорах - уже 1.33 процессоров в день и т.д. Более-менее, нормальный темп будет достигнут
только при производстве партии из нескольких тысяч процессоров (рисунок 2.3).
К слову, графический конвейер не является исключением из правил. Он также малоэффективен при
визуализации небольшого количества примитивов. Поэтому для эффективного использования графического
конвейера программист должен стараться минимизировать количество вызов метода
GraphicsDevice.DrawUserPrimitives, визуализируя за один присест как можно больше примитивов.




Рисунок 2.3. Зависимость производительности конвейера от количества выпускаемых процессоров. Производительность оценивается
по числу процессоров, выпускаемых в среднем за сутки.


2.3.2. Язык HLSL
В начале XXI века корпорация 3dfx работала над революционным GPU Rampage, имеющим на борту массив
вершинных и пиксельных процессоров. Для программирования этих процессоров Microsoft в тесном
сотрудничестве с 3dfx разработала два похожих ассемблеро-подобных языка, которые были включены в
DirectX 8. Язык для программирования вершинных процессоров получил название Vertex Shader 1.0 (VS
1.0), а язык для программирования пиксельных процессоров – Pixel Shader 1.0 (PS 1.0) [С.4].
Соответственно, программы, написанные на этих языках, стали называться вершинными и пиксельными
шейдерами26. К сожалению, графический процессор Rampage так и не поступил в массовое производство по
финансовым причинам: компания 3dfx была объявлена банкротом и вскоре куплена NVIDIA, а проект
Rampage закрыт.
       П р им еч а н ие
       Если быть более точным, зачатки пиксельных шейдеров27 впервые появились в GPU NV10 (1999 год). Однако по
       ряду причин Microsoft не захотела включить поддержку этих шейдеров в DirectX. В результате, с точки зрения
       DirectX-программиста, в NV10 отсутствует какая-либо поддержка шейдеров. Единственная возможность
       задействовать шейдеры NV10 – воспользоваться API OpenGL [К.17], [К.18].




26
   Название шейдер (shader) обусловлено применением первых вершинных и пиксельных процессоров
преимущественно для более точной передачи игры света и тени (shade) на поверхности объектов.
27
   Эти шейдеры получили неофициальное обозначение Pixel Shader 0.5



    
Яндекс цитирования Яндекс.Метрика