використається макрос FAILED(), що перевіряє код на наявність збою. Працює за схемою:
if(FAILED(...))
{ збій }
else
{ функція виконана успішно }
також можна використати макрос SUCCEEDED, за такою схемою
if(SUCCEEDED(...))
{ функція виконана успішно }
else
{ збій }
Тепер потрібно одержати поточні параметри дисплея за допомогою функції GetAdapterDisplayMode().
Створюємо структуру параметрів подання D3DPRESENT_PARAMETERS Direct3DParametr і задаємо значення:
Direct3DParametr.Windowed=TRUE; - віконний режим роботи додатка.
Direct3DParametr.SwapEffect=D3DSWAPEFFECT_DISCARD; - не використати буфери обміну.
Direct3DParametr.BackBufferFormat=stDisplay.Format; - формат відеорежиму заднього буфера встановлюємо рівним поточному відеорежиму.
Створюємо об'єкт інтерфейсу CreateDevice. Всі подальші дії будуть опиратися на даний інтерфейс пристрою і його параметри.
D3DADAPTER_DEFAULT - поточна відеокарта (звичайно в системі встановлена 1 відеокарта).
D3DDEVTYPE_HAL - вибір способу обробки інформації, за допомогою відеокарти.
hWnd - дескриптор головного вікна.
D3DCREATE_HARDWARE_VERTEXPROCESSING - спосіб обробки вершин (у цьому випадку апаратно). Можна використати значення D3DCREATE_SOFTWARE_VERTEXPROCESSING - програмна обробка вершин (при використанні старих відеокарт).
&Direct3DParametr - параметри відеорежиму.
&pDirectDevice - результат операції, покажчик на тільки що створений об'єкт.
3.5 Функція для рендерінгу сцени
У цій частині коду відбувається вивід зображення на екран
void RenderScene(void)
{
if(pDirectDevice==NULL)
return;
pDirectDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 192, 0), 1.0f, 0);
pDirectDevice->BeginScene();
pDirectDevice->EndScene();
pDirectDevice->Present(NULL, NULL, NULL, NULL);
}
Спочатку необхідно очистити задній буфер і заповнити весь простір одним кольором:
Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 192, 0), 1.0f, 0)
Створення сцени відбувається між рядками:
pDirectDevice->BeginScene();
pDirectDevice->EndScene();
Все що в цьому блоці, прорисовується в задньому буфері.
Тепер лишилося вивести вміст заднього буфера на екран:
pDirectDevice->Present(NULL, NULL, NULL, NULL)
На даний момент у нас є повністю працездатна оболонка для створення 3D додатка. Все що було пророблено це ініціалізація пристроїв й очищення екрана в темно-зелені кольори.
3.6 Створення об'єкта
Всі об'єкти при програмуванні в 3D складаються із частин. Найпростіша фігура з якої можна скласти будь-яку іншу - це трикутник. Яка б не була у Вас потужна відеокарта, але всі об'єкти в іграх вона будує із трикутників!
Розіб'ємо об’єкт на трикутники, намагаючись максимально зберегти форми, при цьому використати мінімальну кількість трикутників. У цьому завданні потрібно знати міру й пам'ятати: «Чим більше трикутників - тим ГАРНІШЕ виглядає об'єкт, і ДОВШЕ його обробляє комп'ютер». Відразу хочу Вас заспокоїти, при створенні дуже складних об'єктів, які складаються із сотень або тисяч трикутників, вручну вам їх розбивати не прийдеться, для цього є спеціалізовані програми 3DMax, Maya й ін.
Кожен трикутник має крім координат 3-х вершин ще й параметр перетворення і кольори вершин, тому створимо структуру, що буде визначати всі характеристики вершин:
struct CUSTOMVERTEX //формат вершин
{
FLOAT x, y, z, rhw; //координати вершин
DWORD color; //колір вершин
};
За допомогою рядка:
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE) - описується формат змісту вершин в окремому потоці даних. Тут застосовується гнучкий формат вершин - FVF (Flexible Vertex Format). Всі перераховані елементи збігаються з оголошеними в структурі. D3DFVF_DIFFUSE - указує на те, що застосовується колірна складова з розсіяним світлом.
Всю роботу по підготовці буфера будемо робити в окремій функції:
bool InitBufferVertex(void);
Всі вершини будуть зберігатися в буфері вершин. При заповненні буфера даними його необхідно попередньо заблокувати, після заповнення даними - розблокувати.
Визначаємо покажчик на буфер вершин:
LPDIRECT3DVERTEXBUFFER9 pBufferVertex=NULL;
І введемо координати всіх вершин у форматі структури:
координати (Х, Y, Z), параметр перетворення, кольори
наприклад 1 трикутник
170, 80, 0 1 0x00ff0000
210, 60, 0 1 0x00ff0000
250, 80, 0 1 0x00ff0000
Створюємо буфер вершин:
CreateVertexBuffer(21*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &pBufferVertex, NULL) - указуємо розмір, формат і передаємо покажчик, де буде зберігатися інформація.
Перед записом даних у буфер його необхідно заблокувати, а тільки після цього копіювати дані:
if(FAILED(pBufferVertex->Lock(0, sizeof(stVertex), (void**)&pBV, 0)))
return(false);
memcpy(pBV, stVertex, sizeof(stVertex));
pBufferVertex->Unlock(); //розблокувати буфер для подальшої роботи.
3.7 Рендерінг об'єкта
Висновок всіх об'єктів сцени на екран відбувається у функції рендерінга між рядками
pDirectDevice->BeginScene();
pDirectDevice->EndScene();
Заповнимо цей проміжок.
Укажемо, які дані пересилати в потік даних пристрою, також указуємо розмір даних, визначальну одну вершину:
SetStreamSource(0, pBufferVertex, 0, sizeof(CUSTOMVERTEX));
SetFVF(D3DFVF_CUSTOMVERTEX); // задаємо формат вершин
Виводимо об'єкт, для цього використаємо функцію виводу примітивів (примітив - будь-яка геометрична фігура).
Передаємо як параметри такі значення:
DrawPrimitive(D3DPT_TRIANGLELIST, // що малювати
0, // індекс першої вершини
х // кількість виведених об'єктів
);
4 МЕГАТЕКСТУРИЗАЦІЯ
4.1 Проблема мегатекстуризації
Нехай вся геометрія “розгорнута” на дуже великій текстурі, розміром MxМ, і якщо ця текстура влазить у відео пам'ять - ми можемо відмалювати всю геометрію за один виклик (це неефективно з погляду відсікання видимості, але це можливо). Очевидно, цей випадок нам не цікавий, тому як все працює.
А що якщо текстура не влазить у відео пам'ять? В 99% випадках так і буде.
Коріння цієї проблеми йдуть далеко в історію, зводиться все до текстурного менеджера, існують мільйони алгоритмів різних менеджерів текстур. Алгоритм мегатекстури був відомий ще в 98-му році, його представили на SIGRAPH'і Michael E. Goss й Kei Yuasa, правда вони не підозрювали що він так називається.
Отже це звичайний текстурний менеджер, тільки оперуємо не цільними текстурами, а шматочками (тайлами) дуже великої текстури. Потрібно відштовхуватися від ідеї що у відеопам'яті нам потрібні тільки ті шматки текстури (тайли), які ми бачимо на екрані, інше нас не хвилює.
Виходить що в ідеалі потрібна одна текстура розміром з екран (ну добре не одна, а по одній на дифуз, нормалі, спекуляр і т.д.). Зрозуміло що цього не домогтися, є багато факторів - back faces, occlusion, рух камери й т.д. Які можна побороти до прийнятної форми. Назвемо цю текстуру кешем видимих тайлів.
Із цього ще й випливає така річ як лоди (міпи) текстури: Чим далі від камери тали, тим меншого дозволу (у текселях) тали необхідні. Наприклад, це значить що можна об'єднати 4 сусідніх тала на певній відстані від камери (там де вже 1-й лод) в один тайл