Более совершенные технологии рендеринга в DirectX
Использование индексных буферов
Давайте еще раз рассмотрим геометрию куба, который мы рисовали в предыдущих лабораторных работах.
Прежде мы аппроксимировали каждую сторону куба двумя треугольниками, задавая координаты вершин каждого треугольника. Эти треугольника в вершинах куба имели одинаковые координаты и многократно повторялись при описании треугольников. Код был избыточным и достаточно большим.
Для устранения избыточности удобнее в вершинном буфере задавать только координаты уникальных точек, которыми являются вершины куба, а для каждого аппроксимирующего треугольника указывать три индекса из массива вершинного буфера. Получается, что мы храним уникальные координаты объекта "внавал" отдельно, а для треугольных примитивов указываем триадный список индексов этих координат в массиве вершинного буфера. Такой подход существенно сокращает количество хранимых данных и уменьшает необходимую память компьютера.
Массив с индексами опорных точек объекта, записанных в вершинном буфере, называется индексным буфером. Индексы, сохраняемые в таком буфере, могут быть 16- или 32-разрядными целыми числами. Давайте используем индексный буфер для моделирования вращающегося треугольника.
-
Добавьте
к текущему проекту форму Form1 из лабораторной работы №12.
Для этого временно переименуйте через
панель Solution
Explorer файл Form1.cs текущего
проекта и в меню Project (при выделенном
узле проекта в панели Solution Explorer ) выполните команду Add
Existing Item
-
После
копирования файла Form1.cs в текущий каталог
проекта присвойте ему через панель Solution Explorer имя Form2.cs,
а временно переименованному файлу верните первоначальное
имя Form1.cs
-
Откройте
файл Form2.cs в режиме кода и замените
в нем все вхождения Form1 на Form2,
вызвав в редакторе кода окно замены комбинацией клавиш Ctrl-H
-
Не
закрывая окна Find and Replace откройте
файл Form2.designer.cs и тоже замените
в нем все вхождения Form1 на Form2
-
Не
закрывая окна Find and Replace установите
в нем опцию Look in в значение Current Project и
замените все вхождения пространства имен RenderCube на RenderBest, выполнив команду Replace All
-
Проследите
за тем, чтобы в обеих файлах было использовано одно и
то же пространство имен, равное имени текущего проекта
( namespace RenderBest )
Теперь нужно установить форму Form2 стартовой для возможности ее начального запуска. Для этого:
-
В
панели Solution Explorer выделите узел проекта
и щелкните на пиктограмме Properties,
чтобы вызвать окно свойств проекта
-
Во
вкладке Application окна свойств проекта
установите опцию Startup object в значение RenderBest.Form2
-
Запустите
проект на выполнение, чтобы проверить начальную работоспособность Form2
На этом все подготовительные работы по форме Form2 закончены и можно приступать к построению куба с использованием индексного буфера.
-
Откройте
файл Form2.cs в режиме View Code и
через раскрывающийся список Members правой
верхней части окна редактора позиционируйтесь на функцию vb_Created() класса Form2
-
Введите
в тело функции код определения вершин куба (у нашего куба
8 вершин) в соответствии с рисунком
void vb_Created(object sender, EventArgs e)
{
// Определить внутреннюю ссылку на вершинный буфер
VertexBuffer buffer = (VertexBuffer)sender; // Явное приведение типов
// Создать локальный массив структур непреобразованных координат
CustomVertex.PositionColored[] verts =
new CustomVertex.PositionColored[8]; // Задать размерность массива на 8 вершин
// Задать параметры вершин куба в соответствии с рисунком
//
verts[0] = new CustomVertex.PositionColored(-1.0F, 1.0F, 1.0F, Color.Purple.ToArgb());
verts[1] = new CustomVertex.PositionColored(-1.0F, -1.0F, 1.0F, Color.Red.ToArgb());
verts[2] = new CustomVertex.PositionColored(1.0F, 1.0F, 1.0F, Color.Blue.ToArgb());
verts[3] = new CustomVertex.PositionColored(1.0F, -1.0F, 1.0F, Color.Yellow.ToArgb());
verts[4] = new CustomVertex.PositionColored(-1.0F, 1.0F, -1.0F, Color.Gold.ToArgb());
verts[5] = new CustomVertex.PositionColored(1.0F, 1.0F, -1.0F, Color.Green.ToArgb());
verts[6] = new CustomVertex.PositionColored(-1.0F, -1.0F, -1.0F, Color.Black.ToArgb());
verts[7] = new CustomVertex.PositionColored(1.0F, -1.0F, -1.0F, Color.WhiteSmoke.ToArgb());
// Заполнить вершинный буфер данными треугольников
buffer.SetData(verts, 0, LockFlags.None);
}
Листинг
13.10.
Тело функции vb_Created() класса Form2
Мы существенно уменьшили код создания вершинного буфера, оставив только координаты, необходимые для определения вершин куба. К тому же, для каждой вершины куба мы задали отдельный цвет.
-
Найдите
в функции InitializeGraphics() секцию
создания вершинного буфера и измените его размерность с 36 на 8 (у
нашего куба 8 вершин)
// Создать вершинный буфер
vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
8,
device,
Usage.Dynamic | Usage.WriteOnly,
CustomVertex.PositionColored.Format,
Pool.Default);
Листинг
13.11.
Новая размерность вершинного буфера в функции InitializeGraphics()
// Определение индексного массива вершин треугольных примитивов
private static readonly short[] indices = {
2,0,1,// Задняя грань: треугольник 2-0-1 (невидимая сторона - обход левый)
2,1,3,// Задняя грань: треугольник 2-1-3 (невидимая сторона - обход левый)
5,6,4,// Передняя грань: треугольник 5-6-4 (лицевая сторона - обход правый)
5,7,6,// Передняя грань: треугольник 5-7-6 (лицевая сторона - обход правый)
0,5,4,// Верхняя грань: треугольник 0-5-4 (лицевая сторона - обход правый)
0,2,5,// Верхняя грань: треугольник 0-2-5 (лицевая сторона - обход правый)
1,6,7,// Нижняя грань: треугольник 1-6-7 (невидимая сторона - обход левый)
1,7,3,// Нижняя грань: треугольник 1-7-3 (невидимая сторона - обход левый)
0,6,1,// Левая грань: треугольник 0-6-1 (невидимая сторона - обход левый)
0,4,6,// Левая грань: треугольник 0-4-6 (невидимая сторона - обход левый)
2,3,7,// Правая грань: треугольник 2-3-7 (лицевая сторона - обход правый)
2,7,5 // Правая грань: треугольник 2-7-5 (лицевая сторона - обход правый)
};
Листинг
13.12.
Определение индексного массива-поля класса Form2
-
В
классе Form2 объявите
поле типа Microsoft.DirectX.Direct3D. IndexBuffer с
именем ib для
хранения ссылки на индексный буфер. Ее удобнее разместить
рядом со ссылкой на вершинный буфер
private Device device = null;
private VertexBuffer vb = null;
private IndexBuffer ib = null;
public void InitializeGraphics()
{
.....................
}
Листинг
13.13.
Объявление ссылочной переменной на индексный буфер
Теперь необходимо создать сам индексный буфер, куда можно будет передать для хранения и использования индексы координат вершинного буфера. Индексный буфер имеет свойство сбрасываться также, как вершинный, поэтому необходимо предусмотреть обработчик восстановления индексного буфера для события Created, а также принудительный вызов этого обработчик при первом запуске формы.
-
Разместите
в самом конце функции InitializeGraphics() (после
кода создания вершинного буфера) код создания индексного
буфера и подписку на событие Created.
Подписку на событие Created выполните вручную, чтобы
оболочка правильно создала заготовку обработчика
public void InitializeGraphics()
{
// Создание объекта и настройка параметров представления
// Создать объект параметров представления
PresentParameters presentParams = new PresentParameters();
// Установить оконный режим
presentParams.Windowed = true;
// Сбрасывать содержимое буфера, если он не готов к представлению
presentParams.SwapEffect = SwapEffect.Discard;
// Создать объект устройства и сохранить ссылку на него
device = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, presentParams);
// Создать вершинный буфер
vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
8,
device,
Usage.Dynamic | Usage.WriteOnly,
CustomVertex.PositionColored.Format,
Pool.Default);
// Регистрация события Created вершинного буфера
vb.Created += new EventHandler(vb_Created);
// Принудительный вызов создания треугольника
// и заполнения вершинного буфера при первом
// создании формы
vb_Created(vb, null);
// Создать индексный буфер для координат вершин куба
ib = new IndexBuffer(typeof(short),
indices.Length,
device,
Usage.WriteOnly,
Pool.Default);
// Регистрация события Created индексного буфера
// Код написать вручную (для создания обработчика)!!!
ib.Created += new EventHandler(ib_Created);
// Принудительный вызов обработчика при первом запуске
ib_Created(ib, null);
}
Листинг
13.14.
Создание индексного буфера в функции InitializeGraphics()
-
Код
обработчик заполнения индексного буфера после сброса будет
таким
// Обработчик события повторного заполнения индексного буфера
void ib_Created(object sender, EventArgs e)
{
IndexBuffer buffer = (IndexBuffer)sender;
buffer.SetData(indices, 0, LockFlags.None);
}
Листинг
13.15.
Обработчик повторного заполнения индексного буфера после сброса
Теперь необходимо зарегистрировать индексный буфер в устройстве device, чтобы функция рисования объекта использовала его.
-
Установите
в функции SetupCamera() новую величину
приращения угла, определяющую меньшую скорость вращения
куба
if (flagRotate)
angle += 0.02F;
Листинг
13.16.
Приращение угла поворота куба в SetupCamera()
-
В
функции OnPaint() найдите секцию кода "Сформировать
сцену" и
внесите в нее регистрацию индексного буфера в устройстве
и вызов функции DrawIndexedPrimitives(),
которая при рисовании использует индексный буфер
// Сформировать сцену с учетом параметров
// "положение-нормаль-цвет"
device.BeginScene();
// Установить формат обработки вершин при отображении
device.VertexFormat = CustomVertex.PositionColored.Format;
// Нарисовать треугольник по данным из буфера
device.SetStreamSource(0, vb, 0);
device.Indices = ib;
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.EndScene();
Листинг
13.17.
Регистрация индексного буфера в устройстве
Функция рисования, которую мы использовали, имеет следующий синтаксис
public void DrawIndexedPrimitives( Microsoft.DirectX.Direct3D.PrimitiveType primitiveType, // Чем рисуем System.Int32 baseVertex, // Смещение от начало буфера до индекса первой вершины System.Int32 minVertexIndex, // Минимальный индекс вершины в текущем вызове System.Int32 numVertices, // Число вершин, используемых в текущем вызове System.Int32 startIndex, // Стартовый индекс для считывания и отображения System.Int32 primCount) // Число отображаемых примитивовЛистинг 13.18. Синтаксис функции рисования
Здесь мы не сможем сделать каждую отдельную грань одноцветной, поскольку цвет примитива берется из параметров вершины, а у нас заданы цвета вершин куба, к которым примыкают сразу несколько примитивов разных граней. Но на данный момент так даже красивее!!!






