Опубликован: 28.04.2009 | Доступ: свободный | Студентов: 1736 / 79 | Оценка: 4.36 / 4.40 | Длительность: 16:40:00
Специальности: Программист
Лекция 2:

Визуализация примитивов

Аннотация: Данная лекция рассматривает визуализацию примитивов. Приводится понятие примитива и его основные свойства, а также методы использования эффектов в XNA Framework. Рассматриваются простые задачи, реализованные с помощью треугольников и отрезков с применением эффектов.

Процесс визуализации изображений в XNA Framework ощутимо отличаются от подхода, используемого в "классических" двухмерных библиотеках вроде GDI/GDI+. В XNA Framework все графические построения осуществляются с использованием простых фигур, называемых графическими примитивами. XNA Framework поддерживает три вида графических примитивов: точки, отрезки и треугольники. Эти примитивы очень просты в отображении, поэтому все современные графические ускорители могут рисовать их аппаратно с очень высокой скоростью. Например, видеокарта NVIDIA GeForce 8800 GTX может визуализировать порядка 400 миллионов треугольников в секунду1Приведена производительность в реальных задачах. Вообще производители обожают указывать в описании видеокарты теоретическую пиковую производительность, практически не достижимую в реальных приложениях. В целом, пиковая производительность видеокарты аналогична максимальной скорости автомобилей или локомотивов. К примеру, электропоезд TGV-A теоретически можно разогнать до 513 км/ч , однако на практике его средняя скорость не превышает 350 км/ч..

Каждый примитив задается набором вершин: точка – одной в центре точки, отрезок двумя – вершинами на концах отрезка, а треугольник – тремя вершинами в углах треугольника. В XNA Framework координаты вершин обычно задаются тремя координатами x, y и z. Центр используемой системы координат расположен в центре клиентской области формы, ось положительное направление оси X направлено вправо, ось Y – вверх, а ось Z – из экрана монитора на наблюдателя (рисунок 2.1). Левый нижний угол формы имеет координаты (-1, -1, 0), верхний правый (+1, +1, 0).

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

Рис. 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, которые, как нетрудно догадаться, предназначены для работы с двухмерными и четырехмерными векторами. Структуры 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? Это:

  1. Адрес описываемого поля структуры (смещение от начала структуры).
  2. Тип поля структуры (скаляр, вектор, упакованный вектор2Примером упакованного вектора является 32-х битное число, содержащее информацию о яркости красного, синего и зеленого компонентов цвета. ).
  3. Информация, содержащаяся в данном поле (координаты вершины, цвет вершины, текстурные координаты3Текстурные координаты будут рассмотрены в разделе 5.X и т.п.).
  4. Некоторая другая служебная информация.

Создание массива, описывающего структуру, является довольно монотонной и утомительной операцией. К счастью, разработчики 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);

где

  • 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 - количество примитивов, которые будут визуализированы.

Резюмируем все вышесказанное. Для визуализации примитива приложение должно выполнить следующие шаги:

  • Создать массив с информацией о вершинах примитива.
  • Создать декларацию формата вершины. В большинстве случаев первые два шага логичнее всего выполнять один раз при запуске приложения, например, в обработчике события Load.
  • В обработчике события Paint первым делом необходимо очисть экран методом
    • GraphicsDevice.Clear.
  • Поместить в массив координаты и цвет вершин (если координаты вершин не меняются в процессе выполнения приложения, эту операцию разумно будет вынести в метод Load ).
  • Указать декларацию формата вершины, присвоив свойству GraphicsDevice. VertexDeclaration декларацию, созданную на втором этапе.
  • Нарисовать набор примитивов вызовом метода GraphicsDevice.DrawUserPrimitives.
  • Показать полученное изображение на экране, переключив буферы методом Device.Present.

Однако, это еще не все. Дело в том, что все современные GPU содержан специализированные векторные процессоры, используемые для трансформации вершин и закраски примитивов. Так как эти процессоры принимают участие при визуализации любых примитивов, приложение должно запрограммировать их на выполнение требуемых преобразований. Если этого не сделать, результат вызова метода DrawUserPrimitives будет не предсказуемым4Вызов метода 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 . Программирование вершинных и пиксельных процессоров будет подробно рассмотрено в следующем разделе.

Андрей Леонов
Андрей Леонов

Reference = add reference, в висуал студия 2010 не могу найти в вкладке Solution Explorer, Microsoft.Xna.Framework. Его нету.

Олег Корсак
Олег Корсак
Латвия, Рига
Александр Петухов
Александр Петухов
Россия, Екатеринбург