Более совершенные технологии рендеринга в DirectX
Рендеринг различных типов примитивов
В качестве основы для дальнейших изменений мы взяли из лабораторной работы №12 код построения одного куба без текстурирования сторон, размещенный в файле Form1.cs. Мы уже присоединили этот файл к новому проекту (оболочка физически скопировала этот файл в каталог размещения текущего проекта). Теперь мы усовершенствуем файл Form1.cs, чтобы продемонстрировать отображение одного и того же набора вершин с помощью различных примитивов. Испытания примитивов не предполагают вращения рисуемых объектов, поэтому в функции SetupCamera() необходимо удалить вращение вершин и угол поворота.
-
В
функции SetupCamera() удалите две последних
секции кода, отвечающих за повороты объекта и приращение
угла поворота, а также объявление самой
переменной-поля angle
private float angle = 0;// Закрытый член класса
// Установка камеры в сцену
private void SetupCamera()
{
//Создание перспективы
device.Transform.Projection = Matrix.PerspectiveFovLH(
(float)Math.PI / 4, // Угол зрения равен 45 градусов
// Форматное соотношение сторон
(float)this.ClientSize.Width / (float)this.ClientSize.Height,
1.0F, // Ближний план
100.0F); // Дальний план
//Добавление камеры
device.Transform.View = Matrix.LookAtLH(
new Vector3(0, 0, 5.0F), // Положение камеры
new Vector3(), // Положение объекта текущее
new Vector3(0, 1, 0)); // Направление камеры
// Освещение
device.RenderState.Lighting = false;
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 2.0F,
angle / (float)Math.PI);
if (flagRotate)
angle += 0.1F;
}
Листинг
13.1.
Удаления вращений в коде функции SetupCamera()
-
Удалите
из класса Form1 сначала подписку обработчика
на событие Click формы, затем обработчик Form1_Click(),
а также описание ненужного теперь флага flagRotate
public Form1()
{
InitializeComponent();
// Устраняем мерцание окна при бесконечной перерисовке
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.Opaque, true);
this.Click += new EventHandler(Form1_Click);
}
bool flagRotate = true;
void Form1_Click(object sender, EventArgs e)
{
flagRotate = !flagRotate;
}
Листинг
13.2.
Удаление обработчика остановки вращения и его регистрации
-
Добавьте
в класс объявление поля-ссылки NumberItems на
количество вершин случайно генерируемого объекта, который
мы будем отображать с применением разных примитивов
private Device device = null; // Графический конвейер
private VertexBuffer vb = null; // Вершинный буфер
private const int NumberItems = 12; // Количество вершин объекта
Листинг
13.3.
Константная переменная класса Form1 для задания количества вершин
Мы выбрали количество вершин 12 потому, что это число четное, делится на 3 (что подходит для примитивов LineList и TriangleList ) и не настолько большое, чтобы загромождать рисунок. Объявление переменной как константы поручает компилятору следить за тем, чтобы мы случайно где-нибудь в коде не попытались ее изменить.
-
Измените
в функции InitializeGraphics() размерность вершинного буфера,
заменив 36 на NumberItems
public void InitializeGraphics()
{
..................................
// Создать вершинный буфер
vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
NumberItems,
device,
Usage.Dynamic | Usage.WriteOnly,
CustomVertex.PositionColored.Format,
Pool.Default);
..................................
}
Листинг
13.4.
Новая размерность вершинного буфера в функции InitializeGraphics()
Теперь изменим алгоритм задания координат вершин и заполнения вершинного буфера в функции vb_Created(). Мы применим средства случайного формирования координат вершин и их цветов.
-
Объявите
в составе класса Form1 ссылочную переменную-поле
на генератор случайных чисел по равномерному закону распределения,
а в конструкторе Form1() разместите код
создания и инициализации этого генератора случайных чисел
public Form1()
{
InitializeComponent();
// Устраняем мерцание окна при бесконечной перерисовке
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.Opaque, true);
// Инициализация генератора случайных чисел
rand = new System.Random();
}
private Device device = null; // Графический конвейер
private VertexBuffer vb = null; // Вершинный буфер
private const int NumberItems = 12; // Количество вершин объекта
System.Random rand; // Поле для генератора случайных чисел
Листинг
13.5.
Добавление генератора случайных чисел в файле Form1.cs
-
Измените
код создания вершин объекта в функции vb_Created()
void vb_Created(object sender, EventArgs e)
{
// Определить внутреннюю ссылку на вершинный буфер
VertexBuffer buffer = (VertexBuffer)sender; // Явное приведение типов
// Создать локальный массив ссылок для вершин объекта
CustomVertex.PositionColored[] verts = new
CustomVertex.PositionColored[NumberItems];
// Сгенерировать случайно координаты и цвета вершин
double factor = 2.5d;// Весовой коэффициент
for (int i = 0; i < NumberItems; i++)
{
float xPos = (float)((rand.NextDouble() - rand.NextDouble()) * factor);
float yPos = (float)((rand.NextDouble() - rand.NextDouble()) * factor);
float zPos = (float)((rand.NextDouble() - rand.NextDouble()) * factor);
verts[i].SetPosition(new Vector3(xPos, yPos, zPos));
verts[i].Color = System.Drawing.Color.FromArgb(rand.Next(256),
rand.Next(256),
rand.Next(256)).ToArgb();
}
// Заполнить вершинный буфер данными треугольников
buffer.SetData(verts, 0, LockFlags.None);
}
Листинг
13.6.
Создание вершин со случайными параметрами в функции vb_Created()
Организовать просмотр работы различных примитивов можно последовательным их перебором с достаточной задержкой. Саму задержку можно измерять как разницу во временных тактах процессора между текущим временем и временем запуска приложения и на это время отображать объект одним и тем же примитивом. После завершения очередного цикла перебора всех примитивов необходимо сгенерировать новые параметры вершинного буфера, чтобы случайно изменить рисуемый объект.
-
Добавьте
в класс Form1 объявление
двух новых полей, которые для читабельности удобнее расположить
перед функцией OnPaint()
// Флаг регенерации вершин перед началом
// каждого нового цикла обхода всех примитивов.
// Первое заполнение вершинного буфера выполнено
// в InitializeGraphics() при запуске приложения
private bool needRecreate = false;
// Захват стартового времени в тактах
private static readonly int beginTickCount =
System.Environment.TickCount;
protected override void OnPaint(PaintEventArgs e)
{
........................................
}
Листинг
13.7.
Добавление новых полей в класс Form1
Переменная beginTickCount объявлена как статическая для того, чтобы ее данные хранились в объекте-типе, а не объекте-экземпляре, на случай, если будет создано несколько экземпляров класса Form1. Ключевое слово readonly делает ее доступной "только для чтения". В отличие от константных переменных, требующих немедленной инициализации при объявлении, переменные "только для чтения" допускают отложенную инициализацию в конструкторе класса.
-
В
функции OnPaint() замените вызов библиотечной
функции DrawPrimitives() в
секции кода формирования сцены и цвет фона формы выделенным
ниже блоком кода, чтобы окончательно функция выглядела
так
// Флаг регенерации вершин перед началом
// каждого нового цикла обхода всех примитивов.
// Первое заполнение вершинного буфера выполнено
// в InitializeGraphics() при запуске приложения
private bool needRecreate = false;
// Захват стартового времени в тактах
private static readonly int beginTickCount =
System.Environment.TickCount;
protected override void OnPaint(PaintEventArgs e)
{
// Очистить цветом клиентскую область формы
device.Clear(ClearFlags.Target,
System.Drawing.Color.White,
1.0F, 0);
// Вызов нашей функции установки камеры
SetupCamera();
// Сформировать сцену с учетом параметров
// "положение-нормаль-цвет"
device.BeginScene();
// Установить формат обработки вершин при отображении
device.VertexFormat = CustomVertex.PositionColored.Format;
// Нарисовать треугольник по данным из буфера
device.SetStreamSource(0, vb, 0);
const int countTime = 5 * 1000; // Держать изображение 5 секунд
const int countIndex = 6; // Количество вариантов показа
int index = ((System.Environment.TickCount - beginTickCount) / countTime)
% countIndex;
switch (index)
{
case 0: // PointList
device.DrawPrimitives(PrimitiveType.PointList, 0, NumberItems);
if (needRecreate)// Выполнить только при очередном index == 0
{
vb_Created(vb, null);// Сгенерировать новый вершинный буфер
needRecreate = false;// Сбросить флаг регенерации вершин
}
this.Text = "Несоединенные точки вершин";
break;
case 1: // LineList
device.DrawPrimitives(PrimitiveType.LineList, 0, NumberItems / 2);
needRecreate = true;// Приготовить флаг для регенерации вершин при index == 0
this.Text = "Соединие пар вершин линиями";
break;
case 2: // LineStrip
device.DrawPrimitives(PrimitiveType.LineStrip, 0, NumberItems - 1);
this.Text = "Соединение вершин ломаной";
break;
case 3: // TriangleList
device.DrawPrimitives(PrimitiveType.TriangleList, 0, NumberItems / 3);
this.Text = "Отдельные треугольники";
break;
case 4: // TriangleStrip
device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, NumberItems - 2);
this.Text = "Непрерывная ферма треугольников";
break;
case 5: // TriangleFan
device.DrawPrimitives(PrimitiveType.TriangleFan, 0, NumberItems - 2);
this.Text = "Треугольный веер из точки";
break;
}
device.EndScene();
// Показать буфер кадра
device.Present();
// Принудительно перерисовать
this.Invalidate();
}
Листинг
13.8.
Рендеринг различных типов примитивов в функции OnPaint()
-
Добавьте
в конец функции SetupCamera() код изменения
состояния рендера для увеличения размера отображаемых точек
вершин, чтобы их можно было различить в нулевом режиме PointList
// Установка камеры в сцену
private void SetupCamera()
{
//Создание перспективы
device.Transform.Projection = Matrix.PerspectiveFovLH(
(float)Math.PI / 4, // Угол зрения равен 45 градусов
// Форматное соотношение сторон
(float)this.ClientSize.Width / (float)this.ClientSize.Height,
1.0F, // Ближний план
100.0F); // Дальний план
//Добавление камеры
device.Transform.View = Matrix.LookAtLH(
new Vector3(0, 0, 5.0F), // Положение камеры
new Vector3(), // Положение объекта текущее
new Vector3(0, 1, 0)); // Направление камеры
// Освещение
device.RenderState.Lighting = false;
// Увеличение масштаба отображения точек вершин объекта
device.RenderState.PointSize = 5.0f;
}
Листинг
13.9.
Увеличение масштаба отображения точек в функции SetupCamera()
-
Постройте
приложение и убедитесь, что форма Form1 периодически
генерирует новые объекты и отображает их разными примитивами
