+ All Categories
Home > Documents > Создание трехмерного графического приложения на...

Создание трехмерного графического приложения на...

Date post: 08-Dec-2016
Category:
Upload: dohanh
View: 218 times
Download: 2 times
Share this document with a friend
19
DirectX 9.0 Managed API 1 День технологий Microsoft на факультете ВМиК МГУ Лабораторная работа Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX 9.0
Transcript
Page 1: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

1

День технологий Microsoft на факультете ВМиК МГУ

Лабораторная работа

Создание трехмерного графического приложения на платформе .NET

с применением технологии Microsoft DirectX 9.0

Page 2: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

2

Шаг 1. Создание оконного приложения. 1.1. Запустите интегрированную среду разработки Microsoft Visual Studio .NET 2003. Выберите пункт меню File/New/New Project.

1.2. В появившемся диалоговом окне в списке Project Types выберите пункт Visual C# Projects, в обновившемся списке Templates выберите пункт Windows Application. В поле ввода Name введите название проекта (например, DXApp), в поле ввода Location укажите путь к каталогу проекта (например, С:\). Галочку напротив строки Create directory for new solution можно убрать.

После нажатия кнопки OK будет создан проект для оконного приложения. Для сборки проекта выберите пункт главного меню Build/Build Solution или нажмите Ctrl+Shift+B.

Page 3: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

3

Сборка должна пройти без ошибок. Для выполнения программы выберите пункт меню Debug/Start Without Debugging или нажмите Ctrl+F5.

На экране появится окно вашего приложения.

Шаг 2. Настройка свойств окна. В окне Solution Explorer отображается список файлов проекта. Исходный текст на языке C#, относящийся к главному окну приложения находится в файле Form1.cs. Нажатие над этим файлом правой кнопки мыши приводит к появлению контекстного меню, в котором присутствуют пункты View Code и View Designer, определяющие отображение в окне редактора или исходного текста или внешнего вида окна.

Page 4: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

4

Нажмите правую кнопку мыши в свободной области редактора внешнего вида окна и в появившемся контекстном меню выберите пункт Properties.

На экране появится окно редактора свойств формы. Убедитесь, что третья слева кнопка, отображающая список свойств, нажата.

В свойствах окна (формы) измените свойство Text, задающее заголовок окна, (например, на “Приложение Direct3D”). В свойстве Icon укажите путь к файлу пиктограммы ($LabFiles$\directx.ico). Теперь окно приложения должно выглядеть следующим образом.

Шаг 3. Создание устройства Direct3D для работы с трехмерной графикой. 3.1. Для начала необходимо подключить к проекту сборки DirectX.dll, Direct3D.dll и Direct3DX.dll, содержащие managed классы для работы с DirectX 9.0. Для этого в окне Solution Explorer нажмите правую кнопку мыши над элементом References и выберите в появившемся меню пункт Add Reference.

Page 5: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

5

В появившемся окне при помощи кнопки Browse добавьте в проект перечисленные выше сборки из каталога $LabFiles$\DirectX Assemblies.

3.2. Устройство Direct3D удобно создавать при первом показе окна на экране. Создайте обработчик события Load. Для этого в окне свойств окна (формы) выберите список событий. Для этого нажмите кнопку с изображением молнии. В списке событий дважды нажмите левую кнопку мыши на событие Load.

Будет создан метод с именем Form1_Load, код которого сразу же отобразится в редакторе. До этого момента весь код создавала за нас Visual Studio, теперь код придется писать самим ☺. Значительную помощь в наборе длинных идентификаторов оказывает технология Intellisense, предлагающая выбор методов, свойств или полей данных объекта после набора символа “точка”

Page 6: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

6

3.3. В самом начале исходного текста добавьте две директивы using. Это позволит обращаться к классам DirectX не указывая каждый раз длинных префиксов пространств имен. Здесь и далее жирным шрифтом выделен добавляемый исходный текст. Комментарии можно пропускать ☺. … using System.Data; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; … 3.4. В классе Form1 добавьте поле данных типа Microsoft.Direct3D.Device для хранения ссылки на устройство Direct3D. public class Form1 : System.Windows.Forms.Form { … Device d3d = null; // Устройство для отображения 3D-графики … } 3.5. В метод Form1_Load добавьте код для инициализации устройства Direct3D. Для обработки возможных ошибок код инициализации поместим в блок try/catch (этот фрагмент кода можно взять в файле $LabFiles$\sources.cs). private void Form1_Load(object sender, System.EventArgs e) { try { // Устанавливаем режим отображения трехмерной графики PresentParameters d3dpp = new PresentParameters(); d3dpp.BackBufferCount = 1; d3dpp.SwapEffect = SwapEffect.Discard; d3dpp.Windowed = true; // Выводим графику в окно d3dpp.MultiSample = MultiSampleType.None; // Выключаем антиалиасинг d3dpp.EnableAutoDepthStencil = true; // Разрешаем создание z-буфера d3dpp.AutoDepthStencilFormat = DepthFormat.D16; // Z-буфер в 16 бит d3d = new Device(0, // D3D_ADAPTER_DEFAULT - видеоадаптер по умолчанию DeviceType.Hardware, // Тип устройства - аппаратный ускоритель this, // Окно для вывода графики CreateFlags.SoftwareVertexProcessing, // Геометрию обрабатывает CPU d3dpp); } catch(Exception exc) { MessageBox.Show(this,exc.Message,"Ошибка инициализации"); Close(); // Закрываем окно } } 3.6. Хорошим тоном в программировании считается своевременное освобождение всех ранее полученных ресурсов. Освобождение памяти берет на себя автоматический сборщик мусора, а освобождение ресурсов Direct3D надо указать явно. Для этого добавьте следующий код в уже существующий виртуальный метод Dispose(bool disposing) класса Form1: protected override void Dispose( bool disposing ) { if(disposing) { if(components != null) { components.Dispose(); }

Page 7: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

7

// Освобождаем занятые ранее ресурсы if(d3d != null) d3d.Dispose(); } base.Dispose(disposing); } Теперь проект можно собрать и запустить. Внешний вид окна не изменится, но рисование трехмерных объектов в окне можно будет выполнять, вызывая методы объекта d3d. Шаг 4. Добавление кода для рисования. 4.1. В окне свойств выберите список событий класса Form1 и добавьте обработчик события Paint точно таким же образом как и обработчик события Load.

4.2. В метод Form1_Paint добавьте код, очищающий буфер глубины, заполняющий дублирующий буфер темно зеленым цветом и показывающий его на экран. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { // Очищаем буфер глубины и дублирующий буфер d3d.Clear(ClearFlags.Target|ClearFlags.ZBuffer,Color.Green,1.0f,0); //.Показываем содержимое дублирующего буфера d3d.Present(); } После выполнения шага 4 окно приложение должно выглядеть следующим образом.

Шаг 5. Рисование трехмерного объекта. 5.1. В конструкторе класса Form1 установите режим перерисовки содержимого окна при любом изменении размера

Page 8: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

8

public Form1() { … InitializeComponent(); SetStyle(ControlStyles.ResizeRedraw,true); … } 5.2. В класс Form1 добавьте поле данных teapot типа Mesh для хранения ссылки на полигональный объект. сlass Form1 { … Mesh teapot = null; // Модель чайника … } 5.3. В метод Form1_Load добавьте код, создающий полигональную модель чайника. private void Form1_Load(object sender, System.EventArgs e) … d3d = new Device(…); // Создаем модель чайника и задаем ее свойства teapot = Mesh.Teapot(d3d); … } 5.4. В метод Dispose добавьте код, освобождающий занятые для полигональной модели ресурсы Direct3D. protected override void Dispose(bool disposing) { … if(teapot != null) teapot.Dispose(); if(d3d != null) d3d.Dispose(); … } 5.5. В метод Form1_Paint добавьте код, отображающий чайник на экране. Для этого необходимо задать преобразование для перспективного проецирования координат из системы координат наблюдателя на экран, задать преобразование из системы координат объекта (чайника) в систему координат наблюдателя и вызвать метод для отрисовки полигональной модели на экране. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { d3d.Clear(ClearFlags.Target|ClearFlags.ZBuffer,Color.Green,1.0f,0); … // Начинаем отрисовку кадра d3d.BeginScene(); // Задаем матрицу проецирования d3d.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/3, // Угол зрения Width/(float)Height, // Отношение высоты и ширины окна 0.5f,25.0f); // Диапазон изменения координаты z // Задаем матрицу преобразования мировых координат для чайника: // сдвиг на 3.5 условные единицы :) по оси z от наблюдателя

Page 9: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

9

d3d.Transform.World = Matrix.Translation(0,0,3.5f); // Рисуем чайник teapot.DrawSubset(0); // Завершаем отрисовку кадра d3d.EndScene(); d3d.Present(); } После выполнения этих действий в окне должен появится силуэт чайника.

Шаг 6. Добавляем освещение. 6.1. Для подчеркивания трехмерности объекта можно включить источник освещения. В класс Form1 добавьте поле данных teapotMaterial типа Material для хранения свойств поверхности чайника. сlass Form1 { … Mesh teapot = null; // Модель чайника Material teapotMaterial; // Материал из которого изготовлен чайник … } 6.2. В метод Form1_Load добавьте код, задающий цвет диффузного (рассеянного) и зеркального отражения для материала чайника. private void Form1_Load(object sender, System.EventArgs e) { … // Создаем модель чайника и задаем ее свойства teapot = Mesh.Teapot(d3d); teapotMaterial = new Material(); teapotMaterial.Diffuse = Color.Blue; teapotMaterial.Specular = Color.White; … } Класс, а точнее, структура Material не содержит ссылок на объекты Direct3D, поэтому освобождение материалов в методе Dispose не требуется. 6.3. В методе Form1_Paint задайте положение и свойства одного источника освещения. Перед рисованием чайника установите материал для расчета освещения полигональной модели. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { … d3d.BeginScene();

Page 10: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

10

// Устанавливаем параметры источника освещения d3d.Lights[0].Enabled = true; // Включаем нулевой источник освещения d3d.Lights[0].Diffuse = Color.White; // Цвет источника освещения d3d.Lights[0].Position = new Vector3(0,0,0); // Задаем координаты … d3d.Material = teapotMaterial; // Устанавливаем материал для чайника teapot.DrawSubset(0); … } После выполнения шага 6 модель чайника приобретает объемный вид.

Шаг 7. Рассматриваем чайник со всех сторон. 7.1. На этом шаге к трехмерной сцене будет добавлена анимация. Для начала необходимо организовать постоянную перерисовку окна. Для этого можно воспользоваться механизмом фоновой обработки (idle processing). Как только приложение обработало все сообщения и очередь сообщений оказалась пуста, вызывается событие Application.Idle. Если обработчик этого события пометит главное окно как требующее обновление, то в очередь событий приложения почти сразу будет помещен запрос на перерисовку окна (сообщение WM_PAINT), который приведет к перерисовке окна и, в частности, к вызову метода Form1_Paint. После обработки запроса на перерисовку окна очередь сообщений вновь окажется пуста и будет вызвано событие Idle, обработчик которого вновь пометит главное окно приложения как требующее обновления и т.д. Для реализации этой схемы добавьте к классу Form1 следующий метод OnIdle. class Form1 { … private void OnIdle(object sender,EventArgs e) { Invalidate(); // Помечаем главное окно (this) как требующее перерисовки } … } Для того, чтобы связать метод OnIdle с событием Application.Idle необходимо изменить код метода Main, с которого начинается выполнение программы. static void Main() { // Создаем объект-окно Form1 mainForm = new Form1(); // Cвязываем метод OnIdle с событием Application.Idle Application.Idle += new EventHandler(mainForm.OnIdle); // Показываем окно и запускаем цикл обработки сообщений Application.Run(mainForm); }

Page 11: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

11

7.2. Если теперь собрать и запустить проект, то в главном окне будет наблюдаться неприятное мерцание. Это связано с взаимодействием двойной буферизации Direct3D и отрисовки фона окна. Чтобы избавиться от этого неприятного эффекта добавьте в конструктор класса Form1 код для установки соответствующего режима перерисовки окна. public Form1() { … SetStyle(ControlStyles.ResizeRedraw,true); // Устанавливаем режим обновления окна SetStyle(ControlStyles.Opaque,true); SetStyle(ControlStyles.UserPaint,true); SetStyle(ControlStyles.AllPaintingInWmPaint,true); } 7.3. Для того, чтобы скорость анимации не зависела от производительности компьютера, запомним время начала анимации в поле данных класса Form1. Тогда при каждой перерисовке окна можно определить сколько времени прошло от начала перерисовки и изменить положение и состояние объектов соответствующим образом. Добавьте поле данных с именем startTime типа DateTime в класс Form1 для хранения времени начала анимации. сlass Form1 { … DateTime startTime; // Время начала анимации … } Инициализируйте эту переменную в конце блока try/catch в методе Form1_Load. private void Form1_Load(object sender, System.EventArgs e) { try { … startTime = DateTime.Now; // Засекаем время начала анимации } catch(Exception exc) { … } } В начало метода Form1_Paint добавьте код для вычисления времени в секундах, прошедшего с момента начала анимации до начала отрисовки текущего кадра. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { // Замеряем интервал времени между отрисовкой этого кадра и началом анимации DateTime currTime = DateTime.Now; TimeSpan totalTime = currTime - startTime; double totalSeconds = totalTime.TotalSeconds; … } 7.3. Для вращения чайника достаточно задавать матрицу преобразования мировых координат в зависимости от значения переменной totalSeconds. Заведите в классе Form1 две константы типа double, определяющие частоту вращения (оборотов/c) чайника вокруг осей OX и OY.

Page 12: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

12

сlass Form1 { … const double TeapotRotationX = 0.2; const double TeapotRotationY = 0.3; … } Измените в методе Form1_Paint код для вычисления матрицы преобразования мировых координат, как произведения преобразований вращения и переноса. Перед рисованием чайника можно отключить отсечение нелицевых граней, установив свойство d3d.RenderState.CullMode равным Сull.None. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { … // Отключаем отсечение нелицевых граней d3d.RenderState.CullMode = Cull.None; // Задаем матрицу преобразования для чайника d3d.Transform.World = Matrix.RotationX((float)(totalSeconds*TeapotRotationX*2*Math.PI))* Matrix.RotationY((float)(totalSeconds*TeapotRotationY*2*Math.PI))* Matrix.Translation(0,0,3.5f); … } После выполнения шага 7 чайник должен начать плавно вращаться вокруг двух осей.

Шаг 8. Усложняем сцену. 8.1. На этом шаге в сцену будут добавлены простые объекты, вращающиеся вокруг чайника. Добавьте в класс Form1 поля данных для хранения этих объектов и параметров их движения. сlass Form1 { … Mesh[] objects = new Mesh[10]; // Модели вращающихся объектов Material[] objectMaterials = new Material[3]; // Материал объектов const double OrbitRadius = 1.5; // Радиус орбиты вращения вокруг чайника const double RotationFreq = 0.4; // Частота вращения по орбите… … } 8.2. В метод Form1_Load добавьте код, создающий модели объектов, а именно полигональные аппроксимации сфер радиусом 0.05, и задающий свойства материалов. private void Form1_Load(object sender, System.EventArgs e) { … // Создаем модели объектов, вращающихся вокруг чайника // и задаем свойства материалов

Page 13: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

13

for(int i = 0;i<objects.Length;i++) objects[i] = Mesh.Sphere(d3d, 0.05f, // Радиус сферы 10, // Количество параллелей 10); // Количество меридианов objectMaterials[0].Diffuse = Color.Red; objectMaterials[1].Diffuse = Color.LightGreen; objectMaterials[2].Diffuse = Color.LightBlue; … } 8.3. Класс Microsoft.Direct3D.Mesh содержит ссылку на ресурсы Direct3D, требующие явного освобождения. Поэтому в метод Dispose класса Form1 добавьте код для освобождения всех ресурсов полигональных моделей их массива objects. Для более удобного и независимого от длины массива перечисления его элементов примените оператор foreach. protected override void Dispose(bool disposing) { … foreach(Mesh m in objects) if(m != null) m.Dispose(); … if(d3d != null) d3d.Dispose(); … } 8.4. В методе Form1_Paint отобразите созданные объекты на экран. Для каждого объекта из массива objects будет задана своя матрица преобразования мировых координат, осуществляющая вращение объектов вокруг чайника. При этом каждый объект вращается в своей плоскости. Сферы являются выпуклыми объектам, поэтому для уменьшения объема вычислений отсечение нелицевых граней можно включить (этот фрагмент кода также можно взять из файла $LabFiles$\sources.cs). private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { … d3d.Transform.Projection = Matrix. PerspectiveFovLH(…) // Включаем отсечение нелицевых граней d3d.RenderState.CullMode = Cull.CounterClockwise; // В цикле рисуем все объекты int numObjects = objects.Length; for(int i = 0;i<numObjects;i++) { // Задаем следующее преобразование координат: // Сдвигаем объект на радиус орбиты, // вращаем объект на зависящий от времени угол, // поворачиваем плоскость вращения на угол, зависящий от номера // объекта и совмещаем центр орбит объектов с центром чайника double a = i/(double)numObjects; d3d.Transform.World = Matrix.Translation(0,0,(float)OrbitRadius)* Matrix.RotationY((float)(2*Math.PI*(a + totalSeconds*RotationFreq)))* Matrix.RotationZ((float)(Math.PI*a)* Matrix.Translation(0,0,3.5f); // Задаем материал и рисуем i-й объект d3d.Material = objectMaterials[i % objectMaterials.Length]; objects[i].DrawSubset(0); } … }

Page 14: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

14

Теперь вокруг чайника вращаются несколько разноцветных сфер.

Шаг 9. Текстурирование. 9.1.Создайте в классе Form1 переменную с именем teapotTexture типа Texture для хранения ссылки на текстуру чайника. сlass Form1 { … Mesh teapot = null; // Модель чайника Material teapotMaterial; // Материал из которого изготовлен чайник Texture teapotTexture = null; // Текстура для чайника … } 9.2. Текстура будет загружена из файла. Классы для работы с потоками данных (в том числе и с файлами) находятся в пространстве имен System.IO. Для того, чтобы не указывать этот префикс при каждом обращении к соответствующим классам, добавьте в начало исходного текста директиву using System.IO. … using System.Data; using System.IO; … 9.3. Как и многие другие объекты Direct3D, текстуры удобно создавать при первом показе окна на экран и необходимо явно освобождать, когда они больше не нужны. Код для загрузки текстуры из файла добавьте в метод Form1_Load. Обратите внимание, что для гарантированного закрытия файла с текстурой (даже в случае возникновения исключения) применяется оператор using (не путать с одноименной директивой!). private void Form1_Load(object sender, System.EventArgs e) { … // Загружаем текстуру для чайника из файла с именем spheremap.bmp using(FileStream textureFile = new FileStream("spheremap.bmp",FileMode.Open)) { teapotTexture = Texture.FromStream(d3d, textureFile, // Поток данных с текстурой 0, // Будем использовать как обычную текстуру Pool.Managed); // Область памяти для размещения текстуры textureFile.Close(); } … }

Page 15: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

15

Код для освобождения текстуры разместите в методе Dispose класса Form1. protected override void Dispose(bool disposing) { … if(teapotTexture != null) teapotTexture.Dispose(); foreach(Mesh m in objects) …; … } 9.5. В качестве имени файла с текстурой была указана строка “spheremap.bmp”. Для того, чтобы приложение нашло этот файл скопируйте из каталога $LabFiles$ его в корневой каталог проекта (где лежит файл DXApp.sln). Установите этот каталог в качестве текущего каталога для запуска приложения. Для этого в окне Solution Explorer нажмите правую кнопку мыши над элементом с именем проекта (в нашем случае это DXApp). В появившемся меню выберите пункт Properties.

На экране появится окно свойств проекта. В расположенном слева иерархическом списке выберите раздел Configuration properties/Debugging, в расположенном справа списке установите в свойстве Working directory укажите корневой каталог проекта.

Page 16: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

16

9.4. Созданная средствами Direct3D модель чайника не содержит координат текстуры. Поэтому текстурные координаты ),( vu будут вычисляться исходя их вектора нормали [ ]zyx nnn ,, по следующим формулам

),1(5.0),1(5.0

+=+=

y

x

nvnu

что создает иллюзию отражающей поверхности. В методе Form1_Paint непосредственно перед рисованием чайника добавьте код, включающий текстурирование и устанавливающий соответствующие режимы текстурирования. Не забудьте выключить текстурирование перед рисованием вращающихся вокруг чайника сфер. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { … // Выключаем текстурирование d3d.SetTexture(0,null); int numObjects = objects.Length; for(int i = 0;i<numObjects;i++) { … } … // В качестве текстурных координат берем вектор нормали d3d.TextureState[0].TextureCoordinateIndex = (int)TextureCoordinateIndex.CameraSpaceNormal; // Преобразуем текстурные координаты по заданным формулам d3d.Transform.Texture0 = Matrix.Scaling(0.5f,-0.5f,1.0f)* Matrix.Translation(0.5f,0.5f,0.0f); // Передаем блоку растеризации две текстурные координаты d3d.TextureState[0].TextureTransform = TextureTransform.Count2; // Устанавливаем текстурк d3d.SetTexture(0,teapotTexture); // Устанавливаем свойства материала и рисуем чайник d3d.Material = teapotMaterial; teapot.DrawSubset(0); … }

Главное окно приложения теперь должно выглядеть следующим образом.

9.5. Если внимательно рассматривать полученное динамическое изображение, то можно заметить, что в некоторые моменты времени при некоторых положениях объекта на

Page 17: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

17

текстуре возникают явно заметные прямоугольные элементы. Для избавления от этого эффекта включите билинейную фильтрацию текстур, добавив следующий код перед рисованием чайника в методе Form1_Paint private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {… // Включаем билинейную фильтрацию текстур d3d.SamplerState[0].MinFilter = TextureFilter.Linear; d3d.SamplerState[0].MagFilter = TextureFilter.Linear; … teapot.DrawSubset(0); … }

Шаг 10. Добавляем фон. 10.1. На этом шаге к сцене будет добавлен фон. Или точнее, сцена вместе с наблюдателем будет помещена внутрь текстурированного тора. Добавьте к классу Form1 поля данных для хранения ссылок на тор и на текстуру для тора (аналогично полям данных для чайника и его текстуры). сlass Form1 { … Mesh torus = null; // Тор, внутри которого находится наблюдатель Texture torusTexture = null; // Текстура для тора … } 10.2. В метод Form1_Load добавьте код для создания полигональной модели тора и загрузки текстуры для него. Этот код также будет похож на код для создания модели чайника. private void Form1_Load(object sender, System.EventArgs e) { … // Загружаем модель тора из файла с именем torus.x torus = Mesh.FromFile("torus.x", MeshFlags.Managed, // Область памяти для размещения геометрии d3d); // Загружаем текстуру для тора из файла с именем water.bmp using(FileStream textureFile2 = new FileStream("water.bmp",FileMode.Open)) { torusTexture = Texture.FromStream(d3d, textureFile2, // Поток данных с текстурой 0, // Будем использовать как обычную текстуру Pool.Managed); // Область памяти для размещения текстуры TextureFile2.Close(); } … } Скопируйте в корневой каталог проекта файлы torus.x и water.bmp. 10.3. Точно также, как было сделано для модели чайника, ресурсы, занятые для модели и текстуры тора, необходимо освободить в методе Dispose. protected override void Dispose(bool disposing) { … if(torusTexture != null)

Page 18: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

18

torusTexture.Dispose(); if(torus != null) torus.Dispose(); … } 10.4. Добавьте код для рисования тора в метод Form1_Paint. Последовательность действий полностью аналогична применяемой ранее: задается преобразование координат и настройки для освещения и текстурирования. Затем отрисовывается сам объект. Код для рисования тора отличается тем, что текстурные координаты берутся из геометрических данных модели. Также при рисовании тора отключается расчет интенсивности освещения, так как на торе не заданы векторы нормалей. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {… teapot.DrawSubset(0); … // Устанавливаем преобразование координат для тора d3d.Transform.World = Matrix.Scaling(10,10,10)* Matrix.RotationZ((float)(Math.PI/2))* Matrix.Translation(0,-12,0)* Matrix.RotationX((float)(-Math.PI/6)); // Включаем свертку текстурных координат для правильного отображение // текстурированного тора d3d.RenderState.Wrap0 = WrapCoordinates.One|WrapCoordinates.Zero; // Получаем текстурные координаты из данных модели d3d.TextureState[0].TextureCoordinateIndex = (int)TextureCoordinateIndex.PassThru; // Устанавливаем текстуру для тора d3d.SetTexture(0,torusTexture); // Выключаем преобразование текстурных координат d3d.TextureState[0].TextureTransform = TextureTransform.Disable; // Выключаем расчет освещения d3d.RenderState.Lighting = false; // Рисуем тор torus.DrawSubset(0); // Включаем расчет освещения d3d.RenderState.Lighting = true; … } Внешний вид окна после выполнения шага 10 показан на следующем рисунке.

Шаг 11. Динамическое изменение фона. 11.1. На 9 шаге матрица преобразования текстурных координат использовалась для сдвига и масштабирования координат векторов нормалей. Разрешив преобразования текстурных

Page 19: Создание трехмерного графического приложения на платформе .NET с применением технологии Microsoft DirectX

DirectX 9.0 Managed API

19

координат для тора и изменяя матрицу преобразования текстурных координат можно добиться интересного эффекта движущегося фона. В методе Form1_Paint добавьте следующий код вместо строки, в которой свойству TextureTransform присваивается значение TextureTransform.Disable. private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {… // Устанавливаем текстуру для тора d3d.SetTexture(0,torusTexture); // Вычисляем сдвиг текстурных координат double deltaTex = totalSeconds*0.2; // Приводим его к диапазону [0..1] deltaTex -= Math.Floor(deltaTex); // Задаем матрицу сдвига для двух текстурных координат Matrix texMatrix = Matrix.Identity; texMatrix.M31 = (float)-deltaTex; texMatrix.M32 = (float)-deltaTex; // Применяем матрицы для преобразования текстурных координат d3d.Transform.Texture0 = texMatrix; // Выключаем расчет освещения d3d.RenderState.Lighting = false; // Рисуем тор torus.DrawSubset(0); // Включаем расчет освещения d3d.RenderState.Lighting = true; … } После выполнения шага 11 фон в окне приложения должен начать двигаться (на следующем рисунке этого, к сожалению, не видно ☺)

Поздравляем! Вы успешно выполнили практическую работу!


Recommended