Дослідження технологій створення тривимірних графічних додатків на базі платформи dotNET

DirectX як набір API функцій, розроблених для вирішення завдань, пов'язаних з ігровим і відеопрограмуванням в операційній системі Microsoft Windows. Етапи створення тривимірних графічних додатків на базі платформи dotNET. Аналіз компонентної моделі COM.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык украинский
Дата добавления 22.10.2012
Размер файла 4,4 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

На мові С++ інтерфейс звичайно описується як клас, набір абстрактних віртуальних функцій, що містить тільки. Наприклад, в лістингу 9 описується інтерфейс для роботи з домашніми тваринами (Pets), що містить два методи.

// інтерфейси прийнято іменувати із заголовної букви `I' interface IPet

{

int GetNumberOfLegs() = 0; // абстрактний метод - число ніг

void Speak() = 0; // абстрактний метод - видати звук

};

Для читачів, незнайомих з описом інтерфейсів на С++, пояснимо, що interface - це не нове ключове слово, а всього лише створене для наочності макроозначення (#define) звичного ключового слова struct:

// десь в системних заголовних файлах...

#define interface struct

Чому struct, а не class? Дуже просто: у структурах всі члени за умовчанням мають загальний доступ, тому при описі інтерфейсу не доведеться спеціально виписувати модифікатор доступу public. Відзначимо, що інтерфейс завжди проектується для доступу цілком: Ви або можете дістати доступ до всього інтерфейсу, або немає.

Звернемо увагу: явно вказано, що всі методи структури (або інтерфейсу) IPet є абстрактними (pure virtual). Це означає, що не можна створити безпосередньо екземпляр IPet. Натомість можна створити клас, успадкований від IPet, в якому реалізувати всі абстрактні методи. Говорять, що такий об'єкт називається реалізацією інтерфейсу. У лістингу приведений приклад такої реалізації.

class CDog : public IPet

{

int GetNumberOfLegs();

void Speak();

};

int CDog::GetNumberOfLegs()

{

return 4;

}

void CDog::Speak()

{

MessageBeep(-1); // видамо системний звук Windows

}

Тепер уявимо собі, як міг би використовуватися придуманий нами інтерфейс в програмі на C++. Припустимо, ми збираємося створити об'єкт "собака" і покерувати їм через наш інтерфейс IPet. Ось черговий шматок коду, дуже схожого на реальне використання COM-об'єктів (зокрема, компонентів DirectX):

IPet *pDog = CreatePet("Dog");

// дізнаємося, чи є наш друг чотириногим

int nLegs = pDog->GetNumberOfLegs();

// якщо лап не 4, примусимо собачку загавкати

if (nLegs != 4)

pDog->Speak();

Як бачимо, код використання інтерфейсу дуже схожий на звичайну роботу з об'єктами C++. Основна відмінність полягає в тому, що наша програма не використовує об'єкт CDog безпосередньо (вона про нього навіть не знає), а виконує всю роботу через покажчик на інтерфейс IPet. Тому ж в нашому прикладі міститься виклик гіпотетичної функції CreatePet, яка знала б про деталі реалізації інтерфейсу і уміла створювати екземпляр об'єкту "собака".

Такі обмеження злегка ускладнюють роботу з компонентами, але додають нашій програмі величезну гнучкість. Тепер ми можемо створювати і використовувати готові компоненти не тільки на мові C++. Взагалі кажучи, дослідивши уявлення в пам'яті, яке утворює інтерфейс, ми можемо відтворити це бінарне уявлення іншими засобами (наприклад, на мові C, в якій взагалі немає класів і віртуальних функцій).

Підтримка COM вбудована і в деякі інші програмні середовища, наприклад, в Delphi і Visual Basic. Це дозволяє створювати двійкові версії компонентів: скомпілювавши DLL, що містить код компоненту на одній мові, викликати і використати цей об'єкт на іншій мові програмування.

З останнім твердженням пов'язана одна проблема: механізми створення і знищення об'єктів розрізняються в різних програмних середовищах. Наша гіпотетична функція CreatePet повинна враховувати всі ці особливості, щоб уміти створювати об'єкти, що містяться в двійкових бібліотеках, що відкомпілювалися, і запрошувати у них підтримку необхідного інтерфейсу.

Для створення COM-компонентів звичайно застосовуються функції CoCreateInstance і CoGetClassObject. Ці функції надає бібліотека COM, тому перед їх викликом потрібна її ініціалізація.

Деякі об'єкти DirectX також можна створювати таким чином, але звичайно використовується простіший метод. Бібліотека надає спеціальні функції для створення необхідних програмних компонентів. Наприклад, для створення об'єкту Direct3D версії 9 існує функція Direct3DCreate9, описана в заголовному файлі d3d9.h. Саме цей спосіб був застосований в нашому прикладі.

3.3.10 Інтерфейс IUnknown

Приведений в попередньому розділі інтерфейс IPet не є інтерфейсом в стилі COM. Щоб задовольняти специфікації COM, всі інтерфейси, включаючи інтерфейси DirectX, повинні бути успадковані від спеціального інтерфейсу IUnknown. Не дивлячись на свою назву ("unknown" в перекладі з англійського означає "невідомий"), це -- інтерфейс, про який знають все. У лістингу 12 приведене небагато спрощений опис інтерфейсу IUnknown для мови С++.

interface IUnknown

{

virtual HRESULT QueryInterface( const IID& iid void** ppv )= 0;

virtual ULONG AddRef() = 0;

virtual ULONG Release() = 0;

};

Розглянемо призначення методів IUnknown.

QueryInterface

Про довільний COM-об'єкт звичайно можна сказати тільки те, що він підтримує інтерфейс IUnknown. Призначення QueryInterface -- визначити, чи підтримує даний об'єкт будь-який інший інтерфейс. Пам'ятаєте приклад з інтерфейсом IPet? Якби він був створений з урахуванням специфікації COM, то у довільного об'єкту COM можна було б запитати, чи є він твариною:

IUnknown *pUnk = ... // якимсь чином одержуємо покажчик на об'єкт

IPet *pPet = 0;

if(SUCCEEDED(pUnk->QueryInterface(IID_IPet, (void**)&pPet))

{

... // так, цей об'єкт є твариною

}

Пояснимо два моменти з цього прикладу.

Звичайно функції і методи об'єктів COM повертають спеціальне значення типу HRESULT, для індикації успішності або невдачі виклику. Макроси SUCCEEDED і FAILED є рекомендованим способом перевірки такого результату.

Щоб відрізняти один інтерфейс від іншого (і запрошувати їх підтримку), недостатньо просто рядки з ім'ям. Імена на зразок «IPet» досить поширені, і існує вірогідність одночасного використання таких імен двома різними розробниками. Щоб уникнути таких конфліктів творцями COM було ухвалене рішення використовувати для ідентифікації інтерфейсів і об'єктів спеціальний 128-бітовий ідентифікатор - GUID, який є статистично унікальним. При створенні нового інтерфейсу розробник просто створює новий GUID і описує його в заголовному файлі або бібліотеці типів об'єкту. У нашому прикладі передбачалося, що такої GUID описаний в заголовному файлі інтерфейсу під ім'ям IID_IPet.

AddRef, Release

Звичайний екземпляр класу C++ створюється в програмі викликом конструктора, а віддаляється при виході з області видимості або викликом оператора delete, якщо об'єкт був створений в динамічній пам'яті. Для COM типова ситуація, коли об'єкт реалізує відразу декілька інтерфейсів, і покажчики на ці інтерфейси зберігаються в клієнтах.

При цьому відстежування всіх таких покажчиків стає непростим завданням. Що, якщо об'єкт буде видалений, а посилання на нього десь збережуться? Подальша робота з цими посиланнями приведе до збою програми. Інша, часто виникаюча проблема це необхідність видалення об'єкту після того, як всі посилання на нього зруйновані. Якщо даремний об'єкт не віддаляється, він даремно витрачатиме оперативну пам'ять і інші цінні ресурси.

Призначення методів AddRef і Release -- управління часом життя об'єкту. Одержавши в своє користування інтерфейс об'єкту, необхідно викликати AddRef. Це дозволить гарантувати, що об'єкт не буде зруйнований, поки використовується хоч би один його інтерфейс.

Завершуючи роботу з об'єктом, необхідно повідомити його про це. Для кожного одержаного від нього інтерфейсу необхідно викликати метод Release. Звичайно об'єкт знищується, якщо на нього більше немає зовнішніх посилань. Але реалізація цієї поведінки цілком контролюється програмістом компоненту. Значення методів AddRef і Release, що повертається, найчастіше містить поточне значення внутрішнього лічильника посилань об'єкту, але його рекомендується ігнорувати, оскільки на це значення немає чіткої специфікації. Проте, одному з авторів доводилося бачити ось такий сумнівний код "гарантованого видалення" об'єкту DirectX:

while(p->Release()); // (C) 1999, 2000 NVIDIA Corporation

Як бачите, великі корпорації іноді можуть дозволити собі ігнорувати рекомендації специфікацій COM.

Сподіваємося, що цього короткого знайомства з моделлю COM буде досить для того, щоб розібратися в прикладах, що наводяться. Якщо ви хочете дізнатися про неї більше, рекомендуємо прочитати спершу книгу Дейла Роджерсона «Основи COM».

3.4 Managed DirectX. Програмування для .NET

У .NET-программистов тепер теж є можливість легко створювати Direct3D-приложения. Починаючи з дев'ятої версії, до складу DirectX SDK включений так званий Managed DirectX -- програмний інтерфейс до бібліотеки DirectX для платформи .NET. Він включає .NET-сборки з реалізацією компонентів DirectX, програмні приклади і документацію.

Для створення програм з використанням Managed DirectX необхідно, щоб крім середовища виконання DirectX на комп'ютері була встановлена остання версія .NET Framework (на даний момент - 1.1), а також набір складок Managed DirectX. Ось перелік цих файлів:

§ Microsoft.DirectX.AudioVideoPlayback.dll

§ Microsoft.DirectX.Diagnostics.dll

§ Microsoft.DirectX.Direct3D.dll

§ Microsoft.DirectX.Direct3DX.dll

§ Microsoft.DirectX.DirectDraw.dll

§ Microsoft.DirectX.DirectInput.dll

§ Microsoft.DirectX.DirectPlay.dll

§ Microsoft.DirectX.DirectSound.dll

§ Microsoft.DirectX.dll

Ми створимо тестовий проект з використанням середовища Visual Studio 2003, хоча простий DirectX-додаток цілком можна створити і без цього середовища розробки: як і у випадку з GDI+, досить текстового редактора і компілятора командного рядка csc.exe. Отже, створіть новий проект (Рис 3.6). Із списку Project Types виберіть пункт Visual C# Projects, а з переліку проектів, що з'явився, -- пункт Windows Application. Введіть ім'я нового проекту, виберіть каталог для збереження його файлів і натисніть OK.

Рис 3.6 Створення нового додатку для платформи .NET

У нас з'явиться вікно дизайнера форми - головного вікна майбутнього додатку. Ми не використовуватимемо дизайнер, а введемо початковий код всієї програми в редакторі. Тому натисніть на дизайнері праву кнопку миші і виберіть з контекстного меню, що з'явилося, пункт View Code.

Видаліть весь текст з редактора і введіть замість нього код з лістингу.

using System;

using System.Windows.Forms;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

class TestForm: Form

{

static void Main()

{

TestForm form = new TestForm();

form.Text = "Direct3D для .NET";

form.Width = 400;

form.Height = 300;

form.InitD3D();

form.Show();

while(form.Created)

{

form.Render();

Application.DoEvents();

}

}

Device device;

Mesh teapot;

public void InitD3D()

{

PresentParameters parameters = new PresentParameters();

parameters.Windowed = true;

parameters.SwapEffect = SwapEffect.Discard;

device = new Device(0, DeviceType.Hardware, this

CreateFlags.SoftwareVertexProcessing, parameters);

teapot = Mesh.Teapot(device);

}

public void Render()

{

device.Clear(ClearFlags.Target,

System.Drawing.Color.Blue, 1.0f, 0);

device.BeginScene();

try

{

device.RenderState.Lighting = true;

device.Lights[0].Type = LightType.Directional;

device.Lights[0].Direction = new Vector3(7 -2, 1);

device.Lights[0].Diffuse = System.Drawing.Color.Yellow;

device.Lights[0].Commit();

device.Lights[0].Enabled = true;

Material material = new Material();

material.Ambient = System.Drawing.Color.White;

material.Diffuse = System.Drawing.Color.White;

material.Specular = System.Drawing.Color.White;

device.Material = material;

Matrix matrix = new Matrix();

matrix.Scale(0.4f, 0.4f, 0.4f);

matrix.M43 = 1; // перенесення по осі Z

device.Transform.World = matrix;

teapot.DrawSubset(0);

}

finally

{

device.EndScene();

}

device.Present();

}

}

Програма виконує побудову тієї ж сцени, що і її еквівалент на C++, але текст її помітно коротший. Це пов'язано з тим, що нам не довелося возитися з низькорівневими деталями: реєстрацією класу вікна і обробкою повідомлень. Відмітимо також, що, хоча в .NET підтримується концепція інтерфейсів, при створенні Managed API для інтерфейсів DirectX було вирішено упакувати їх в классы- «обгортки», що помітно спрощує їх використання.

Для успішної компіляції одержаної програми необхідно додати в проект посилання на наступні складки Managed DirectX:

· Microsoft.DirectX.dll

· Microsoft.DirectX.Direct3D.dll

· Microsoft.DirectX.Direct3DX.dll

Для цього в меню Project виберіть пункт Add Reference. і в списку складок, що з'явився, по черзі виберіть подвійним клацанням миші всі три складки (Рис 3.7)

За умовчанням при установці Managed DirectX на комп'ютер ці файли поміщаються в каталог <Windows>\Microsoft.NET\Managed DirectX\v4.09.00.0900\. Якщо з якоїсь причини инсталлятор не помістив їх в глобальний кеш складок (таке трапляється), то ви зможете знайти їх там, щоб самостійно додати посилання на ці складки в свій проект.

Рис 3.7 Додавання складок Direct3D у проект на C#

Тепер компіляція повинна пройти без проблем. Побудуйте програму і переконайтеся, що воно виводить на екран таке саме зображення, як і приклад на C++.

Можна приступати до детальнішого знайомства з Direct3D. Почнемо з ініціалізації.

3.5 Початок роботи. Ініціалізація Direct3D

Будь-яка бібліотека містить, як правило, структури даних, що потребують правильної ініціалізації, і DirectX -- не виключення.

У цьому розділі ми розповімо про особливості ініціалізації компоненту Direct3D, питаннях вибору доступних графічних пристроїв і установці відеорежимів.

3.5.1 Ініціалізація і перелік графічних пристроїв засобами C++

Як вже мовилося, у версії для C++ належна підготовка бібліотеки для роботи виконується створенням об'єкту Direct3D. Цей об'єкт реалізує COM-інтерфейс IDirect3D9, основне призначення якого -- проведення подальшої ініціалізації і настройка режимів графічного пристрою.

Для створення об'єкту Direct3D у нашому прикладі використовувався код наступного вигляду:

IDirect3D9 *pD3D;

pD3D = Direct3DCreate9(D3D_SDK_VERSION);

Тут D3D_SDK_VERSION -- константа, визначена в заголовному файлі d3d9.h (взагалі-то, у складі DirectX 9.0 SDK є і старий заголовний файл d3d8.h, у якому ця константа має інше значення, так що будьте обережні). Її призначення - переконатися в тому, що програма скомпільована і зібрана з однією і тією ж версією заголовних файлів і бібліотек.

В результаті виконання ми одержуємо покажчик на інтерфейс IDirect3D9, використовуючи який можна проводити подальші дії по настройці графічного устаткування.

Останнім часом набули поширення конфігурації ПК з декількома відеоадаптерами, наприклад, комбінація інтегрованої «офісної» відеокарти і додаткової «ігрової». Досить частим завданням є отримання списку доступних графічних пристроїв - наприклад, щоб дозволити користувачу вибрати відеокарту, яку він вважає за краще використовувати для віртуальної битви.

Для отримання кількості доступних в системі відеоадаптерів використовується метод IDirect3D9::GetAdapterCount(). Подальший перебір доступних адаптерів можна здійснювати, послідовно викликаючи метод IDirect3D9::GetAdapterIdentifier з порядковим номером Adapter, що збільшується:

HRESULT GetAdapterIdentifier(UINT Adapter

DWORD Flags

D3DADAPTER_IDENTIFIER9 *pIdentifier );

Як значення Flags потрібно передати 0 (для звичайного перебору) або константу D3DENUM_WHQL_LEVEL для перебору тільки тих адаптерів, драйвери яких мають сертифікацію Microsoft Windows Hardware Quality Labs (WHQL). У останньому випадку не дивуйтеся, якщо комп'ютер почне «стукатися» в Інтернет для завантаження новітніх сертифікатів.

Останній параметр - покажчик на структуру D3DADAPTER_IDENTIFIER9, у яку заноситься інформація про вибраний відеоадаптер. От як, використовуючи цю інформацію, вивести в консоль дані про всі встановлені відеоадаптери:

#include <stdio.h>

#include <d3d9.h>

#pragma comment(lib, "d3d9.lib")

int main()

{

IDirect3D9 *pD3D = Direct3DCreate9(D3D_SDK_VERSION);

if(!pD3D)

{

printf("Direct3DCreate9 failed!\n");

return 1;

}

int nCount = pD3D->GetAdapterCount();

for(int i=0; i<nCount; i++)

{

D3DADAPTER_IDENTIFIER9 info;

pD3D->GetAdapterIdentifier(i, 0 &info);

printf("Adapter %d: \n"

"\tDriver: %s\n"

"\tDescription: %s\n"

"\tDeviceName: %s\n",

i, info.Driver, info.Description,

info.DeviceName);

}

return 0;

}

Приклад виведення цієї програми на комп'ютері з однією відеокартою:

Adapter 0:

Driver: ati2dvag.dll

Description: RADEON 9600 SERIES

DeviceName: \.\DISPLAY1

А ось результат виконання на машині з трьома відеокартами:

Adapter 0:

Driver: nv4_disp.dll

Description: NVIDIA GeForce DDR

DeviceName: \.\DISPLAY1

Adapter 1:

Driver: perm2dll.dll

Description: Appian Graphics Jeronimo Pro

DeviceName: \.\DISPLAY2

Adapter 2:

Driver: perm2dll.dll

Description: Appian Graphics Jeronimo Pro

DeviceName: \.\DISPLAY3

Схожим чином розв'язується і інше завдання: перерахувати список доступних для даного адаптера відеорежимів. Для переліку режимів інтерфейс IDirect3D9 надає методи GetAdapterModeCount і EnumAdapterModes. Але, на відміну від обмеженого числа доступних адаптерів, існує безліч різних комбінацій дозволу, колірних форматів і частоти оновлення екрану. Тому при переліку доступних режимів необхідно додатково указувати допустимі піксельні формати (кольори, що описують глибину, і порядок колірних складових). Константи форматів описані в переліку D3DFORMAT.

3.5.2 Перелік пристроїв і відеорежимів для Managed Direct3D

Версія Direct3D для .NET не вимагає явної ініціалізації бібліотеки. Функція Direct3DCreate9 викликається автоматично в статичному конструкторі класу Microsoft.DirectX.Direct3D.Manager.

За допомогою цього класу також легко одержати інформацію про список доступних графічних пристроїв. Його властивість Adapters повертає список структур AdapterInformation, що містять всі потрібні дані. У лістингу приведений приклад програми на C#, що також виводить список встановлених адаптерів.

using System;

using System.Windows.Forms;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

class TestForm: Form

{

static void Main()

{

foreach(AdapterInformation adapterInfo in Manager.Adapters)

Console.WriteLine("Adapter: \n\tDriver: {0}n\t"+

"Description: {1}n\tDeviceName: {2}n",

adapterInfo.Information.DriverName,

adapterInfo.Information.Description,

adapterInfo.Information.DeviceName);

}

}

Завдання отримання списку доступних для кожного адаптера відеорежимів також розв'язується дуже просто: у структурі AdapterInformation існує властивість SupportedDisplayModes, що надає список всіх доступних режимів.

Як вже мовилося, цей список може містити сотні комбінацій, тому розумно відбирати з нього тільки режими з відповідним колірним форматом. Тому властивість SupportedDisplayModes підтримує індексування за кодом формату, описаним в класі Microsoft.DirectX.Direct3D.Format просто вкажіть код формату в квадратних дужках після імені властивості.

Ось приклад виведення всіх доступних для першого відеоадаптера комбінацій відеорежимів з глибиною кольору High Color 5-6-5 (по 5 біт для представлення червоного і синього кольору і 6 біт для передачі зеленого):

foreach(DisplayMode mode in

Manager.Adapters[0].

SupportedDisplayModes[Format.R5G6B5])

Console.WriteLine("{0}x{1}, {2} Hz, Format: {3}",

mode.Width, mode.Height, mode.RefreshRate, mode.Format);

Ви, можливо, відмітили, що нам знову допомагає особливість .NET Framework: всі константи переліків, як, наприклад, назви різних колірних форматів, повертають «читані» імена при їх висновку.

3.5.3 Ініціалізація графічного режиму для C++

Для виведення зображень в Direct3D використовується об'єкт Device (пристрій), який відповідає за взаємодію з апаратною частиною відеоадаптера. При створенні цього об'єкту необхідно вказати ряд параметрів, які ми зараз і розглянемо.

Пригадаємо фрагменти коду створення об'єкту Direct3D Device в своїй першій програмі:

HRESULT hr = pD3D->CreateDevice(

D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd

D3DCREATE_SOFTWARE_VERTEXPROCESSING,

&params &pDevice);

Перший параметр методу IDirect3D9::CreateDevice визначає адаптер, для якого створюється об'єкт Device. Можна вказати або порядковий номер, одержаний переліком (як в лістингу 14), так і константу D3DADAPTER_DEFAULT, при завданні якої буде використаний адаптер, вибраний користувачем за умовчанням.

Наступний параметр визначає тип пристрою і може приймати одне з трьох значень: D3DDEVTYPE_HAL, D3DDEVTYPE_REF або D3DDEVTYPE_SW. Другий і третій режими використовують програмну емуляцію всіх операцій Direct3D і можуть мати сенс тільки для відладки, оскільки виконуються дуже поволі.

Параметр hWnd необхідний для вказівки дескриптора вікна Windows, до якого буде прив'язане створення пристрою.

Наступний параметр вперше з'явився в DirectX 8.0 завдяки реалізації у відеокартах останнього покоління апаратної підтримки T&L (Transform and Lighting, Трансформація і освітлення). Ми вказали значення D3DCREATE_SOFTWARE_VERTEXPROCESSING, щоб використовувати програмну обробку вершин примітивів. Для включення апаратної підтримки T&L використовуйте константу D3DCREATE_HARDWARE_VERTEXPROCESSING. Це зніме велике навантаження з центрального процесора по обсчету вершин, але обмежить число відеокарт, що підтримують програму.

Далі необхідно передати покажчик на структуру D3DPRESENT_PARAMETERS, що містить додаткові параметри ініціалізації. Повний перелік полів цієї структури приведений в довідці по Direct3D. Як ми вже переконалися, для ініціалізації віконного відеорежиму досить ініціалізувати структуру нулями і вказати тільки три параметри:

D3DPRESENT_PARAMETERS params;

ZeroMemory( &params, sizeof(params));

params.Windowed = TRUE;

params.SwapEffect = D3DSWAPEFFECT_DISCARD;

params.BackBufferFormat = D3DFMT_UNKNOWN;

Ще чого одна користь установки налагоджувальної версії DirectX Runtime: вказівка значення D3DSWAPEFFECT_DISCARD у полі SwapEffect допомагає при відладці додатків Direct3D. При цьому кожен кадр, що виводиться, перед побудовою сцени заповнюється випадковим «шумом», і ви легко відмітите ситуацію, при якій зображення, що виводиться, не заповнює всю область кадру.

Ініціалізація повноекранного відеорежиму вимагає додаткової вказівки, як мінімум, ще чотирьох полів: BackBufferWidth, BackBufferHeight (дозвіл екрану), BackBufferFormat (колірний формат) і FullScreenRefreshRateInHz (частота оновлення екрану). У лістингу 16 приведений приклад ініціалізації відеорежиму 1024x768, TrueColor, з частотою кадрів за умовчанням:

D3DPRESENT_PARAMETERS params;

ZeroMemory( &params, sizeof(params));

params.Windowed = FALSE;

params.SwapEffect = D3DSWAPEFFECT_DISCARD;

params.BackBufferWidth = 1024;

params.BackBufferHeight = 768;

params.BackBufferFormat = D3DFMT_A8R8G8B8;

params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

HRESULT hr = pD3D->CreateDevice(

D3DADAPTER_DEFAULT,

D3DDEVTYPE_HAL, hWnd

D3DCREATE_SOFTWARE_VERTEXPROCESSING,

&params &pDevice);

Якщо відеокарта не підтримує вказаний колірний формат, виклик CreateDevice поверне помилку. Інакше змінна pDevice міститиме покажчик на створений пристрій IDirect3DDevice9.

3.5.4 Ініціалізація графічного режиму: версія для C#

Установка відеорежиму в реалізації Managed DirectX дуже схожа на версію для C++, з однією істотною відмінністю: об'єкт Device створюється викликом конструктора, що виглядає природніше. Ось опис цього конструктора:

public Device(

int adapter

DeviceType deviceType

Control renderWindow

CreateFlags behaviorFlags

PresentParameters presentationParameters

);

Сенс параметрів залишився тим же, що і у версії для C++. Значення констант містяться в класах-переліках DeviceType, CreateFlags і т.д., що не вимагає їх запам'ятовування. Вище вже був приведений код для ініціалізації віконного пристрою, а в лістингу 17 міститься приклад установки повноекранного відеорежиму (з тими ж характеристиками, що і в прикладі на C++)

PresentParameters parameters = new PresentParameters();

parameters.BackBufferWidth = 1024;

parameters.BackBufferHeight = 768;

parameters.BackBufferFormat = Format.A8R8G8B8;

parameters.FullScreenRefreshRateInHz =

PresentParameters.DefaultPresentRate;

parameters.Windowed = false;

parameters.SwapEffect = SwapEffect.Discard;

device = new Device(0, DeviceType.Hardware, this

CreateFlags.SoftwareVertexProcessing, parameters);

Вказівка неправильних або непідтримуваних параметрів відеорежиму приведе до генерації виключення Microsoft.DirectX.Direct3D.InvalidCallException.

3.5.5 Видалення невидимих деталей

Наш симпатичний чайник малюється в дуже вигідному ракурсі, який приховує явні дефекти зображення. Признатися, ми ретельно підібрали положення об'єкту і кут огляду для першого прикладу: хотілося швидше одержати готове зображення, не стомлюючи вас зайвими проблемами.

Але настав час рухатися далі, і зараз ідилія буде зруйнована. Не лякайтеся, просто пора познайомитися з реалізацією алгоритмів видалення невидимих деталей в Direct3D.

Піраміда видимості

Достатньо злегка зрадити параметри сцени, як з чайником почне творитися щось недобре. Знайдіть в програмі на C++ наступний фрагмент:

D3DMATRIX transform = {

scale, 0.0f, 0.0f, 0.0f

0.0f, scale, 0.0f, 0.0f

0.0f, 0.0f, scale, 0.0f

0.0f, 0.0f, 1.0f, 1.0f

};

І заміните його новим (зверніть увагу на виділений текст):

D3DMATRIX transform = {

scale, 0.0f, 0.0f, 0.0f

0.0f, scale, 0.0f, 0.0f

0.0f, 0.0f, scale, 0.0f

0.0f, 0.0f, 0.3f, 1.0f

};

Аналогічна ділянка в програмі на C# виглядає так:

matrix.M43 = 1; // перенесення по осі Z

Також заміните тільки константу в рядку:

matrix.M43 = 0.3f; // перенесення по осі Z

У обох програмах зміна торкнулася тільки одного елементу матриці перетворення, елементу з індексом (4, 3).

Ми ще детально розглядатимемо геометричні перетворення в просторі Direct3D, зараз досить сказати, що цей елемент відповідає за переміщення об'єкту від центру координат по осі Z. Тобто ми відсунули чайник від камери не на 1 одиницю відстані, як в першому випадку, а на 0.3. Результат такої зміни показаний на рисунку 3.8.

Рис 3.8 Об'єкт відрізаний з боку спостерігача

Не менш вражаючий результат вийде, якщо зрадити той же елемент матриці із значення 1.0 до всього 1.05 (рис 3.9).

Рис 3.9 Об'єкт відрізаний з протилежної сторони

Як видно з малюнків, всі деталі об'єкту, що виходять за певні межі, акуратно «відрізуються». Якщо ви проведете експерименти з елементами матриці (4, 1) і (4, 2), що відповідають за переміщення об'єкту по двох іншим координатним осям, то відмітите, що висновок обмежений і в цих координатах. Це наочна демонстрація роботи механізму відсікання в Direct3D. Навіщо потрібні такі обмеження?

Якщо провести чотири уявні промені від точки спостереження до країв екрану, то вийде піраміда з прямокутником (екраном) в підставі. Продовжуючи промені далі, углиб віртуального світу, який «спроектований» на наш екран, ми одержимо піраміду видимості (viewing frustum). Все, що знаходиться за межами цієї піраміди, все одно буде поза полем проекції, так навіщо малювати ці деталі, даремно витрачаючи час?

Якщо розміркувати, то не потрібно малювати і дуже видалені об'єкти, потрібно уміти вчасно зупинитися. Все одно на шляху погляду рано чи пізно зустрінеться перешкода. Та і людське око не побачить чайник, видалений на відстань в, скажімо, десятки кілометрів. Те ж саме торкається, наприклад, предметів, що «знаходяться» у віртуальному світі позаду нас або перед екраном. Тому в DirectX і реалізовано відсікання об'єктів по усіченій піраміді, освіченій передній, задній і чотирма бічними відсікаючими площинами (рис. 3.10).

Рис 3.10 Проекція зображення і піраміда видимості

Це досить корисна функціональність, що дозволяє заощадити час при обробці складних сцен, а, отже, підвищити продуктивність. Проте зараз ми просто відключимо цей механізм, оскільки в наших учбових прикладах швидкодія не важливо.

Щоб відмовитися від відсікання в програмі на C++, перед виведенням об'єкту додайте строчку:

pDevice->SetRenderState(D3DRS_CLIPPING, FALSE);

Еквівалентна строчка на C# виглядає так:

device.RenderState.Clipping = false;

Можете перевірити: переміщення об'єкту по сцені більше не відображатимуться на його цілісності.

3.5.6 Буфер глибини (Z-buffer)

Щоб проілюструвати чергову проблему, що виникає при зображенні складних об'єктів (або груп об'єктів), додамо в сцену динамічність.

Хай наш чайник обертається з постійною швидкістю навколо осі Y. Для цього потрібно, по-перше, примусити програму постійно перемальовувати сцену. Оскільки наша програма на C++ викликає метод Render в обробнику WM_PAINT, то для постійного перемальовування досить викликати функцію InvalidateRect після малювання:

case WM_PAINT:

{

PAINTSTRUCT ps;

BeginPaint(hWnd &ps);

g_App.Render();

EndPaint(hWnd &ps);

InvalidateRect(hWnd, 0, TRUE);

return 0;

}

Для реалізації обертання об'єкту скористаємося бібліотекою утиліт D3DX. Нам допоможе функція D3DXMatrixRotationY, яка створює матрицю повороту на необхідний кут. Для її використання необхідно створити об'єкт D3DXMATRIX і передати кут повороту в радіанах.

Клас D3DXMATRIX, також наданий бібліотекою D3DX, є просто обгорткою над вже знайомою структурою D3DMATRIX. У цьому класі реалізований ряд корисних математичних операцій, а також операції порівняння матриць.

Для формування остаточної матриці помножимо одержану матрицю повороту на початкову матрицю transform, що існувала в первинній версії програми. Виправлений фрагмент методу Render представлений в лістингу.

float scale = 0.4f;

D3DXMATRIX transform(

scale, 0.0f, 0.0f, 0.0f

0.0f, scale, 0.0f, 0.0f

0.0f, 0.0f, scale, 0.0f

0.0f, 0.0f, 1.0f, 1.0f

);

D3DXMATRIX rotation;

D3DXMatrixRotationY(&rotation, GetTickCount()/1000.0f);

rotation *= transform;

pDevice->SetTransform(D3DTS_WORLD, &rotation);

pDevice->SetRenderState(D3DRS_CLIPPING, FALSE);

pTeapot->DrawSubset(0);

pDevice->EndScene();

Як видно з лістингу, для отримання монотонно зростаючого з кожним кадром кута повороту ми скористалися функцією GetTickCount, що повертає число мілісекунд з моменту старту програми. Ми ділимо це число на 1000, таким чином, наш чайник обертається строго із швидкістю 1 радіан в секунду (Рис 3.11).

Рис 3.11 Зображення малюється неправильно (носик «просвічує» крізь чайник)

А ось і обіцяний дефект: можна відмітити, що деякі деталі чайника «просвічують» крізь корпус, хоча винні їм затулятися. Якщо ми повторимо малювання об'єкту в цій же сцені, але з трохи іншими координатами, перетин двох цих фігур також не зображатиметься правильно.

Річ у тому, що наш об'єкт складається з трикутних примітивів, а порядок їх малювання не визначений. Якщо трикутники, з яких складається носик чайника, малюватимуться після зображення корпусу, це неминуче затре вже створене зображення.

Один з найефективніших способів рішення цієї проблеми давно реалізований апаратний у всіх сучасних відеокартах і називається алгоритмом Z-буфера, або буфера глибини. У його основі лежить простий принцип: кожному пикселу кадру відповідає спеціальна величина, звана завглибшки.

При включенні цього режиму виведення примітиву приводить до заповнення не тільки буфера кадру, але і Z-буфера: у нього записуються значення «глибини» (ступені віддаленості) відповідних крапок. Крапка виводиться тільки за умови, що вона ближче поточного значення в Z-буфері, при цьому Z-буфер заповнюється новим значенням.

Для включення буфера глибини при ініціалізації відеорежиму необхідно заповнити додатково два поля структури D3DPRESENT_PARAMETERS:

params.EnableAutoDepthStencil = TRUE;

params.AutoDepthStencilFormat = D3DFMT_D16;

Перше поле вирішує використання Z-буфера, друге визначає його формат (в даному випадку, для кожного пиксела зберігатиметься 16-бітове значення глибини).

Перед виведенням кожного кадру на екран необхідно очищати буфер глибини, інакше старі значення в ньому заважатимуть висновку нових кадрів. У цьому нам допоможе вже знайомий метод Clear з використанням прапора D3DCLEAR_ZBUFFER. Як третій параметр потрібно передати в цей метод значення 1.0 (максимальна глибина). Крім того, необхідно включити перевірку Z test і вирішити запис в буфер глибини. От як виглядатиме почало методу Render:

pDevice->Clear( 0, NULL

D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,

D3DCOLOR_XRGB(0,0,128), 1.0f, 0 );

pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

pDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);

Після компіляції програми елементи об'єкту, що перекриваються, будуть отрисовываться коректно (рис 3.12).

Рис 3.12 Правильне зображення (після включення Z-буфера)

У C#-программе рендеринг вже і так проводиться безперервно, досить додати код повороту і включення Z-буфера. Щоб не повторюватися, привнесемо невелику різноманітність: чайник в нашій програмі буде не тільки обертатися, але і переміщатися по осі Z.

У лістингу приведений повний текст модифікованої програми:

using System;

using System.Windows.Forms;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

class TestForm: Form

{

static void Main()

{

TestForm form = new TestForm();

form.Text = "Direct3D для .NET";

form.Width = 400;

form.Height = 300;

form.InitD3D();

form.Show();

while(form.Created)

{

form.Render();

Application.DoEvents();

}

}

Device device;

Mesh teapot;

public void InitD3D()

{

try

{

PresentParameters parameters = new PresentParameters();

parameters.Windowed = true;

parameters.SwapEffect = SwapEffect.Discard;

parameters.EnableAutoDepthStencil = true;

parameters.AutoDepthStencilFormat = DepthFormat.D16;

device = new Device(0, DeviceType.Hardware, this

CreateFlags.SoftwareVertexProcessing, parameters);

teapot = Mesh.Teapot(device);

}

catch(Exception e)

{

MessageBox.Show(e.Message);

return;

}

}

public void Render()

{

try

{

device.Clear(ClearFlags.Target|ClearFlags.ZBuffer,

System.Drawing.Color.Blue, 1.0f, 0);

device.RenderState.ZBufferEnable = true;

device.RenderState.ZBufferWriteEnable = true;

device.BeginScene();

device.RenderState.Lighting = true;

device.Lights[0].Type = LightType.Directional;

device.Lights[0].Direction = new Vector3(7 -2, 1);

device.Lights[0].Diffuse = System.Drawing.Color.Yellow;

device.Lights[0].Commit();

device.Lights[0].Enabled = true;

Material material = new Material();

material.Ambient = System.Drawing.Color.White;

material.Diffuse = System.Drawing.Color.White;

material.Specular = System.Drawing.Color.White;

device.Material = material;

Matrix matrix = new Matrix();

matrix.Scale(0.7f, 0.7f, 0.7f);

matrix *= Matrix.RotationY(

System.Environment.TickCount / 300.0f );

float move = (float)Math.Sin(

System.Environment.TickCount/2000.0f)*4+6;

matrix.M43 = move;

Text = move.ToString("##.##");

device.RenderState.Clipping = false;

device.Transform.World = matrix;

device.Transform.Projection = Matrix.PerspectiveFovLH(

(float)Math.PI / 4, 1.0f, 1.0f, 100.0f );

teapot.DrawSubset(0);

device.EndScene();

device.Present();

}

catch(Exception e)

{

return;

}

}

}

3.5.6 Видалення нелицьових граней

Інший спосіб оптимізації виведення складних сцен полягає в діленні всіх примітивів об'єкту на «обернені лицем до спостерігача» і «обернені особою від спостерігача». Якщо об'єкт не містить «дірок», то при будь-якому його положенні завжди видно тільки лицьові грані. Отже, відмовившись від виведення нелицьових примітивів, можна заощадити приблизно 50% часу на побудову сцени. Цей спосіб відсікання називається cull (англ. «отбраковка»).

У Direct3D режим cull включений за умовчанням. Лицьовими вважаються трикутники, вершини яких описуються проти напряму годинникової стрілки (на їх екранній проекції, зрозуміло). Як вже було показано, бібліотека D3DX створює об'єкти з урахуванням цього режиму. Якщо ж встановити зворотний режим відсікання, то малюватимуться тільки внутрішні порожнини об'єкту, що приведе до отримання досить химерного зображення (рис 3.13).

Рис 3.13 Зображення з оберненими лицьовими гранями

Для установки такого режиму Cull в програмі на C++ використовується метод SetRenderState:

pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);

А в програмі на C# застосовується наступний синтаксис:

device.RenderState.CullMode = Cull.Clockwise;

Якщо створений об'єкт містить «дірки» або напівпрозорі ділянки, крізь них повинні бути видно нелицьові грані. Для цього необхідне відключення режиму cull (використовуйте, відповідно, константи D3DCULL_NONE і Cull.None).

4. Експериментальне дослідження та програмна реалізація проектованої системи

4.1 Програмна реалізація та опис основних процедур і функцій розробленої системи

4.1.1 Створення віконного додатку

Запускаємо інтегроване середовище розробки Microsoft Visual Studio .NET 2008. Вибераєм пункт меню File/New/New Project.

Рис. 4.1 Інтегроване середовище розробки Microsoft Visual Studio .NET 2008

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

Рис. 4.2 Діалогове вікно

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

Рис. 4.3 Пункт головного меню Build/Build Solution

Збірка повинна пройти без помилок. Для виконання програми выбераем пункт меню Debug/Start Without Debugging або натискаємо Ctrl+F5.

Рис. 4.4 Пункт меню Debug/Start Without Debugging

На екрані з'явиться вікно нашого додатку.

Рис. 4.5 Вікно додатку

4.4.2 Настройка властивостей вікна

У вікні Solution Explorer відображається список файлів проекту. Початковий текст на мові C#, відноситься до головного вікна додатку знаходиться у файлі Form1.cs. Натиснення над цим файлом правої кнопки миші приводить до появи контекстного меню, в якому присутні пункти View Code і View Designer, що визначають відображення у вікні редактора або початкового тексту або зовнішнього вигляду вікна.

Рис. 4.6 Вікно Solution Explorer

Натиснемо праву кнопку миші у вільній області редактора зовнішнього вигляду вікна і в контекстному меню, що з'явилося, обираємо пункт Properties.

Рис. 4.7 Пункт Properties

На екрані з'явиться вікно редактора властивостей форми. Переконуємося, що третя зліва кнопка, що відображає список властивостей, натиснута.

Рис. 4.8 Список властивостей

У властивостях вікна (форми) змінюємо властивість Text, задаючи заголовок вікна (наприклад, на «Додаток Direct3D»). У властивості Icon указуємо шлях до файлу піктограми ($LabFiles$\directx.ico). Тепер вікно додатку повинне виглядати таким чином.

Рис. 4.9 Вікно додатку

4.4.3 Створення пристрою Direct3D для роботи з тривимірною графікою.

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

Рис. 4.10 Вікно Solution Explorer

У вікні, що з'явилося, за допомогою кнопки Browse додамо в проект перераховані вище складки з каталога $LabFiles$\DirectX Assemblies.

Рис. 4.11 Вікно Add References

Пристрій Direct3D зручно створювати при першому показі вікна на екрані. Створюємо обробник події Load. Для цього у вікні властивостей вікна (форми) обираємо список подій. Для цього нажмаем кнопку із зображенням блискавки. У списку подій двічі нажмаем ліву кнопку миші на подію Load.

Рис. 4.12 Створюємо обробник події Load

Буде створений метод з ім'ям Form1_Load, код якого відразу ж відобразиться в редакторі.

До цього моменту весь код створювала за нас Visual Studio, тепер код доведеться писати самим . Значну допомогу в наборі довгих ідентифікаторів надає технологія Intellisense, що пропонує вибір методів, властивостей або полів даних об'єкту після набору символу «крапка»

На самому початку початкового тексту додаємо дві директиви using. Це дозволить звертатися до класів DirectX не указуючи кожного разу довгих префіксів просторів імен.

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

У класі Form1 додаємо поле даних типу Microsoft.Direct3D.Device для зберігання посилання на пристрій Direct3D.

public class Form1 : System.Windows.Forms.Form { .

Device d3d = null; // Пристрій для відображення 3D-графики

. }

У метод 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(); // Закриваємо вікно }

}

Хорошим тоном в програмуванні вважається своєчасне звільнення всіх раніше одержаних ресурсів. Звільнення пам'яті бере на себе автоматичний складальник сміття, а звільнення ресурсів Direct3D треба вказати явно. Для цього додаємо наступний код у вже існуючий віртуальний метод Dispose(bool disposing) класу Form1:

protected override void Dispose( bool disposing ){ if(disposing){

if(components != null){

components.Dispose(); }

// Звільняємо зайняті раніше ресурси if(d3d != null) d3d.Dispose();

} base.Dispose(disposing);

}

Тепер проект можна зібрати і запустити. Зовнішній вигляд вікна не зміниться, але малювання тривимірних об'єктів у вікні можна буде виконувати, викликаючи методи об'єкту d3d.

4.4.4 Додавання коду для створення зображень

У вікні властивостей выбераем список подій класу Form1 і додаємо обробник події Paint таким самим чином як і обробник події Load.

Рис. 4.13 Обробник події Paint

У метод 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.14 Вікно додатку

4.4.5 Створення тривимірного об'єкту

У конструкторі класу Form1 встановлюємо режим перемальовування вмісту вікна при будь-якій зміні розміру

public Form1() { .

InitializeComponent();

SetStyle(ControlStyles.ResizeRedraw,true);

У клас Form1 додаємо поле даних teapot типу Mesh для зберігання посилання на полігональний об'єкт.

сlass Form1 { .

Mesh teapot = null; // Модель книги

У метод Form1_Load додаємо код, що створює полігональну модель книги.

private void Form1_Load(object sender, System.EventArgs e) .

d3d = new Device(.);

// Створюємо модель і задаємо її властивості

teapot = Mesh.Teapot(d3d);

У метод Dispose додаємо код, що звільняє зайняті для полігональної моделі ресурси Direct3D.

protected override void Dispose(bool disposing){ .

if(teapot != null) teapot.Dispose();

if(d3d != null)d3d.Dispose();

У метод 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 від спостерігача

d3d.Transform.World = Matrix.Translation(0,0,3.5f);

// Малюємо книгу

teapot.DrawSubset(0);

// Завершуємо отрисовку кадру

d3d.EndScene();

d3d.Present(); }

Після виконання цих дій у вікні повинен з'явиться силует книги.

Рис. 4.15 Силует книги

4.4.6 Джерела освітлення

Для підкреслення тривимірності об'єкту можна включити джерело освітлення. У клас Form1 додаємо поле даних teapotMaterial типу Material для зберігання властивостей поверхні книги.

сlass Form1 { .

Mesh teapot = null; // Модель книги

Material teapotMaterial; // Матеріал

. }

У метод 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 не потрібне.

У методі Form1_Paint задаємо положення і властивості одного джерела освітлення. Перед малюванням книги встановлюємо матеріал для розрахунку освітлення полігональної моделі.

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { .

d3d.BeginScene();

// Встановлюємо параметри джерела освітлення

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); . }

Після виконання даних дій модель книги набуває об'ємного вигляду.

Рис. 4.16 Книга набуває об'ємного вигляду

4.4.7 Зміна точки зору на об'єкт

На цьому кроці до тривимірної сцени буде додана анімація. Спершу необхідно організувати постійне перемальовування вікна. Для цього можна скористатися механізмом фонової обробки (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);

}

Якщо тепер зібрати і запустити проект, то в головному вікні спостерігатиметься неприємне мерехтіння. Це пов'язано з взаємодією подвійної буферизації Direct3D і отрисовки фону вікна. Щоб позбавитися цього неприємного ефекту додаємо в конструктор класу Form1 код для установки відповідного режиму перемальовування вікна.

public Form1() { .

SetStyle(ControlStyles.ResizeRedraw,true);

// Встановлюємо режим оновлення вікна

SetStyle(ControlStyles.Opaque,true);

SetStyle(ControlStyles.UserPaint,true); SetStyle(ControlStyles.AllPaintingInWmPaint,true);

}

Для того, щоб швидкість анімації не залежала від продуктивності комп'ютера, запам'ятаємо час почала анімації в полі даних класу 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;

. }

Для обертання книги досить задавати матрицю перетворення світових координат залежно від значення змінної totalSeconds. Заводимо в класі Form1 дві константи типу double, визначальні частоту обертання (оборотов/c) книги навколо осей OX і OY.

сlass Form1 {

const double TeapotRotationX = 0.2;

const double TeapotRotationY = 0.3;

}

Змінюємо в методі Form1_Paint код для обчислення матриці перетворення світових координат, як твори перетворень обертання і перенесення. Перед малюванням книги можна відключити відсікання нелицьових граней, встановивши властивість d3d.RenderState.CullMode рівним Сull.None.


Подобные документы

  • Аналіз особливостей конвертації файлів графічних форматів з використанням технології dotNet і створення системи, яка дозволяє наочно проілюструвати принципи програмування з використанням особливостей цієї платформи. Етапи створення windows-додатків.

    дипломная работа [3,1 M], добавлен 22.10.2012

  • Розробка гнучкої інтегрованої автоматизованої системи доступу до каталогу навчальних відеофільмів в мультимедійних класах металургійного факультету Національної металургійної академії. Теоретичні аспекти проектування додатків на базі платформи dotNET.

    дипломная работа [4,0 M], добавлен 26.10.2012

  • Призначення і основні характеристики систем автоматизації конструкторської документації. Основні методи створення графічних зображень і геометричних об’єктів. Методи побудови та візуалізація тривимірних об’єктів. Опис інтерфейсу користувача системи.

    дипломная работа [1,7 M], добавлен 26.10.2012

  • Дослідження інструментальних засобів для створення систем спільного навчання. Створення Windows-додатків на основі Visual C#. Функціональні можливості та програмна реалізація системи інтерактивної взаємодії. Програмна реалізація модулю прийому зображення.

    дипломная работа [4,5 M], добавлен 22.10.2012

  • Визначення принципів розробки додатків для Windows 8 засобами об'єктно-орієнтованого програмування. Розробка програмного застосування для перегляду графічних файлів з функціями здобуття інформації про слайд-шоу. Інтерфейс користувача та лістинг програми.

    курсовая работа [2,8 M], добавлен 23.10.2014

  • Розробка та тестування додатків, які базуються на елементах мови програмування Java, принципи програмування в її середовищі. Вивчення переваг Java-платформи, прикладний програмний інтерфейс та особливості сучасних засобів створення Java-додатків.

    дипломная работа [2,8 M], добавлен 22.06.2011

  • Огляд існуючих типів додатків, їх переваг та недоліків, принципів створення. HTML — стандартна мова розмітки документів для Web. Загальнi вiдомостi про Ajax. Мова JavaScript, проблема з налагодженням сценаріїв. Динамічне створення Flash-анімації.

    дипломная работа [868,8 K], добавлен 23.04.2011

  • Найбільш розповсюджені середовища створення графічних зображень та 3D моделей. Основні інструменти векторних редакторів. Функції програм Adobe Photoshop и Корелдроу. Графічні моделі, характеристики й типи графічних файлів. Створення власних моделей.

    дипломная работа [6,7 M], добавлен 25.06.2011

  • Android, iOS та Windows як основні платформи для розробки додатків для мобільних пристроїв. Перелік вимог до програмної системи. Основні вимоги, які є критичними для працездатності мобільного додатку. Аналіз основних напрямків розвитку системи.

    курсовая работа [1,1 M], добавлен 19.08.2016

  • Сучасні API для програмування тривимірної графіки, математичні основи. Віртуальна камера, конвеєр візуалізації. Вершинні та піксельні шейдери. Розробка та реалізація ігрового додатку. Система постобробки зображення. Реалізація механіки ігрового процесу.

    дипломная работа [4,7 M], добавлен 27.06.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.