Главная » Статьи » Графика (OpenGL) |
Инициализация OpenGL в Windows.
«Инициализация OpenGL в Windows» Наконец-то у меня выдалось пара выходных, и я могу заняться дальнейшим обустройством своей странички, а самое главное начать помогать тем, кто хочет научиться создавать собственные компьютерные 3D-игры(я кстати тоже...:)), либо просто интересуется данной темой, не важно почему, главное, что это кому-то интересно!!! Итак, приступим. Сегодня мы просто создадим простое мастдайное окошко, где великий и могучий OpenGL будет рисовать простой "проволочный" каркас шарика... В последствии, будем надеяться, мы сможем в этом окошке наблюдать нечто покруче,(может даже покруче Дум3 :)). Многим этот урок покажется бессмысленным, но как я недавно смог убедиться, у некоторых все еще с этим возникают проблемы, несмотря на многочисленные сетевые ресурсы, иногда очень толковые. Да, чуть не забыл самое главное! Кодить мы будем на С++ (MS VS6.0), графическая библиотека которую будем использовать - это OpenGL!!! Исходный код максимально прокомментирован и поэтому вопросов, по идее, возникнуть не должно. По себе знаю, чем больше комментариев, тем понятнее. Ну а если все-таки вопросы возникнут, - все нужные ссылки внизу, по возможности отвечу... НАЧАЛИ КОДИТЬ: Первым делом создадим пустой проект приложения Win32(никаких MFC), и включим в него все необходимые заголовочные файлы, без которых нам не удастся откомпилировать нашу программу... #include //Присутствует во всех приложениях Win32 #include //Наша OpenGL библиотека #include //Библиотека утилит OpenGL #include //И еще одна, с ее помощью можно рисовать стандартные объекты (сферы, кубы, и др.) Следуюие строчки подключают статические библиотеки OpenGL.lib, Glu32.lib b Glaux.lib которые также необходимы для успешной компиляции проекта... #pragma comment (lib, "OpenGL32.lib") #pragma comment (lib, "Glu32.lib") #pragma comment (lib, "Glaux.lib") Вместо этих строчек можно зайти в меню Project -> Settings -> закладка Link -> поле Object/library modules: добавить в конец строки через пробел (OpenGL32.lib Glu32.lib Glaux.lib). Далее объявляем необходимые переменные... static GLint resW = 640, resH = 480; // Размер окна bool isFullscr = false; // Полноэкранный режим (по умолчанию - нет) static GLdouble cntr; // Счетчик. Отвечает за вращение шарика BOOL keys[256]; // Массив для процедуры обработки клавиатуры HGLRC hGLRC; // Контекст рендеринга OpenGL HDC hDC; // Контекст устройства Windows static GLfloat light1_position[] = {0.0, 0.0, 3.0, 1.0}; //Позиция источника света Надеюсь все предельно ясно. Далее напишем функцию инициализации OpenGL, в которой мы инициализируем стандартное освещение, установим метод закраски полигонов, и выполним еще несколько функций, о которых мы более подробно поговорим в следующих уроках, а пока из всей этой функции следует обратить внимание на одну единственную функцию: glEnable (GL_DEPTH_TEST); эта функция включает тест глубины, это нужно для того, чтобы при рисовании нескольких объектов, те объекты которые спереди (ближе к точке обзора) перекрывали те, которые находятся за ними, иначе сами представьте что случится... например чайник стоит за ящиком, но тем не менее рисуется спереди :) прикольно, но нам это не нужно... GLvoid InitGL() // Вызвать после создания окна GL { // LoadGLTextures(); GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0}; glClearColor (0.5, 0.5, 0.5, 0.0); //*********** Инициализация освещения ********************* GLfloat light_position[] = {0.0, 0.0, 10.0, 1.0}; glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 20.0); glLightfv (GL_LIGHT0, GL_POSITION, light_position); glLightf (GL_LIGHT0, GL_SPECULAR, 128); glEnable (GL_LIGHTING); glEnable (GL_LIGHT0); //********************************************************** glShadeModel (GL_SMOOTH); // Метод закраски: СГЛАЖЕНЫЙ glEnable (GL_DEPTH_TEST); // Включаем тест глубины // ВКЛ. АВТОМАТИЧЕСКИЙ ПЕРЕСЧЕТ НОРМАЛЕЙ glEnable (GL_AUTO_NORMAL); glEnable (GL_NORMALIZE); // ВКЛ. ОТСЕЧЕНИЕ ЗАДНИХ ГРАНЕЙ glEnable (GL_CULL_FACE); // Включаем отсечение граней glCullFace (GL_BACK); // Указываем, что отсекать будем задние грани } Теперь началось самое интересное... Функция перерисовки, обновления, как хотите ее называйте, а выполняет она следующее: устанавливает размер порта просмотра(это то место где мы будем рисовать)который не всегда может быть равен размеру окна widowsa, но в нашем случае их размеры равны. Затем устанавливаем и настраиваем матрицу проекции с помощью ф-ции gluPerspective которая не является стандартной для OpenGL а включена в отдельную библиотеку утилит OpenGL, об этом можно догадаться по трем первым буквам ф-ции(glu - gl utility). Далее мы устанавливаем матрицу модели-вида, и практически все готово для того, чтобы рисовать... void reshape(int width,int height) { glViewport(0,0,width,height); // Устанавливаем размер порта просмотра glMatrixMode( GL_PROJECTION ); // Переключаемся на матрицу проекции glLoadIdentity(); // Сбрасываем матрицу проекции путем установки единичной матрицы gluPerspective (50.0, (GLfloat)width/(GLfloat)height, 1.0, 100.0); // настраиваем нашу камеру // gluPerspective (угол_обзора, отношение_ширины_к_высоте_порта_просмотра, ближняя_плоскость_отсечения, дальняя_плоскость_отсечения); // gluLookAt( 0,0,3, 0,0,0, 0,1,0 ); // попробуйте...:) glMatrixMode( GL_MODELVIEW ); // Переключаемся на матрицу модели-вида glLoadIdentity(); // Сбрасываем матрицу модели-вида путем установки единичной матрицы } И сама функция рисования, в которой нас пока интересуют первые 2е строчки. Первая говорит OpenGL, что нужно очистить буфер цвета и буфер глубины. Эти операции выполняются перед рисованием каждого кадра! Очищая буфер цвета, мы стираем то, что уже нарисовали и отобразили на экране, и затем рисуем следующий кадр. Обновляя буфер глубины мы следим за тем, чтоб на экране рисовалось то что мы видим, и не рисовалось то, что от нас скрыто(ого как загнул)... void display() { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очищаем буфера glLoadIdentity(); // Грузим единичную матрицу glPushMatrix(); cntr +=0.1; glTranslatef (0.0, 0.0, -5.0); glRotated(cntr, 0.0f, 1.0f, 0.0f); auxWireSphere(1.0); if (cntr >= 360.0f) cntr =0.0f; glPopMatrix(); glFlush(); // Досрочное завершение рисования (ф-я необязательна) } НУ ВОТ... Наконец мы добрались до самой изюминки данного урока! Это две функции, которые отвечают за создание окна, его вид, наличие отсутствие кнопочек, контекстного меню, получение и обработку сообщений(команд) операционной системы и т.д. и т.п. Функция WindowFunc отвечает за инициализацию формата пикселей, и обработку сообщений(команд) операционной системы. Вот ее код... LRESULT CALLBACK WindowFunc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { RECT Screen; GLuint PixelFormat; static PIXELFORMATDESCRIPTOR pfd= { sizeof(PIXELFORMATDESCRIPTOR), //Размер структуры 1, // Номер версии PFD_DRAW_TO_WINDOW | // Рисовать будем в окно PFD_SUPPORT_OPENGL | // Установим поддержку OpenGL PFD_DOUBLEBUFFER, // Выберем двойной буфер PFD_TYPE_RGBA, // Режим цветности - RGBA 32, // Выберем 32 битную глубину цвета 0, 0, 0, 0, 0, 0, // Игнорирование цветовых битов 0, // Нет буфера прозрачности 0, // Игнорируем сдвиговый бит 0,//---------------------// Нет буфера аккумуляции 1, 1, 1, 1, // Биты аккумуляции игнорируются 32, // 32 битный Z-буфер (буфер глубины) 0, // Нет буфера трафарета 0, // Нет вспомогательных буферов PFD_MAIN_PLANE, // Рисовать в главную плоскость(слой) 0, // Резерв 0, 0, 0 // Маски слоя игнорируются }; switch(message) { // сообщение WM_CREATE приходит // один раз при создании окна case WM_CREATE: hDC = GetDC(hWnd); // Получить контекст устройства для окна PixelFormat = ChoosePixelFormat(hDC, &pfd);// Найти ближайшее совпадение для нашего формата пикселов if (!PixelFormat) // Если не удалось то... { // Выводим сообщение об ошибке MessageBox (0, "Не удается найти подходящий PixelFormat!", "Error", MB_OK | MB_ICONERROR); SendMessage(hWnd, WM_DESTROY, wParam, lParam);// Это сообщение говорит, что программа должна завершится break; // Предотвращение повтора кода } SetPixelFormat (hDC, PixelFormat, &pfd); // Теперь нужно установить наш формат пикселов if (!SetPixelFormat (hDC, PixelFormat, &pfd)) // Если не удалось то... { // Выводим сообщение об ошибке MessageBox (0, "Не удается установить PixelFormat!","Ошибка!!!", MB_OK | MB_ICONERROR); // Уничтожаем окно! SendMessage(hWnd, WM_DESTROY, wParam, lParam); break; } hGLRC = wglCreateContext (hDC); //Захватывает Контекст Рендеринга и сохраняет его в переменной hGLRC if (!hGLRC) // Захватить не удалось... { // Выводим сообщение об ошибке MessageBox (0, "Не удается создать контекст рендеринга OpenGL!","Ошибка!!!", MB_OK | MB_ICONERROR); // Уничтожаем окно! SendMessage(hWnd, WM_DESTROY, wParam, lParam); break; } wglMakeCurrent(hDC, hGLRC); // делаем его текущим if (!wglMakeCurrent(hDC, hGLRC)) // Если не получилось... { MessageBox (0, "Не удается активировать контекст рендеринга GLRC!", "Ошибка!!!", MB_OK | MB_ICONERROR); // Уничтожаем окно! SendMessage(hWnd, WM_DESTROY, wParam, lParam); break; } // Все... окошко создано... InitGL (); // Инициализируем наш GL break; // это сообщение приходит при уничтожении окна case WM_DESTROY: // Уничтожает окно case WM_CLOSE: //-|-|-|-|-|-|- ChangeDisplaySettings (NULL, 0);// Восстанавливает первоначальное разрешение(видеорежим) if(hDC) wglMakeCurrent(hDC, NULL); // Освобождаем контекст рендеринга... if(hGLRC) wglDeleteContext(hGLRC); // ...и удаляем его if(hWnd) ReleaseDC(hWnd, hDC); // Рвем с ним всякие связи. PostQuitMessage(0); // Убиваем окно! break; case WM_SIZE: GetClientRect (hWnd, &Screen);// Создает область рисования и возвращает высоту и ширину окна reshape(Screen.right, Screen.bottom ); break; case WM_SYSKEYDOWN: case WM_KEYDOWN: // Если клавиша нажата то... keys[wParam] = TRUE; // В массив keys[key number] заносится TRUE break; case WM_SYSKEYUP: case WM_KEYUP: // Если клавиша отпущена то... keys[wParam] = FALSE; // В массив keys[key number] заносится FALSE break; default: // Реакция на все остальные сообщения ОС... return DefWindowProc(hWnd,message,wParam,lParam); // ...по умолчанию } // если где-то что-то не так... return 0; // ...возвращаем ошибку } WM_CREATE, WM_DESTROY, WM_SIZE и т.д. - это все сообщения, которые ОС посылает нашему окну все время. И таких команд очень много, а мы затронули лишь необходимый минимум. Полный список находится в файле windows.h, будет интересно - загляните. ИДЕМ ДАЛЬШЕ... Функция, с которой начинается выполнение любой Win32-программы! Это функция WinMain! Она отвечает за создание окна, его свойства и все, что с ним связано... В общем название функции само за себя говорит! Давайте на нее поглядим... int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR str,int nWinMode) { MSG msg; // Структура сообщения Windows WNDCLASS wc; //Структура класса Windows для установки типа окна HWND hWnd; //Сохранение дескриптора окна wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Стиль нашего окна wc.lpfnWndProc = WindowFunc; // Определяем ф-цию ответственную за обработку сообщений ОС wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; // иконка нашего окошка (по умолчанию) wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Курсор который будет отображаться при наведении на наше окошко (стрелка) wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "OpenGLWinClass"; // Определяем название класса окна if (!RegisterClass(&wc)) // Если класс зарегистрировать не удалось... { MessageBox(0, "Ошибка при регистрации класса окна!","Ошибка!!!", MB_OK | MB_ICONERROR); return FALSE; } if(isFullscr) // Если режим полноэкранный - создаем окно с соответствующими параметрами { // создаем окно hWnd = CreateWindow( "OpenGLWinClass", // название класса окна "УРОК 01 >OpenGL is COOL", // Заголовок окна WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // Стиль (вид) окна 0, 0, resW, resH, // Размеры окна NULL, NULL, hInstance, NULL); } else // Иначе устанавливаем наше окошко, его вид и свойства { // создаем окно hWnd = CreateWindow( "OpenGLWinClass", // название класса окна "УРОК 01 >OpenGL is COOL", // Заголовок окна WS_OVERLAPPEDWINDOW | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE | WS_SIZEBOX, // Стиль (вид) окна 0, 0, resW, resH, // Размеры окна NULL, NULL, hInstance, NULL); } if (!hWnd) // если создать окно не удалось... { MessageBox(0, "Ошибка при создании окна!","Ошибка!!!", MB_OK | MB_ICONERROR); return FALSE; } if(isFullscr) { DEVMODE dmScreenSettings; // Режим работы устройства (видяхи) memset (&dmScreenSettings, 0, sizeof(DEVMODE));// Выделение памяти для хранения установок dmScreenSettings.dmSize = sizeof(DEVMODE); // Размер структуры Devmode dmScreenSettings.dmPelsWidth = resW; // Ширина экрана dmScreenSettings.dmPelsHeight = resH; // Высота экрана dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;// Режим Пиксела ChangeDisplaySettings (&dmScreenSettings, CDS_FULLSCREEN);// Переключение в полный экран } //else ChangeDisplaySettings (&dmScreenSettings, CDS_GLOBAL); ShowWindow(hWnd,nWinMode); // Показать окно UpdateWindow(hWnd); // Обновить окно SetFocus(hWnd); // Сделать окно активным while (1) { // Обработка всех сообщений while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } else return TRUE; } display(); // Нарисовать сцену SwapBuffers(hDC); // Переключить буфер экрана if (keys[VK_ESCAPE]) SendMessage(hWnd, WM_CLOSE, 0, 0);// Если ESC - выйти } } Ну, вот вроде все! Исходный код здесь(14.5k) Если что не понятно пишите, постараюсь ответить. Пожелания замечания, тоже приветствуются. Виталий Демиденко © 2007 Content-Disposition: form-data; name="sort" 50 | |
Просмотров: 35908 | Рейтинг: 0.0/0 |
Всего комментариев: 0 | |