[an error occurred while processing this directive]

[an error occurred while processing this directive]


Автор: Роман Подобедов
Email
: romka@ut.ee
WWW
: www.ut.ee/~romka
Версия 1.0 (1 Мая 2000)

Работа с OpenGL при помощи GLUT.

(Часть I. Основы GLUT и основные операции с окнами)

Содержание:

  1. Что такое GLUT?
  2. Основные шаги для построения минимальной программы
  3. Работа с окнами в GLUT
  4. Связь между GLUT и вашей OpenGL программой

1. Что такое GLUT?

Как вы уже знаете, OpenGL является мультиплатформенной библиотекой, т.е. программы написанные с помощью OpenGL можно легко переносить на различные операционные системы, при этом получая один и тот же визуальный результат. Единственное что плохо - это то, что для конкретной операционной системы необходимо по своему производить настройку OpenGL. То есть, допустим, вы написали OpenGL программу под Windows и захотели перенести её в Linux, код OpenGL должен перенестись без проблем, но вот операции с окнами, интерфейс управления, операции с устройствами ввода/вывода нужно заново переписать уже под другую операционную систему - Linux.

К счастью, существует специальная мультиплатформенная библиотека, позволяющая решить вышеописанные проблемы. И называется эта библиотека - GLUT.

Данная статья познакомит вас с основными возможностями программирования с использованием GLUT. Вы узнаете каким образом создавать окна, элементы интерфейса, управлять устройствами ввода/вывода и как все это связывать с OpenGL. В первой части этой статьи вы познакомитесь с основами GLUTа и основными операциями с окнами.

2. Основные шаги для построения минимальной программы

Прежде всего необходимо научиться самому основному - это созданию окна, в котором можно будет рисовать с помощью OpenGL. Минимальная программа, которая создает окно и что-нибудь рисует там состоит из следующих шагов:

  1. Инициализация GLUT
  2. Установка параметров окна.
  3. Создание окна.
  4. Установка функций, отвечающих за рисование в окне и изменении формы окна.
  5. Вход в главный цикл GLUT.

Итак, рассмотрим все 5 пунктов поподробнее:

1. Инициализация GLUT производится командой: 

void glutInit(int *argcp, char **argv); 

Первый параметр представляет из себя указатель на количество аргументов в командной строке, а второй - указатель на массив аргументов. Обычно эти значения берутся из главной функции программы: int main(int argc, char *argv[]).

2. Установка параметров окна содержит в себе несколько этапов. Прежде всего необходимо указать размеры окна:

void glutInitWindowSize(int width, int height);

Первый параметр width - ширина окна в пикселях, второй height - высота окна в пикселях. Отмечу также, что если эту команду опустить, то GLUT сам установит размеры окна по умолчанию, обычно это 300x300.

Далее можно задать положение создаваемого окна относительно верхнего левого угла экрана. Делается это командой:

void glutInitWindowPosition(int x, int y);

Необходимо также установить для окна режим отображения информации. Т.е. установить для окна такие параметры как: используемая цветовая модель, количество различных буферов, и т.д. Для этого в GLUT существует команда:

void glutInitDisplayMode(unsigned int mode);

У команды имеется единственный параметр, который может быть представлен одной из следующих констант или комбинацией этих констант с помощью побитового ИЛИ.

Константа Значение
GLUT_RGB Для отображения графической информации используются 3 компоненты цвета RGB.
GLUT_RGBA То же что и RGB, но используется также 4 компонента ALPHA (прозрачность).
GLUT_INDEX Цвет задается не с помощью RGB компонентов, а с помощью палитры. Используется для старых дисплеев, где количество цветов например 256.
GLUT_SINGLE Вывод в окно осуществляется с использованием 1 буфера. Обычно используется для статического вывода информации.
GLUT_DOUBLE Вывод в окно осуществляется с использованием 2 буферов. Применяется для анимации, чтобы исключить эффект мерцания.
GLUT_ACCUM Использовать также буфер накопления (Accumulation Buffer). Этот буфер применяется для создания специальных эффектов, например отражения и тени.
GLUT_ALPHA Использовать буфер ALPHA. Этот буфер, как уже говорилось используется для задания 4-го компонента цвета - ALPHA. Обычно применяется для таких эффектов как прозрачность объектов и антиалиасинг.
GLUT_DEPTH Создать буфер глубины. Этот буфер используется для отсечения невидимых линий в 3D пространстве при выводе на плоский экран монитора.
GLUT_STENCIL Буфер трафарета используется для таких эффектов как вырезание части фигуры, делая этот кусок прозрачным. Например, наложив прямоугольный трафарет на стену дома, вы получите окно, через которое можно увидеть что находится внутри дома.
GLUT_STEREO Этот флаг используется для создания стереоизображений. Используется редко, так как для просмотра такого изображения нужна специальная аппаратура.

Вот пример использования этой команды:void glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);

3. Создание окна. После того как окно установлено необходимо его создать.

int glutCreateWindow(const char *title);  

Эта команда создаёт окно с заголовком, который вы укажете в качестве параметра и возвращает HANDLER окна в виде числа int. Этот HANDLER обычно используется для последующих операций над этим окном, таких как изменение параметров окна и закрытие окна.

4. Установка функций, отвечающих за рисование в окне и изменении формы окна.

После того, как окно, в которое будет выводится или как говорят рендерится графическая информация, подготовлено и создано, необходимо связать с ним процедуры, которые будут отвечать за вывод графической информации, следить за размерами окна, следить за нажатиями на клавиши и т.д. Самая первая и самая необходимая функция которую мы рассмотрим, отвечает за рисование. Именно она всегда будет вызываться операционной системой, чтобы нарисовать (перерисовать) содержимое окна. Итак, задаётся эта функция командой:

void glutDisplayFunc(void (*func)(void));

Единственный параметр этой функции - это указатель на функцию, которая будет отвечать за рисование в окне. Например чтобы функция void Draw(void), определенная в вашей программе отвечала за рисование в окне, надо присоединить ее к GLUT следующим образом: glutDisplayFunc(Draw);

И ещё одна функция, которая по моему мнению является важной - это функция, которая отслеживает изменения окна. Как только у окна изменились размеры, необходимо перестроить вывод графической информации уже в новое окно с другими размерами. Если этого не сделать, то например увеличив размеры окна, вывод информации будет производиться в старую область окна, с меньшими размерами. Определить функцию, отвечающую за изменение размеров окна нужно следующей командой:

void glutReshapeFunc(void (*func)(int width, int height));

Единственный параметр - это указатель на функцию, отвечающую за изменение размеров окна, которая как видно должна принимать два параметра width и height, соответственно ширина и высота нового (измененного) окна.

5. Вход в главный цикл GLUT.

Ну и последнее, что необходимо сделать, чтобы запустить нашу программу - это войти в так называемый главный цикл GLUT. Этот цикл запускает на выполнение так называемое сердце GLUT, которое обеспечивает взаимосвязь между операционной системой и теми функциями, которые отвечают за окно, получают информацию от устройств ввода/вывода. Для того, чтобы перейти в главный цикл GLUT, надо выполнить единственную команду:

void glutMainLoop(void);

Вот в принципе основные шаги для самой простейшей GLUT программы. Теперь я приведу эту самую программу. Как вы сейчас убедитесь - это очень легкая программа и трудного тут ничего нет. И ещё, прежде чем использовать функции из GLUT необходимо к программе подключить заголовок: #include <GL/glut.h>. Программа чертит линию посередине экрана. Вот текст самой программы:


#include <GL/glut.h>

void Reshape(int width, int height)
{
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(-1, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
}

void Draw(void)
{
  glClear(GL_COLOR_BUFFER_BIT);

  glColor3f(0.0f, 0.0f, 1.0f);
  glLineWidth(1);

  glBegin(GL_LINES);
    glVertex2f(0, 0.5f);  
    glVertex2f(0, -0.5f);  
  glEnd();

  glFlush();  
}

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  glutInitWindowSize(400, 300);
  glutInitWindowPosition(100, 100);

  glutInitDisplayMode(GLUT_RGB);
  glutCreateWindow("Romka Demo");

  glutReshapeFunc(Reshape);
  glutDisplayFunc(Draw);
  glClearColor(0, 0, 0, 0);

  glutMainLoop();
  return 0;
}


 

Полный исходный код программы можно взять отсюда: simple_demo.zip

После того как наша первая программа готова, я расскажу вам немножко поподробнее об операциях с окнами.

3. Работа с окнами в GLUT

GLUT предоставляет всевозможные функции для работы с окнами. Среди этих функций найти можно практически всё необходимое для создания небольших программ.

Как я уже выше говорил, при создании окна, функция glutCreateWindow при успешном завершении возвращает дескриптор созданного окна (значение int). Это значение в дальнейшем может быть использовано для операций с этим окном. Рассмотрим те операции, которые можно производить над окном. Прежде всего необходимо выбрать наше окно, т.е. сделать его текущим (не забывайте, создано может быть несколько окон и надо сообщить GLUT с каким именно окном вы хотите работать). Если вы создали всего одно окно то следующую функцию можно опустить.

void glutSetWindow(int win);

Эта функция делает окно win текущим. После этого можно производить различные операции над этим окном.

int glutGetWindow(void);

Эта функция позволяет вам определить какое в данный момент окно является активным. Функция возвращает дескриптор окна.

После того как выбрано окно, можно производить над ним конкретные действия:

Функция Значение
void glutSetWindowTitle(const char *title); Изменяет заголовок окна
void glutPositionWindow(int x, int y); Изменяет расположение окна. Новые координаты левого верхнего угла окна (x,y)
void glutReshapeWindow(int width, int height); Изменяет размеры окна. Новые размеры окна: width, height
void glutIconifyWindow(void); Сворачивает окно в иконку.
void glutHideWindow(void); Скрывает окно.
void glutShowWindow(void); Показывает окно.
void glutFullScreen(void); Развернуть окно на весь экран. При этом убираются с экрана заголовок окна и его остальные части, кроме содержимого окна, т.е. ваше приложение становится полноэкранным.

Существует еще одна интересная функция, которая позволяет устанавливать в окне форму курсора:

void glutSetCursor(int cursor);

Параметр cursor может принимать одно из следующих значений: GLUT_CURSOR_RIGHT_ARROW, GLUT_CURSOR_LEFT_ARROW, GLUT_CURSOR_INFO, GLUT_CURSOR_DESTROY, GLUT_CURSOR_HELP, GLUT_CURSOR_CYCLE, GLUT_CURSOR_SPRAY, GLUT_CURSOR_WAIT, GLUT_CURSOR_TEXT, GLUT_CURSOR_CROSSHAIR, GLUT_CURSOR_UP_DOWN, GLUT_CURSOR_LEFT_RIGHT, GLUT_CURSOR_TOP_SIDE, GLUT_CURSOR_BOTTOM_SIDE, GLUT_CURSOR_LEFT_SIDE, GLUT_CURSOR_RIGHT_SIDE, GLUT_CURSOR_TOP_LEFT_CORNER, GLUT_CURSOR_TOP_RIGHT_CORNER, GLUT_CURSOR_BOTTOM_RIGHT_CORNER, GLUT_CURSOR_BOTTOM_LEFT_CORNER, GLUT_CURSOR_INHERIT, GLUT_CURSOR_NONE, GLUT_CURSOR_FULL_CROSSHAIR.

В GLUT существует специальный режим, позволяющий вам использовать режим игры для вашей OpenGL программы, т.е. когда ваше приложение занимает весь экран.

Для этого в GLUT существуют специальные команды для управления этим режимом.

Первая из них:

void glutGameModeString(const char *string);

Эта функция определяет параметры устанавливаемого разрешения экрана, глубину цвета и частоту обновления экрана(в герцах). Формат входного параметра такой:

(Screen_width)x(Screen_height):(Depth)@(Refresh_Frequency)

Например: glutGameModeString("640x480:16@72"); - Означает, что необходимо выбрать разрешение экрана 640 на 480 пикселей при глубине цвета в 16 бит и частоте обновления экрана 72 герца.

Чтобы перейти в режим GAME MODE необходимо вызвать функцию:

int glutEnterGameMode(void);

А чтобы выйти из режима GAME MODE необходимо вызвать функцию:

void glutLeaveGameMode(void);

Ну и чтобы было понятнее приведу пример использования этого режима:


...
int main(int argc, char *argv[])
{

  WindW = 400;
  WindH = 300;

  glutInit(&argc, argv);
  glutInitWindowSize(WindW, WindH);
  glutInitDisplayMode(GLUT_RGB);
  // Enter to game mode: 800x600, 32 bit color, 72 Hz refresh rate
  glutGameModeString("800x600:32@72"); // For full screen game mode
  glutEnterGameMode(); 

  glutReshapeFunc(Reshape);
  glutDisplayFunc(Draw);
  glutKeyboardFunc(Key);
  glClearColor(0, 0, 0, 0);
  glutMainLoop();

  return 0;

}
...


 

Полный исходный код программы можно взять отсюда: game_mode.zip

Отмечу также что можно вызывать функцию glutGameModeString и таким образом:

glutGameModeString("800x600:32");

Т.е. не указывая частоту обновления экрана. При этом GLUT сама выберет подходящую частоту.

4. Связь между GLUT и вашей OpenGL программой

В этой части я расскажу о том, каким образом происходит взаимодействие вашей OpenGL программы и GLUT.

Самой первой по значимости (без которой не обойтись ни в одной программе) идет функция отвечающая за перерисовку содержимого окна:

void glutDisplayFunc(void (*func)(void));

Эта функция устанавливает функцию в вашей программе, которая будет отвечать за перерисовку окна. Это нужно для того, что когда системе потребуется перерисовать ваше окно (например когда вы из свернутого состояния развернули окно), то она вызовет соответствующую функцию в вашей программе, которая ответственна за перерисовку содержимого окна. Также функция перерисовки окна вызывается при самом первом запуске вашего приложения. Отмечу, что если вы создаете окно в GLUT, то определять функцию перерисовки окна нужно обязательно! С другой стороны, вы сами можете заставить систему перерисовать окно, это нужно например тогда, когда вы изменили состояния объектов или изменили их внешность, то вам понадобится перерисовать содержимое окна, чтобы отобразить сделанные изменения. Поэтому в GLUT существует специальная функция glutPostRedisplay(), которая заставляет систему перерисовать текущее окно. Если вдруг вам захочется перерисовать не текущее окно, а какое-то другое, то предусмотрена следующая функция: glutPostWindowRedisplay(int win), которая перерисовывает содержимое win окна.

void glutReshapeFunc(void (*func)(int width, int height));

Как уже говорилось, это функция устанавливает другую функцию в вашей программе, которая будет ответственна за изменение размеров окна. Как только у окна изменились размеры, необходимо перестроить вывод графической информации уже в новое окно с другими размерами. Для этого в вызываемую функцию (т.е. функцию в вашей программе) передаются 2 параметра width и height, т.е. ширина и высота измененного окна. Обычно в самом начале функции, вызывается функция glViewport, например так: glViewport(0, 0, width, height), чтобы перестроить вывод изображения уже в окно с другими размерами.

Еще одна интересная функция, которая устанавливает функцию в вашей программе, которая будет вызываться системой всякий раз, когда окно становиться видимым или наоборот становиться невидимым.

void glutVisibilityFunc(void (*func)(int state));

Единственный параметр, который передается в вызываемую системой функцию: state. Сравнивая этот параметр с константами GLUT_NOT_VISIBLE и GLUT_VISIBLE можно определить, что произошло с окном, либо оно стало невидимым или наоборот - видимым.

void glutIdleFunc(void (*func)(void));

Функция, которая устанавливает функцию в вашей программе, которая будет вызываться системой всякий раз, когда ваше приложение простаивает, т.е. ничего не делает. Часто эту функцию используют для анимации.

Ну и последняя функция, которую мы рассмотрим:

void glutTimerFunc(unsigned int millis, void (*func)(int value), int value);

Эта функция устанавливает таймер. Первый параметр millis задает время в миллисекундах, по истечении которых вызывается функция, которая указана как второй параметр. Третий параметр value указывает индентификатор таймера, т.е. таймеров может быть одновременно запущено несколько. То же самое значение value есть и в функции, которая вызывается таймером. Это нужно для того, чтобы определить, какой таймер сработал, т.е. если ваша функция обрабатывает несколько таймеров.

Далее привожу пример небольшой программки, которая рисует вращающуюся линию. Исходный текст программы можно взять и отсюда: animation.zip


#include <stdio.h>
#include <GL/glut.h>

int WindW, WindH;
int i;
int alpha;

void Reshape(int width, int height) // Reshape function
{
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(-1, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  
  WindW = width;
  WindH = height;
}

void Draw(void) // Window redraw function
{
  glClear(GL_COLOR_BUFFER_BIT);

  glLineWidth(3);
  glColor3f(0.0f, 0.6f, 0.9f);

  glPushMatrix();
  glRotatef(alpha, 0.0f, 0.0f, 1.0f);
  alpha += 4;
  if (alpha > 359) alpha = 0;
  glBegin(GL_LINES);
    glVertex2f(-0.5f, 0.5f);
    glVertex2f(0.5f, -0.5f);
  glEnd();
  glPopMatrix();

  glFlush();  
  glutSwapBuffers();
}

void Visibility(int state) // Visibility function
{
  if (state == GLUT_NOT_VISIBLE) printf("Window not visible!\n");
  if (state == GLUT_VISIBLE) printf("Window visible!\n");
}

void timf(int value) // Timer function
{
  glutPostRedisplay();  // Redraw windows
  glutTimerFunc(40, timf, 0); // Setup next timer
}

int main(int argc, char *argv[])
{
  WindW = 400;
  WindH = 300;
  alpha = 0; 
 
  glutInit(&argc, argv);
  glutInitWindowSize(WindW, WindH);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  (void)glutCreateWindow("Romka Demo");

  glutReshapeFunc(Reshape); // Set up reshape function
  glutDisplayFunc(Draw);    // Set up redisplay function 
  glutTimerFunc(40, timf, 0); // Set up timer for 40ms, about 25 fps
  glutVisibilityFunc(Visibility); // Set up visibility funtion
  glClearColor(0, 0, 0, 0);

  glutMainLoop();

  return 0;
}


Попробуйте свернуть окно, а потом развернуть и посмотрите на консоль, это сработала процедура Visibility.


[an error occurred while processing this directive]


[an error occurred while processing this directive]