Библиотека GLUT и OpenGL
Наряду с намазолившим глаза DirectX, как известно существует уже очень давно такая скромная графическая библиотека OpenGL, которая, хоть уже и достаточно ненова, но всё ещё является источником «холиваров» между её сторонниками и сторонниками DirectX.
Кто-то считает OpenGL уже мётрвым, но он живее всех живых: его спасает его кроссплатформенность и необозримое количество расширений. К слову о кроссплатформенности: заголовочные файлы GLUT существуют в вариантах для C/C++, Fortran и Ada, что уже говорит о чём-то.
Если работать напрямую с функциями OpenGL, то программа будет достаточно объёмной по количеству кода и на реализацию уйдёт больше временных и интеллектуальных ресурсов. Помочь призвана библиотека GLUT. В GLUT есть всё необходимое для быстрой и эффективной работы с OpenGL.
Библиотека GLUT
Библиотека GLUT'а для Win32 представляет собой набор файлов:
- DLL-библиотка glut32.dll — ядро GLUT.
- Статичная LIB-библиотека glut32.lib — библиотека для линкования к программе.
- Заголовочный файл glut.h.
Кроме стандартных методов инициализации, GLUT предлагает также наборы методов для оранизации меню, перехода в полноэкранный режим, рисования примитивов, вывода текста...
Единственным недостатком и проигрышем относительно DirectX самого OpenGL является то, что он не объектно ориентирован, что отчасти затрудняет написание кода, с другой стороны облегчает работу.
Пример
#include "glut.h"
#include <stdio.h>
#define _CRT_TERMINATE_DEFINED
#include <stdlib.h>
//
// ВНИМАНИЕ!
// Библиотека GLUT содержит в себе объвление метода exit, описанного в stdlib.h.
// Воизбежание конфликта, перед подключением stdlib.h после glut.h придётся объявлять _CRT_TERMINATE_DEFINED
//
#define MAXPOINTS 75 // Константа, задающая максимальное количество точек
//
// Метод, вызываемый при изменении размеров окна
//
void _wreshape( int width, int height );
//
// Метод, вызываемый при обновлении изображения в окне
//
void _wdisplay();
//
// Метод, вызываемый при каком-либо нажатии мышью в окне
//
void _mouseclick( int button, int state, int x, int y );
//
// Генератор заголовка для окна
//
void generateWindowTitle();
//
// Структура точки
//
struct POINT {
public:
//
// Абсцисса
//
int x;
//
// Ордиата
//
int y;
//
// Конструктор
//
POINT( int _x, int _y ) : x(_x), y(_y) { }
};
POINT** points; // Указатель на массив точек
int cidx = 0; // Индекс текущей точки
float color_R = 1, // Красная составляющая цвета
color_G = 0, // Зелёная составляющая цвета
color_B = 0; // Синяя составляющая цвета
//
// Главный метод
//
int main( int argc, char* argv[] ) {
int i = 0;
printf( "GLUT example\n\nLeft button - add point\nMiddle button - change color\nRight button - clear" );
//
// Запрос памяти под массив
//
points = ( POINT** )malloc( MAXPOINTS * sizeof( POINT* ) );
//
// Инициализация GLUT
//
glutInit( &argc, argv );
//
// Задание размера окна GLUT
//
glutInitWindowSize( 500, 500 );
//
// Задание позиции окна GLUT
//
glutInitWindowPosition( 100, 100 );
//
// Установка цветового режима RGBA
//
glutInitDisplayMode( GLUT_RGBA );
//
// Создание окна с соответствующим заголовком
//
glutCreateWindow( "the PAINT" );
//
// Регистрация функции смены размера
//
glutReshapeFunc( _wreshape );
//
// Регистрация функции отображения
//
glutDisplayFunc( _wdisplay );
//
// Регистрация функции обработки мыши
//
glutMouseFunc( _mouseclick );
//
// Очистка окна чёрным цветом
//
glClearColor( 0, 0, 0, 0 );
//
// Главный цикл
//
glutMainLoop();
//
// Всё хорошо, не волнуйтесь
//
return 0;
}
void _wreshape( int width, int height ) {
glViewport( 0, 0, 500, 500 );
glLoadIdentity();
gluOrtho2D( 0, 500, 0, 500 );
glMatrixMode( GL_MODELVIEW );
glFlush();
}
void _wdisplay() {
int i = 0;
//
// Обновление заголовка и очистка окна
//
generateWindowTitle();
glClear( GL_COLOR_BUFFER_BIT );
//
// Если точек более 2, то рисуем ломаную
//
if ( cidx > 1 ) {
//
// Всё рисование должно лежать между glBegin( GLenum ) и glEnd
// Аргумент glBegin задаёт режим рисования. В данном случае рисуются линии.
//
glBegin( GL_LINES );
for( i = 0; i < cidx - 1; i++ ) {
//
// Задание цвета
//
glColor3f( color_R, color_G, color_B );
//
// Установка вершин
//
glVertex2i( points[i]->x, points[i]->y );
glVertex2i( points[i + 1]->x, points[i + 1]->y );
}
//
// Завершение отрисовки
//
glEnd();
}
//
// Слив
//
glFlush();
}
void _mouseclick( int button, int state, int x, int y ) {
//
// Если кнопку нажали( не отпустили )
//
if ( state == GLUT_DOWN )
//
// Смотрим, что это за кнопка
//
switch ( button ) {
//
// Левая кнопка. Тогда добавляем точку.
//
case GLUT_LEFT_BUTTON:
if ( cidx < MAXPOINTS ) {
points[cidx] = new POINT( x, 500 - y );
cidx++;
}
else
printf( "> Points limit reached! Press right mouse button to reset." );
break;
//
// Средняя кнопка. Циклически меняем цвет К -> З -> C.
//
case GLUT_MIDDLE_BUTTON:
if ( color_R == 1 ) {
color_R = 0;
color_G = 1;
}
else if ( color_G == 1 ) {
color_G = 0;
color_B = 1;
}
else {
color_R = 1;
color_G = 0;
color_B = 0;
}
break;
//
// Правая кнопка. Сброс.
//
case GLUT_RIGHT_BUTTON:
cidx = 0;
break;
}
//
// Вызываем метод отрисовки, чтобы применить изменения
//
_wdisplay();
}
void generateWindowTitle() {
//
// Запрос памяти под строку заголовка и формирование заголовка
//
char* result = ( char* )malloc( 50 * sizeof( char ) );
sprintf( result, "the PAINT[ %i / %i ]", cidx, MAXPOINTS );
//
// Установка заголовка
//
glutSetWindowTitle( result );
//
// Очистка памяти напоследок
//
free( ( void* )result );
}
Что делает этот пример? Он фиксирует точки нажатия мыши в окне и рисует по ним ломаную линию. Средняя кнопка меняет цвет, правая — сбрасывает точки.
Данный пример демонстрирует лишь малую часть возможностей OpenGL хотя бы по тому, что он представляет двумерную графику.
Взгляд внутрь
Теперь посмотрим внутрь библиотеки GLUT. Если посмотреть объявления использованных коллбековых функций, то увидим следующее:
GLUTAPI void APIENTRY glutDisplayFunc(void (GLUTCALLBACK *func)(void));
GLUTAPI void APIENTRY glutReshapeFunc(void (GLUTCALLBACK *func)(int width, int height));
GLUTAPI void APIENTRY glutMouseFunc(void (GLUTCALLBACK *func)(int button, int state, int x, int y));
Здесь нас интересует константа GLUTCALLBACK:
/* GLUT callback calling convention for Win32. */
# define GLUTCALLBACK _cdecl
Касательно ссылок на методы скажу, что указывая их, надо посматривать на их тип вызова(тип вызовов по умолчанию задаётся в настройках компиляции проекта или принудительно в объявлении метода). Для справки привожу пример:
class _CLASS {
public:
static void static_method();
void nonstatic_method();
};
//
// СЛУЧАЙ 1: ссылка на статичный метод класса
//
glutSetDisplayFunc( _CLASS::static_method );
//
// СЛУЧАЙ 2: ссылка на нестатичный метод класса
// Кстати, экземпляр тут совсем и не затрагивается
//
glutSetDisplayFunc( &_CLASS::nonstatic_method );
Ну, вот на пока и всё.
Напоследок добавлю ссылку:
Домашняя страница GLUT
В следующем обзоре по теме заглянем несколько глубже и я расскажу про собственноручно написанного «сапёра».
Василий «DarkWolf» Юмашов, 18.01.2010