Файлы к лабораторной работе Вы можете скачать здесь.
В этой работе мы рассмотрим следующее
Z -буферов) для более реалистичного
рендерингаДля имеются
следующие примитивы:
PointList -
отображает на экране объект в виде набора точек. Этот
тип можно использовать для рисования индексных примитивов.LineList - набор
линий, соединяющих пары вершин. При использовании этого
примитива нужно иметь для соединения четное число вершин.LineStrip -
набор ломаных прямых, соединяющих между собой последовательно
все вершины объекта. Должно быть минимум две вершины для
соединения с использованием этого примитива.TriangleList -
Отображает набор трех вершин как отдельный изолированный
треугольник. Прорисовка невидимой поверхности определяется
текущим состоянием рендера отбора (device.RenderState.CullMode
= TriangleStrip -
непрерывная треугольная ферма, когда каждый следующий треугольник
рисуется на базе двух вершин предыдущего треугольника и
одной новой вершины. По умолчанию отображаются все четные
треугольники.TriangleFan
- веер из треугольников, имеющих одну общую вершину.При рисовании можно использовать одни и те же данные о вершинах для любого из этих типов примитивов. Приложение будет интерпретировать при каждом рисовании тот тип примитива, который мы ему сообщаем. Испытаем на практике новые типы примитивов.
Постройте
новый пустой проект для C# с именем RenderBest
Добавьте
через меню Project командой Add
Reference, удерживая клавишу Ctrl, ссылки на
библиотечные сборкиSystem.dllSystem.Drawing.dllSystem.Windows.Forms.dllMicrosoft.DirectX .dllMicrosoft.DirectX .Direct3D .dll
Через
меню Project оболочки командой Add
Existing Item добавьте к проекту файл Form1.cs из
лабораторной работы №12
Отключите
консольное окно, вызвав окно свойств проекта через меню Project оболочки
и установив
во вкладке Application свойство Output type в
значение Windows
Application
Запустите
приложение, чтобы проверить, что вариант Form1 работы №12 функционирует
нормально
На этом подготовительный этап к выполнению данной лабораторной работы завершен.
В качестве основы для дальнейших изменений мы взяли
из лабораторной работы №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;
}
Удалите
из класса Form1 сначала подписку обработчика
на событие Click формы, затем обработчик Form1_Click(),
а также описание ненужного теперь флага flagRotatepublic 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;
}
Добавьте
в класс объявление поля-ссылки NumberItems на
количество вершин случайно генерируемого объекта, который
мы будем отображать с применением разных примитивовprivate Device device = null; // Графический конвейер
private VertexBuffer vb = null; // Вершинный буфер
private const int NumberItems = 12; // Количество вершин объекта
Мы выбрали количество вершин 12 потому, что это число четное,
делится на 3 (что подходит для примитивов LineList и TriangleList )
и не настолько большое, чтобы загромождать рисунок. Объявление
переменной как константы поручает компилятору следить за
тем, чтобы мы случайно где-нибудь в коде не попытались ее
изменить.
Измените
в функции InitializeGraphics() размерность вершинного буфера,
заменив 36 на NumberItemspublic void InitializeGraphics()
{
..................................
// Создать вершинный буфер
vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
NumberItems,
device,
Usage.Dynamic | Usage.WriteOnly,
CustomVertex.PositionColored.Format,
Pool.Default);
..................................
}
Теперь изменим алгоритм задания координат вершин и заполнения
вершинного буфера в функции 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; // Поле для генератора случайных чисел
Измените
код создания вершин объекта в функции 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);
}
Организовать просмотр работы различных примитивов можно последовательным их перебором с достаточной задержкой. Саму задержку можно измерять как разницу во временных тактах процессора между текущим временем и временем запуска приложения и на это время отображать объект одним и тем же примитивом. После завершения очередного цикла перебора всех примитивов необходимо сгенерировать новые параметры вершинного буфера, чтобы случайно изменить рисуемый объект.
Добавьте
в класс Form1 объявление
двух новых полей, которые для читабельности удобнее расположить
перед функцией OnPaint()// Флаг регенерации вершин перед началом
// каждого нового цикла обхода всех примитивов.
// Первое заполнение вершинного буфера выполнено
// в InitializeGraphics() при запуске приложения
private bool needRecreate = false;
// Захват стартового времени в тактах
private static readonly int beginTickCount =
System.Environment.TickCount;
protected override void OnPaint(PaintEventArgs e)
{
........................................
}
Переменная beginTickCount объявлена как
статическая для того, чтобы ее данные хранились в объекте-типе,
а не объекте-экземпляре, на случай, если будет создано несколько
экземпляров класса Form1. Ключевое слово делает
ее доступной "только для чтения". В отличие от константных
переменных, требующих немедленной инициализации при объявлении,
переменные "только
для чтения" допускают отложенную инициализацию в конструкторе
класса.
В
функции 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();
}
Добавьте
в конец функции 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;
}
Постройте
приложение и убедитесь, что форма Form1 периодически
генерирует новые объекты и отображает их разными примитивами
Давайте еще раз рассмотрим

Прежде мы аппроксимировали каждую сторону куба двумя треугольниками, задавая координаты вершин каждого треугольника. Эти треугольника в вершинах куба имели одинаковые координаты и многократно повторялись при описании треугольников. Код был избыточным и достаточно большим.
Для устранения избыточности удобнее в
вершинном буфере задавать
только координаты уникальных точек, которыми являются
вершины куба, а для
каждого аппроксимирующего треугольника указывать три индекса
из массива вершинного буфера.
Получается, что мы храним уникальные координаты объекта
"внавал" отдельно, а для треугольных примитивов
указываем триадный список индексов этих координат в массиве
вершинного буфера. Такой подход существенно сокращает количество
хранимых данных и уменьшает необходимую
Массив с индексами опорных точек объекта,
записанных в вершинном буфере, называется индексным
буфером. Индексы, сохраняемые в таком буфере,
могут быть 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
Введите
в 
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);
}
Мы существенно уменьшили код создания вершинного буфера, оставив только координаты, необходимые для определения вершин куба. К тому же, для каждой вершины куба мы задали отдельный цвет.
Найдите
в функции InitializeGraphics() секцию
создания вершинного буфера и измените его размерность с 36 на 8 (у
нашего куба 8 вершин)// Создать вершинный буфер
vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
8,
device,
Usage.Dynamic | Usage.WriteOnly,
CustomVertex.PositionColored.Format,
Pool.Default);
Добавьте
в класс Form2 поле для индексного
массива 16-short или Int16 )// Определение индексного массива вершин треугольных примитивов
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 (лицевая сторона - обход правый)
};
В
классе Form2 объявите
поле типа Microsoft.IndexBuffer с
именем ib для
хранения ссылки на индексный буфер. Ее удобнее разместить
рядом со ссылкой на вершинный буферprivate Device device = null;
private VertexBuffer vb = null;
private IndexBuffer ib = null;
public void InitializeGraphics()
{
.....................
}
Теперь необходимо создать сам индексный буфер,
куда можно будет передать для хранения и использования
индексы координат вершинного буфера.
Индексный буфер имеет свойство сбрасываться также, как
вершинный, поэтому необходимо предусмотреть обработчик
восстановления индексного
буфера для события 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);
}
Код
обработчик заполнения индексного буфера после сброса будет
таким// Обработчик события повторного заполнения индексного буфера
void ib_Created(object sender, EventArgs e)
{
IndexBuffer buffer = (IndexBuffer)sender;
buffer.SetData(indices, 0, LockFlags.None);
}
Теперь необходимо зарегистрировать индексный буфер в устройстве device, чтобы функция рисования объекта использовала его.
Установите
в функции SetupCamera() новую величину
if (flagRotate)
angle += 0.02F;
В
функции 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();
Функция рисования, которую мы использовали, имеет следующий синтаксис
public void DrawIndexedPrimitives( Microsoft.DirectX.Direct3D.PrimitiveType primitiveType, // Чем рисуем System.Int32 baseVertex, // Смещение от начало буфера до индекса первой вершины System.Int32 minVertexIndex, // Минимальный индекс вершины в текущем вызове System.Int32 numVertices, // Число вершин, используемых в текущем вызове System.Int32 startIndex, // Стартовый индекс для считывания и отображения System.Int32 primCount) // Число отображаемых примитивов
Постройте
приложение и получите вращающийся куб с красочными

Здесь мы не сможем сделать каждую отдельную грань одноцветной, поскольку цвет примитива берется из параметров вершины, а у нас заданы цвета вершин куба, к которым примыкают сразу несколько примитивов разных граней. Но на данный момент так даже красивее!!!
Буфер глубины ( ) применяется
в приложениях для хранения информации о глубине
отображаемого объекта. Эта информация используется в процессе
Через
контекстное меню панели Solution Explorer получите
копию файла Form2 и переименуйте
ее в Form3.cs
Через
контекстное меню панели Solution Explorer откройте
файл Form3.cs в режиме View Code
В
панели Solution Explorer двойным щелчком
на файле Form3.designer.cs откройте
его в рабочей области редактора оболочки
Строго проследите,
чтобы открытыми были только файлы Form3.cs Form3.designer.cs и
замените в них все вхождения Form2 на Form3 через
окно замены, которое можно вызвать комбинацией клавиш Ctrl-H.
Предварительно настройте опции окна
замены по приведенному снимку
Выполните
команду меню Project/RenderBest Properties и
установите форму Form3 стартовой
Запустите
проект на выполнение и убедитесь, что Form3 работает
в прежнем варианте Form2, т.е. целостность
кода сохранилась и мы можем продолжать развитие проектаВначале вместо одного куба создадим 9 кубов и для правильного их отображения отодвинем камеру.
Добавьте
в класс Form3 новую функцию DrawCubes() для
рисования 9 кубовprivate void DrawCubes()
{
//
// Средний ряд
//
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 4.0f) *
Matrix.Translation(0.0f, 0.0f, 0.0f); // По центру
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI / 2.0f,
angle / (float)Math.PI * 4.0f) *
Matrix.Translation(4.0f, 0.0f, 0.0f); // Сдвиг вправо
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 4.0f,
angle / (float)Math.PI / 2.0f) *
Matrix.Translation(-4.0f, 0.0f, 0.0f); // Сдвиг влево
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
//
// Нижний ряд
//
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 4.0f) *
Matrix.Translation(0.0f, -4.0f, 0.0f); // По центру
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI / 2.0f,
angle / (float)Math.PI * 4.0f) *
Matrix.Translation(4.0f, -4.0f, 0.0f); // Сдвиг вправо
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 4.0f,
angle / (float)Math.PI / 2.0f) *
Matrix.Translation(-4.0f, -4.0f, 0.0f); // Сдвиг влево
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
//
// Верхний ряд
//
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 4.0f) *
Matrix.Translation(0.0f, 4.0f, 0.0f); // По центру
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI / 2.0f,
angle / (float)Math.PI * 4.0f) *
Matrix.Translation(4.0f, 4.0f, 0.0f); // Сдвиг вправо
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 4.0f,
angle / (float)Math.PI / 2.0f) *
Matrix.Translation(-4.0f, 4.0f, 0.0f); // Сдвиг влево
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
if (flagRotate)
angle += 0.02F;
}
Удалите
из функции SetupCamera() две последних
секции, ответственные за вращение куба, поскольку эту функциональность
мы включили в новую функцию DrawCubes()// Установка камеры в сцену
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.02F;
}
Замените
в секции формирования сцены функции OnPaint() вызов функции DrawIndexedPrimitives() на вызов функции DrawCubes()// Сформировать сцену с учетом параметров
// "положение-нормаль-цвет"
device.BeginScene();
// Установить формат обработки вершин при отображении
device.VertexFormat = CustomVertex.PositionColored.Format;
// Нарисовать треугольник по данным из буфера
device.SetStreamSource(0, vb, 0);
device.Indices = ib;
DrawCubes();
device.EndScene();
В
функции SetupCamera() отодвиньте камеру, чтобы была видна
вся сцена с 9 кубами// Установка камеры в сцену
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, 15.0F), // Положение камеры
new Vector3(), // Положение объекта текущее
new Vector3(0, 1, 0)); // Направление камеры
// Освещение
device.RenderState.Lighting = false;
}
Запустите
приложение и получите примерно следующее
Настало время перейти к использованию буферов глубины объектов. Давайте добавим еще три куба к нашей сцене вращающихся кубов.
Добавьте в конец функции DrawCubes() код для рисования еще трех кубовprivate void DrawCubes()
{
//
// Средний ряд
//
//.................................................
//
// Дополнительные кубы
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 4.0f) *
Matrix.Translation(
0.0f,
(float)Math.Cos(angle),
(float)Math.Sin(angle));
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI / 2.0f,
angle / (float)Math.PI * 4.0f) *
Matrix.Translation(
4.0f,
(float)Math.Sin(angle),
(float)Math.Cos(angle));
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 2.0f) *
Matrix.Translation(
-4.0f,
(float)Math.Cos(angle),
(float)Math.Sin(angle));
device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
if (flagRotate)
angle += 0.02F;
}
Запустите
приложение и увидите по горизонтальному среднему ряду еще
дополнительных три кубаЗдесь мы хотим получить вращение дополнительных кубов вокруг
трех центральных кубов, расположенных по горизонтали. Чтобы
этого достичь, следует добавить буфер глубины в устройство device.
Существуют разные форматы буферов глубины, но большинство
современных графических плат поддерживают 16-
Добавьте
в функцию InitializeGraphics() перед кодом создания устройства
параметры настройки буфера глубиныpublic void InitializeGraphics()
{
// Создание объекта и настройка параметров представления
// Создать объект параметров представления
PresentParameters presentParams = new PresentParameters();
// Установить оконный режим
presentParams.Windowed = true;
// Сбрасывать содержимое буфера, если он не готов к представлению
presentParams.SwapEffect = SwapEffect.Discard;
// Настройка буфера глубины для параметров устройства
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat =
Microsoft.DirectX.Direct3D.DepthFormat.D16;
// Создать объект устройства и сохранить ссылку на него
device = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, presentParams);
// Создать вершинный буфер
// .................................................
}
Запускаем
приложение и видим, что вместо кубов появились их осколкиИсчезновение сцены после добавления параметров буфера глубины произошло потому, что этот буфер не был очищен
Добавьте
к первой секции кода функции OnPaint() дополнительный
флаг очистки буфера глубиныprotected override void OnPaint(PaintEventArgs e)
{
// Очистить цветом клиентскую область формы
device.Clear(ClearFlags.Target
| ClearFlags.ZBuffer,
System.Drawing.Color.CornflowerBlue,
1.0F, 0);
..........................................
}
Запустите
приложение и мы увидим фантастическую картину, иллюстрирующую
использование буфера глубины
В данной лабораторной работе мы познакомились с более совершенными методами рендеринга, а именно
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.