OpenGL у середовищі Windows

Представляю вашій увазі дещо з раннього.

Створення програми Win32

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

1) Створення початкового проекту За допомогою Visual Studio це дуже просто: 1. Виберіть у меню File(Файл) пункт New (новий). 2. Серед усього переліку можливих проектів нам знадобиться Win32 додаток. У полі Project Name (назва проекту) введіть Skel (Skeleton – Скелет. Цим ми будемо називати додаток на основі якого будуватимемо всі інші. Це кістяк.) На рис.01 зображено діалог установок.

середовищі
Мал.01 Створення Win32 програми Не забудьте вказати в полі Location (місцезнаходження) розташування проекту. Найкраще відведіть для цього окрему директорію.

вікна

3. Після натискання кнопки Ок (застосувати) з'явиться діалогове вікно із вибором типу нашого проекту. Рис.02. Виберіть Simple Win32 application (простий Win32 додаток).

середовищі

Рис.02 Вибір початкового додатка Просте додаток створить нам головний файл, у якому ми писатимемо програми.

Тепер натисніть Finish (завершити) і на цьому роботу з менеджером проектів можна вважати завершеною.

2) створення головного вікна Якщо ви запустите зараз відкомпілюєте наш додаток (за допомогою команди Build – клавіша F7), а потім запустіть його (за допомогою команди Execute Program – клавіші Ctrl_F5), то нічого не станеться. Це тому, що наша програма зараз порожня, але ми це швидко виправимо. Панелька, розташована зліва, містить інформацію про наш проект, про файли, що використовуються в ньому.Перейдіть до кладки FileList (список файлів) і побачите дерево ієрархій файлів нашого проекту. Знайдіть файл Skel.cpp і відкрийте його. Рис.03. У цьому файлі знаходиться функція WinMain, яка викликається під час запуску нашої програми. Як бачите вона порожня (return 0 – вихід із функції з поверненням 0), саме тому при запуску нашої програми нічого не відбувалося. Тепер все, що ми напишемо туди і буде виконуватись, тож уперед. Наше завдання – побудувати звичайне «віндівське» вікно.

Рис.03 Список фалів проекту та відкритий головний файл – Skel.cpp

вікна

Для створення вікна нам знадобиться: 1. Додамо дві літерні змінні – назву класу нашої програми та заголовок вікна Char * szClassName = OpenGL Application; Char * szWndName = "OpenGL"; 2. Тепер нам потрібно додати ще одну змінну, яка характеризує клас нашого вікна. WNDCLASS wc; wc.style = CS_HREDRAW CS_VREDRAW CS_OWNDC; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = szAppName; Серед різних властивостей, які ми задамо цій змінній, ми повинні вказати і функцію, яку Windows використовуватиме для надсилання нам повідомлень. Wc.lpfnWndProc = (WNDPROC) MsgProc; Тобто. технологія роботи нашої програми буде наступна: створюємо вікно і вказуємо функцію обробки повідомлень вікна, а потім обробляємо різні отримані повідомлення (повідомлення на перемальовку, натискання клавіші і т.д.) Коли ж ми заповнимо всі необхідні нам параметри вікна то клас нашого вікна потрібно зареєструвати за допомогою функції RegisterClass(&wc); 3. Все, клас з такиміменем зареєстрований, тепер можна і створити наше довгоочікуване віконце за допомогою функції CreateWindow( , , , , , , , , , , );

Ця функція поверне нам код вікна. Він нам знадобиться, так що збережемо його в окремій змінній типу HWND HWND hWnd; … hWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOWWS_CLIPCHILDRENWS_CLIPSIBLINGS, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, 6 );

4. Вікно створили, залишилося встановити на нього фокус SetFocus (hWnd); А потім показати його Showindow (hWnd, True) Тут нам і знадобилася змінна коду нашого вікна. 5. Тепер ми готові приймати та обробляти Windows повідомлення: BOOL bGotMsg; MSG msg;

if( bGotMsg ) TranslateMessage( &msg ); DispatchMessage(&msg); > else InvalidateRect( hWnd, NULL, FALSE ); > >

? останнє – додамо до нашої програми нову функцію INT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); Змінна Msg означатиме тип повідомлення, через яке викликається ця функція. Взагалі можливих повідомлень дуже великий перелік, але нам в роботі буде потрібно лише небагато. WM_CREATE - створення WM_DESTROY - знищення WM_PAINT - перемальовка WM_RESIZE - зміна розмірів Щоб перевірити дійсність виклику windows цих повідомлень давайте зробимо так, щоб при їхньому виклику видавалося повідомлення Mess NULL, WM_FormCreate, Message, MB_OK); �?так тіло нашої функції має виглядати так: … switch( uMsg ) case WM_CREATE: MessageBox( NULL, "WM_FormCreate", "Message", MB_OK); return 0;

case WM_CLOSE: MessageBox( NULL, "WM_FormCreate", "Message", MB_OK); DestroyWindow(hWnd); PostQuitMessage(0); return 0; >

return DefWindowProc( hWnd, uMsg, wParam, lParam ); … Ось це я думаю все. Запустіть програму і перед вами має з'явитися звичайне вікно, причому це вікно не повинно мати області малювання (начебто всередині порожньо), т.к. ми її не поставили і вона нам і не знадобиться. У ex01 знаходиться вихідний варіант цієї роботи.

Підключення OpenGL

1) Щоб підключити функції бібліотеки OpenGL на початку програми, впишіть використання заголовка з прототипами цих функцій #include Тепер підключіть саму бібліотеку #pragma comment (lib, «opengl32.lib»)

Розмістимо в ній нову змінну, в якій вкажемо необхідні для нашої роботи параметри: static PIXELFORMATDESCRIPTOR pfd = sizeof(PIXELFORMATDESCRIPTOR), // розмір структури 1, // версія PFD_DRAW_TO_WINDOW // малювання у вікні PFD_SUPPORT_OPENGL // підтримка OpenGL PFD_DOUBLEBUFFER, // режим подвійної буферизації PFD_TYPE_RGBA, // режим RGBA кольору GetDeviceCaps(hDC, BITSPIXEL), // задаємо розмір буфера кольору 0,0,0 ,0,0,0, // не використовуємо 0,0, // не використовуємо 0,0,0,0,0, // не використовуємо 16 // розмір буфера глибини 0, // не використовуємо 0, // не використовуємо PFD_MAIN_PLANE, // висновок на головну площину 0, // не використовуємо 0,0,0 > ;

! ? використовуючи наступний виклик DescribePixelFormat(hDC, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); Ми отримаємо в нашій змінній pfd прапори dwFlags, що містять інформацію про стан системи: Якщо є прапор PFD_NEED_PALETTE, то програма запущена в режимі 256 кольорів. У цьому випадку видати повідомлення про некоректний режим та вийти з програми. (Який сенс продовжувати роботу, якщо все буде не красиво) Якщоє прапор PFD_GENERIC_FORMAT, то додаток буде працювати з підтримкою прискорювача, а інакше програмна емуляція.

Після того, як ми задали формат пікселів, потрібно створити контекст відтворення для роботи з OpenGL. Подібно до того, що контекст пристрою зберігає налаштування GDI, так і контекст відтворення зберігає налаштування OpenGL. Додамо нову змінну, що зберігає значення контексту відтворення HGLRC hGLRC; Тепер можна створити контекст. (функція wglCreateContext поверне його значення) hGLRC = wglCreateContext( hDC ); Коли ми активізуємо контекст відтворення, зможемо використовувати команди OpenGL для малювання. wglMakeCurrent( hDC, hGLRC ); Тобто. схема нашої програми буде наступна:

Пристрій Монітор Відео карта Контекст відтворення додаток

Що ж до малювання, то як ви пам'ятаєте при встановленні формату пікселів, ми вказали режим подвійної буферизації. Це означає, що малювати будемо на поза екранному буфері, а потім поміняємо його з екранним і тим самим отримаємо плавне перемальовування. Тобто додамо до нашої функції обробки повідомлень нове – WM_PAINT (повідомлення на перемальовку). На початку малювання обов'язкове слід очистити буфер кадру, щоб стерти все попереднє намальоване: GlClear ( GL_COLOR_BUFFER_BIT ); Можете тут же вставити команду OpenGL, яка задає колір фону, яким заповнюється буфер після очищення. GlClearColor( 0.5f, 0.5f, 0.5f, 1.0f), де всі 4 параметри - RGBA состовляющие кольори ( я для фону задав сірий колір) Хоча виклик цієї функції не обов'язковий при малюванні кожного кадру, тому помістіть її десь після активації контексту відтворення. Наприкінці малювання – зміна буферів SwapBuffers(hDC); 3) При знищенні програми (повідомленняWM_DESTROY або WM_CLOSE) нам потрібно деактивувати контекст відтворення, видалити його та звільнити контекст пристрою: wglMakeCurrent( 0, 0 ); wglDeleteContext( hGLRC ) ; ReleaseDC(hWnd, hDC); Якщо у вас з'явилося вікно із сірим фоном, то я вас вітаю – ви побудували просте OpenGL додаток. У ex.02 знаходиться цей проект.

LOG – файли

//закриваємо файл - створення успішно завершено fclose (logfile); return true; > Ми відкрили файл та відчистили його. Тепер тільки додаватимемо до нього інформацію. 2) Функція додавання файлу printLOG(char* text, …) va_list arg_list;

//Initialize variable argument list va_start(arg_list, text);

//Open the log file for append if((logfile = fopen("Log.txt", "a+"))==NULL) return false;

//Write the text and a newline vfprintf(logfile, text, arg_list); putc('\n', logfile);

//Close the file fclose(logfile); va_end(arg_list);

return true; > Тут ми відкриваємо в наш файл як додавання. Функція влаштована так, що ви можете форматувати рядок, що задається. Ось приклад використання цієї функції: Output( “Renderer: %s”, glGetString(GL_RENDERER) ); Бачите, функція виведення організована дуже зручно. 3) Додамо процедуру, яку викликатимемо при знищенні вікна: FreeLOG(void) if(logfile) fclose(logfile);

return true; > Файл обов'язково потрібно закривати, щоб потім його можна було переглядати в інших програмах. В ex.04 знаходиться наш проект із використанням LOG файлу.

Підключення розширень

Вертикальна синхронізація

Однак можна зробити нашу роботу із завантаження розширень ще простіше. Для цього можна скористатися, наприклад, бібліотекою GLEW.Її можна завантажити в мережі інтернет, вона вільно розповсюджується. Зручно в тому сенсі, що в ній всі функції вже описані і нам не потрібно завантажувати їх, бібліотека зробить цю роботу за нас. Давайте виключимо вертикальну синхронізацію, як у попередньому прикладі, тільки тепер із використанням GLEW.

Повноекранні/екранні режими

Дуже часто нам знадобиться не просто віконна програма, а розгортання вікна на весь екран із можливою зміною роздільної здатності екрану (як робиться у всіх іграх). Так що давайте навчимося змінювати роздільну здатність екрану, а також переходити з екранного режиму в повноекранний і навпаки. 1) Під час створення програми ми повинні всі можливі режими занести в окремий масив, щоб потім знати, які режими підтримує монітор. Введіть нові змінні: Int nummodes; // кількість підтримуваних режимів DEVMODE devmodes; // масив характеристик кожного їх Введем функцію з переліку можливих режимів Void EnumDisplayModes() DEVMODE devmode;

// підрахуємо кількість режимів // щоб знати, скільки місця відокремлювати під масив Nummodes=0; While( EnumDisplaySettings(NULL, nummodes, &devmode)) Nummodes++;

Devmodes = new DEVMODE[nummodes];

// заносимо до нашого масиву характеристики кожного // режиму Nummodes=0; While( EnumDisplaySettings(NULL, nummodes, &devmodes[nummodes])) Nummodes++; > Тепер додамо функцію переходу вікна в екранний режим Void SetWindow() // змінюємо роздільну здатність ChangeDisplaySettings(NULL, 0);

// виставляємо положення та розміри нашого вікна Rect wrect; >