Direct3D въведение. Урок 1.

В настоящия урок ще направим пътешествие в бездната на DirectX съсредоточавайки се върху COM обекта Direct3D съдържащ всичко необходимо за двумерна и тримерна графика. Ще разгледаме следните теми:
•    Какво е COM
•    Двойно буфериране и Page Flipping
•    Създаване на базовата програма
•    Създаване на проект

Какво е COM.
Този въпрос е по лесен за задаване от колкото за отговаряне. COM е мощна технология за интегриране, която ви позволява да смесвате коренно различни технологии по време на изпълнение. COM позволява на разработчиците да пишат софтуер който работи съвместно, независимо от програмния език, нишките и т.н..
    COM e протокол който свързва един софтуерен модул с друг. След като осъществи връзката между двата модула те могат да комуникират посредством механизъм наречен интерфейс. Интерфейсите не изискват статично или динамично свързани входни точки ни фиксирани кодирани адреси, освен няколко COM функции с общо предназначение който стартират комуникационния процес.
    DirectX предлага множество от COM обекти който се грижат за 2D, 3D графиката, входа от мишката и клавиатурата, звука и т.н.

Двойно буфериране и Page Flipping.
Двойно буфериране е метод, използван за визуализиране на графика, чрез който се отстранява премигването, получаващо се, когато обект се рисува или премества.

Методите които рисуват обекти на екрана се изпълняват без да вземат в предвид в какво състояние се намира той. Те рисуват и изтриват изображения без да се синхронизират. При двойното буфериране цялата видима област се рисува в буфер от RAM(с размер на видео буфера) паметта и след това се копира наведнъж във видео буфера. По този начин се минимизира възможността от смесване на различни изображения и операцията по прехвърляне на данни е много бърза. Недостатък при използването на дублиращ буфер е, че за него е нужно да се заделя допълнително памет. Не винаги е необходимо да се подържа втори буфер за целия екран. Ако областта на визуализация е само 100 пиксела висока и 100 пиксела широка дублиращия буфер ще бъде с размер 100x100. Взимайки всичко това под внимание използването на дублиращ буфер обикновено е по бързо, от колкото директния достъп до видео паметта. Получената анимация и графика ще бъде далеч по-съвършена. 
    Смяната на видео страницата(Page Flipping) е модификация на двойното буфериране. При този метод се използват две видео страници. Когато едната е визуализирана другата се прерисува. Обновената страница се визуализира посредством насочването на видео указателя към нея. Получава се мигновенна визуализация.


 


За да бъде постигната по добра производителност DirectX поддържа множество буфери. Допълнителните буфери дават възможност на приложението да продължава да рисува обектите в следващия буфер без да е необходимо да изчаква завършването на прехвърлянето на изображението на екрана. Този способ е известен като “Swap chain”.

Създаване на базовата програма
    Нашата първа програма използваща DirectX ще създаде прозорец и ще го запълни със син цвят. За тази цел трябва да изпълним следните четери стъпки:
1.    Създаване на глобалните променливи и прототипи
2.    Създаване на функция инициализираща Direct3D
3.    Създаване на функция рисуваща кадрите
4.    Създаване на функция за освобождаване на Direct3D

1.    Създаване на глобалните променливи и прототипи
Необходимо е дадобавим няколко реда в началото на нашата програма преди да започнем да използваме Direct3D. Нека прегледаме кода по долу и видим какво прави той.

  1.  
  2. // включваме основните windows хедър
  3. // файлове и хедър фаила на Direct3D
  4. #include <windows.h>
  5. #include <windowsx.h>
  6. #include <d3d9.h>
  7.  
  8. #pragma comment (lib, "d3d9.lib")
  9.  
  10. //глобални променливи
  11. // указател към нашия Direct3D интерфейс
  12. LPDIRECT3D9 d3d;   
  13. // указател към device класа
  14. LPDIRECT3DDEVICE9 d3ddev;   
  15.  
  16. //прототипи на функции
  17. // установява и инициализира Direct3D
  18. void initD3D(HWND hWnd);   
  19. // изчертава един кадър
  20. void render_frame(void);   
  21. // затваря Direct3D и освобождава паметта
  22. void cleanD3D(void);   
  23.  
  24. // прототип на WindowProc функцията
  25. LRESULT CALLBACK WindowProc(HWND hWnd,
  26.                             UINT message,
  27.                             WPARAM wParam,
  28.                             LPARAM lParam);
  29.  
#include <d3d9.h>
Този ред включва в програмата Direct3D хедър файла. Той съдържа множество декларации на функции съдържащи се в Direct3D библиотеката.

#pragma comment (lib, "d3d9.lib")
Включва Direct3D библиотеката. Препроцесорната команда #pragma оказва с първия си параметър lib че искаме да добавим в проекта библиотека “d3d9.lib”.

LPDIRECT3D9 d3d;
Тази променлива е указател към Direct3D интерфейса. Това означава че ще бъде създаден обект от тип iDirect3D9 и той ще бъде достъпван само през d3d указателя.

LPDIRECT3DDEVICE9 d3ddev;
Direct3D Device интерфейса държи цялата информация за графичните драйвъри, видео къртата и всичко друго свързано с графичния хардуер. d3d9dev е указател към такъв обект който държи цялата информация.

2.    Създаване на функция инициализираща Direct3D
Първата стъпка за създаване на Direct3D e да се създаде интерфеис и да се инициализира графичното устройство. Ще изпълним тези две стъпки използвайки две функции и структура държаща информацията за графичното устройство. Разгледайте функцията по долу:
  1. // тази функция инициализира и подготвя
  2. // Direct3D за употреба
  3. void initD3D(HWND hWnd)
  4. {
  5.     // създава Direct3D интерфаис
  6.     d3d = Direct3DCreate9(D3D_SDK_VERSION);   
  7.  
  8.     // създава структура която пази
  9.     // информацията за устройството
  10.     D3DPRESENT_PARAMETERS d3dpp;   
  11.  
  12.     // нулира структурата и я подготвя за употреба
  13.     ZeroMemory(&d3dpp, sizeof(d3dpp));
  14.     // програмата се изпълнява в прозорец
  15.     d3dpp.Windowed = TRUE;   
  16.     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  17.     // задава прозореца който ще се използва за Direct3D
  18.     d3dpp.hDeviceWindow = hWnd;   
  19.  
  20.  
  21.     // създава устройство използвайки тази
  22.     // информация и информацията от d3dpp структурата
  23.     d3d->CreateDevice(D3DADAPTER_DEFAULT,
  24.                       D3DDEVTYPE_HAL,
  25.                       hWnd,
  26.                       D3DCREATE_SOFTWARE_VERTEXPROCESSING,
  27.                       &d3dpp,
  28.                       &d3ddev);
  29. }
Ако коментарите във функцията са ви достатъчни може да продължите към следващата точка. В противен случай прегледайте описанието на командите по долу.

d3d = Direct3DCreate9(D3D_SDK_VERSION);
Това е първата Direct3D функция която трябва да бъде извикана. Неиното предназначение е да създаде Direct3D интерфейс. Връщаната стойност е адреса на създадения интерфейс. Той ще бъде запазен в променливата d3d декларирана по горе в кода.

D3DPRESENT_PARAMETERS d3dpp;
D3DPRESENT_PARAMETERS е структура чиито член променливи съдържат инхормация за графичното устройство. Ще разгледаме три от член променливите който се използват в настоящото приложение.

ZeroMemory(&d3dpp, sizeof(d3dpp));
Използваме функцията ZeroMemory за да инициализираме бързо структурата d3dpp изцяло с NULL. Използвайки тази функция не ни се налага да инициализираме всяка член променлива по отделно.

d3dpp.Windowed = TRUE;
Когато пускаме приложението в прозорец стойноста на тази член променлива трябва да бъде TRUE. Ако искаме приложението да върве на цял екран то стойноста трябва да бъде FALSE.

d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

Direct3D подържа различни механизми за Swap Cain. За повече информация вижте в документацията на Direct3D.

d3dpp.hDeviceWindow = hWnd;
Тази член променлива указва прозореца който ще бъде използван от Direct3D.

d3d->CreateDevice()
Функцията ще създаде нов графичен обект през който ще имате достъп до всичките графични възможности на вашата видео карта. Първия параметър указва че искаме да използваме видео картата по подразбиране. Втория параметър указва на Direct3D че искаме хардуера да обработва нашата графика. Третия параметър е указател към нашия прозорец. Петия и шестия параметър са съответно указател към d3dpp структурата държаща информация за графичното устройство и указател към указател в който ще бъде записан адреса на интерфейса към графичното устройство.

3.    Създаване на функция рисуваща кадрите
Чрез тази функция ще изчертаем един кадър. Кадъра е доста опростен и съдържа единственно син фон. Можете да промените цвета на фона ако желаете. Кода на функцията е даден по долу.
  1. void render_frame(void)
  2. {
  3.     // изчиства прозореца и го запълва със син цвят
  4.     d3ddev->Clear(0, NULL, D3DCLEAR_TARGET,
  5.                   D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
  6.  
  7.     // маркира започването на изчертаването на сцената
  8.     d3ddev->BeginScene();   
  9.  
  10.     //тук трябва да бъде извършено
  11.     //изчертаването на обектите в сцената
  12.  
  13.     // маркира края на изчертаването на сцената
  14.     d3ddev->EndScene();   
  15.  
  16.     // визуализира създадената сцена на екрана
  17.     d3ddev->Present(NULL, NULL, NULL, NULL);  
  18. }

d3ddev->Clear()
Функцията истрива(запълва) буфера с определен цвят.Първите два параметъра на функцията задават размера на масива и самия масив от правоъгалници които да бъдат изтрити. В много случеи изкаме да изтрием целята повърхност. Това можем да направим лесно като зададем 0 за дължина на масива и NULL за масива. Третия параметър със стоиност D3DCLEAR_TARGET указва че ще бъде изтрит задния буфер. Следващия параметър оказва цвета. Последните два параметъра ще разгледаме в следващите уроци.

d3ddev->BeginScene()
Функцията BeginScene() указва на Direct3D че сме готови да започнем изчертаването. Тази функция трябва да бъде извикана поради две причини. Първо необходимо е да се укаже на Direct3D че ще достъпваме паметта. Второ буфера в паметта ще бъде заключен гарантираики ни ексклузивен достъп до него.

d3ddev->EndScene()
Следва извикването на функцията EndScene която отключва преди това заключения от BeginScene буфер правейки го достъпен за останалите процеси който имат нужда от него. Отключването и заключването на графичния буфер е бавно но зъдължитилно. Препоръчително е да се направи само веднъж за всеки кадър.

d3ddev->Present()
Накрая извикваме функцията Present  която визуализира кадъра на екрана.

4.    Създаване на функция за освобождаване на Direct3D
Това е последната стъпка от създаването на Direct3D програма.
  1. // това е функцията която
  2. // освобождава заетите от Direct3D и COM ресурси
  3. void cleanD3D(void)
  4. {
  5.     // затваря и освобождава 3D устройството
  6.     d3ddev->Release();
  7.     // затваря и освобождава Direct3D
  8.     d3d->Release();   
  9. }
Тук извикваме Release функцията за всеки от създадените интерфейси d3ddev и d3d. Няма параметри просто изчистване на паметта.

Създаване на проект
1.    Във Visual C++ създайте нов Win32 Project.

  • File > New > Project
  • От Projects tab изберете Win32 Project
  • Напишете име на проекта си, например "Direct3D Project"
  • Натиснете Next
  • От Application Settings tab-а изберете Windows Application и Empty Project
  • Натиснете Finish

2.    Настройте проекта си.

  • Project > Properties...
  • В категорията Linker изберете Input.
  • В Additional Dependencies добавете "d3d9.lib"

3.    Ако сте инсталирали правилно DirectX SDK-то не би трябвало да имате проблем със директориите, но все пак проверете:

  • Tools > Options
  • От категорията Projects изберете VC++ Directories
  • В Show Directories for менюто вижте дали в Include files има директорията, в която сте инсталирали DirectX SDK. Ако я няма - добавете я, като от нея изберете поддиректория include.
  • Направете същото и за и Library Files, но този път ако я няма изберете поддиректория libs

4.    Добавяме source code-а.

  • File > New > File
  • От Files tab-а, изберете Visual C++ категорията и C++ File иконката.
  • Напишете име, например "Main.cpp"
  • Копирайте сегмента код долу и го сложете в новосъздадения файл.

5.    Build-ване и Стартиране на програмата.

  • Натиснете F7, за да build-нете проекта си.
  • Натиснете F5, за да го стартирате.

  1. // включваме основните windows хедър
  2. // файлове и хедър фаила на Direct3D
  3. #include <windows.h>
  4. #include <windowsx.h>
  5. #include <d3d9.h>
  6.  
  7. #pragma comment (lib, "d3d9.lib")
  8.  
  9. //глобални променливи
  10. // указател към нашия Direct3D интерфейс
  11. LPDIRECT3D9 d3d;   
  12. // указател към device класа
  13. LPDIRECT3DDEVICE9 d3ddev;   
  14.  
  15. //прототипи на функции
  16. // установява и инициализира Direct3D
  17. void initD3D(HWND hWnd);   
  18. // изчертава един кадър
  19. void render_frame(void);   
  20. // затваря Direct3D и освобождава паметта
  21. void cleanD3D(void);   
  22.  
  23. // прототип на WindowProc функцията
  24. LRESULT CALLBACK WindowProc(HWND hWnd,
  25.                             UINT message,
  26.                             WPARAM wParam,
  27.                             LPARAM lParam);
  28.  
  29.  
  30. // входната точка на Win32 програмите
  31. int WINAPI WinMain(HINSTANCE hInstance,
  32.                    HINSTANCE hPrevInstance,
  33.                    LPSTR lpCmdLine,
  34.                    int nCmdShow)
  35. {
  36.     HWND hWnd;
  37.     WNDCLASSEX wc;
  38.  
  39.     ZeroMemory(&wc, sizeof(WNDCLASSEX));
  40.  
  41.     wc.cbSize = sizeof(WNDCLASSEX);
  42.     wc.style = CS_HREDRAW | CS_VREDRAW;
  43.     wc.lpfnWndProc = WindowProc;
  44.     wc.hInstance = hInstance;
  45.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  46.     wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  47.     wc.lpszClassName = "WindowClass";
  48.  
  49.     RegisterClassEx(&wc);
  50.  
  51.     hWnd = CreateWindowEx(NULL,
  52.                           "WindowClass",
  53.                           "Direct3D",
  54.                           WS_OVERLAPPEDWINDOW,
  55.                           300, 300,
  56.                           800, 600,
  57.                           NULL,
  58.                           NULL,
  59.                           hInstance,
  60.                           NULL);
  61.  
  62.     ShowWindow(hWnd, nCmdShow);
  63.  
  64.     // инициализиране на Direct3D
  65.     initD3D(hWnd);
  66.  
  67.     // начало на основния цикъл:
  68.  
  69.     MSG msg;
  70.  
  71.     while(TRUE)
  72.     {
  73.         while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  74.         {
  75.             TranslateMessage(&msg);
  76.             DispatchMessage(&msg);
  77.         }
  78.  
  79.         if(msg.message == WM_QUIT)
  80.             break;
  81.  
  82.         render_frame();
  83.     }
  84.  
  85.     // изтриване на DirectX и COM
  86.     cleanD3D();
  87.  
  88.     return msg.wParam;
  89. }
  90.  
  91.  
  92. // тази функция прихваща съобщенията към програмата
  93. LRESULT CALLBACK WindowProc(HWND hWnd,
  94.                             UINT message,
  95.                             WPARAM wParam,
  96.                             LPARAM lParam)
  97. {
  98.     switch(message)
  99.     {
  100.         case WM_DESTROY:
  101.             {
  102.                 PostQuitMessage(0);
  103.                 return 0;
  104.             } break;
  105.     }
  106.  
  107.     return DefWindowProc (hWnd, message, wParam, lParam);
  108. }
  109.  
  110.  
  111. // тази функция инициализира и подготвя
  112. // Direct3D за употреба
  113. void initD3D(HWND hWnd)
  114. {
  115.     // създава Direct3D интерфаис
  116.     d3d = Direct3DCreate9(D3D_SDK_VERSION);   
  117.  
  118.     // създава структура която пази
  119.     // информацията за устройството
  120.     D3DPRESENT_PARAMETERS d3dpp;   
  121.  
  122.     // нулира структурата и я подготвя за употреба
  123.     ZeroMemory(&d3dpp, sizeof(d3dpp));
  124.     // програмата се изпълнява в прозорец
  125.     d3dpp.Windowed = TRUE;   
  126.     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  127.     // задава прозореца който ще се използва за Direct3D
  128.     d3dpp.hDeviceWindow = hWnd;   
  129.  
  130.  
  131.     // създава устройство използвайки тази
  132.     // информация и информацията от d3dpp структурата
  133.     d3d->CreateDevice(D3DADAPTER_DEFAULT,
  134.                       D3DDEVTYPE_HAL,
  135.                       hWnd,
  136.                       D3DCREATE_SOFTWARE_VERTEXPROCESSING,
  137.                       &d3dpp,
  138.                       &d3ddev);
  139. }
  140.  
  141.  
  142. // тази функция изчертава един кадър на екрана
  143. void render_frame(void)
  144. {
  145.     // изчиства прозореца и го запълва със син цвят
  146.     d3ddev->Clear(0, NULL, D3DCLEAR_TARGET,
  147.                   D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
  148.  
  149.     // маркира започването на изчертаването на сцената
  150.     d3ddev->BeginScene();   
  151.  
  152.     //тук трябва да бъде извършено
  153.     //изчертаването на обектите в сцената
  154.  
  155.     // маркира края на изчертаването на сцената
  156.     d3ddev->EndScene();   
  157.  
  158.     // визуализира създадената сцена на екрана
  159.     d3ddev->Present(NULL, NULL, NULL, NULL);  
  160. }
  161.  
  162.  
  163. // това е функцията която
  164. // освобождава заетите от Direct3D и COM ресурси
  165. void cleanD3D(void)
  166. {
  167.     // затваря и освобождава 3D устройството
  168.     d3ddev->Release();
  169.     // затваря и освобождава Direct3D
  170.     d3d->Release();   
  171. }


 

Автор: Сергей Георгиев [adviser@cpp-examples.com]