Tuesday, 15 February 2011

Знакомимся с OpenGL

OpenGL


Знакомство с OpenGL нужно начать с того, что OpenGL — это спецификация. Т.е. OpenGL лишь определяет набор обязательных возможностей. Реализация же зависит от конкретной платформы.

OpenGL является кроссплатформенным, независимым от языка программирования API для работы с графикой. OpenGL — низкоуровневый API, поэтому для работы с ним неплохо иметь некоторое представление о графике в целом и знать основы линейной алгебры.

Именования


Скажем пару слов об именовании функций в OpenGL. Во-первых имена всех функций, предоставляемых непосредственно OpenGL, начинаются с приставки gl. Во-вторых функции, задающие некоторый параметр, характеризующийся набором чисел (например координату или цвет), имеют суффикс вида [число параметров + тип параметров + представление параметров].



  • Число параметров — указывает число принимаемых параметров. Принимает следующие значения: 1, 2, 3, 4
  • Тип параметров — указывает тип принимаемых параметров. Возможны следующие значения: b, s, i, f, d, ub, us, ui. Т.е. byte (char в C, 8-битное целое число), short (16-битное целое число), int (32-битное целое число), float (число с плавающей запятой), double (число с плавающей запятой двойной точности), unsigned byte, unsigned short, unsigned int (последние три — беззнаковые целые числа)
  • Представление параметров — указывает в каком виде передаются параметры, если каждое число по отдельности, то ничего не пишется, если же параметры передаются в виде массива, то к названию функции дописывается буква v


Пример: glVertex3iv задает координату вершины, состоящую из трех целых чисел, передаваемых в виде указателя на массив.

Графика


Все графические объекты в OpenGL представляют собой набор точек, линий и многоугольников. Существует 10 различных примитивов, при помощи которых строятся все объекты. Как двухмерные, так и трехмерные. Все примитивы в свою очередь задаются точками — вершинами.

  • GL_POINTS — каждая вершина задает точку
  • GL_LINES — каждая отдельная пара вершин задает линию
  • GL_LINE_STRIP — каждая пара вершин задает линию (т.е. конец предыдущей линии является началом следующей)
  • GL_LINE_LOOP — аналогично предыдущему за исключением того, что последняя вершина соединяется с первой и получается замкнутая фигура
  • GL_TRIANGLES — каждая отдельная тройка вершин задает треугольник
  • GL_TRIANGLE_STRIP — каждая следующая вершина задает треугольник вместе с двумя предыдущими (получается лента из треугольников)
  • GL_TRIANGLE_FAN — каждый треугольник задается первой вершиной и последующими парами (т.е. треугольники строятся вокруг первой вершины, образуя нечто похожее на диафрагму)
  • GL_QUADS — каждые четыре вершины образуют четырехугольник
  • GL_QUAD_STRIP — каждая следующая пара вершин образует четырехугольник вместе с парой предыдущих
  • GL_POLYGON — задает многоугольник с количеством углов равным количеству заданных вершин


Для задания примитива используется конструкция glBegin (тип_примитива)…glEnd (). Вершины задаются glVertex*. Вершины задаются против часовой стрелки. Координаты задаются от верхнего левого угла окна. Цвет вершины задается командой glColor*. Цвет задается в виде RGB или RGBA. Команда glColor* действует на все вершины, что идут после до тех пор, пока не встретится другая команда glColor* или же на все, если других команд glColor* нет.

Вот код рисующий квадрат с разноцветными вершинами:

  1. glBegin(GL_QUADS);
  2. glColor3f(1.0, 1.0, 1.0);
  3. glVertex2i(250, 450);
  4. glColor3f(0.0, 0.0, 1.0);
  5. glVertex2i(250, 150);
  6. glColor3f(0.0, 1.0, 0.0);
  7. glVertex2i(550, 150);
  8. glColor3f(1.0, 0.0, 0.0);
  9. glVertex2i(550, 450);
  10. glEnd();

Основы программы на OpenGL


Для платформонезависимой работы с окнами можно использовать библиотеку GLUT. GLUT упрощает работу с OpenGL.

Для инициализации GLUT в начале программы надо вызвать glutInit (&argc, argv). Для задания режима дисплея вызывается glutInitDisplayMode (режим), где режим может принимать следующие значения:

  • GLUT_RGBA — включает четырехкомпонентный цвет (используется по умолчанию)
  • GLUT_RGB — то же, что и GLUT_RGBA
  • GLUT_INDEX — включает индексированный цвет
  • GLUT_DOUBLE — включает двойной экранный буфер
  • GLUT_SINGLE — включает одиночный экранный буфер (по умолчанию)
  • GLUT_DEPTH — включает Z-буфер (буфер глубины)
  • GLUT_STENCIL — включает трафаретный буфер
  • GLUT_ACCUM — включает буфер накопления
  • GLUT_ALPHA — включает альфа-смешивание (прозрачность)
  • GLUT_MULTISAMPLE — включает мультисемплинг (сглаживание)
  • GLUT_STEREO — включает стерео-изображение


Для выбора нескольких режимов одновременно нужно использовать побитовое ИЛИ '|'. Например: glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH) включает двойную буферизацию, Z-буфер и четырехкомпонентный цвет. Размеры окна задаются glutInitWindowSize (ширина, высота). Его позиция — glutInitWindowPosition (х, у). Создается окно функцией glutCreateWindow (заголовок_окна).

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

  • void glutDisplayFunc (void (*func) (void)) — задает функцию рисования изображения
  • void glutReshapeFunc (void (*func) (int width, int height)) — задает функцию обработки изменения размеров окна
  • void glutVisibilityFunc (void (*func)(int state)) — задает функцию обработки изменения состояния видимости окна
  • void glutKeyboardFunc (void (*func)(unsigned char key, int x, int y)) — задает функцию обработки нажатия клавиш клавиатуры (только тех, что генерируют ascii-символы)
  • void glutSpecialFunc (void (*func)(int key, int x, int y)) — задает функцию обработки нажатия клавиш клавиатуры (тех, что не генерируют ascii-символы)
  • void glutIdleFunc (void (*func) (void)) — задает функцию, вызываемую при отсутствии других событий
  • void glutMouseFunc (void (*func) (int button, int state, int x, int y)) — задает функцию, обрабатывающую команды мыши
  • void glutMotionFunc (void (*func)(int x, int y)) — задает функцию, обрабатывающую движение курсора мыши, когда зажата какая-либо кнопка мыши
  • void glutPassiveMotionFunc (void (*func)(int x, int y)) — задает функцию, обрабатывающую движение курсора мыши, когда не зажато ни одной кнопки мыши
  • void glutEntryFunc (void (*func)(int state)) — задает функцию, обрабатывающую движение курсора за пределы окна и его возвращение
  • void glutTimerFunc (unsigned int msecs, void (*func)(int value), value) — задает функцию, вызываемую по таймеру

Затем можно запускать главный цикл glutMainLoop ().

Первая программа


Теперь мы знаем основы работы с OpenGL. Можно написать простую программу для закрепления знаний.

Начнем с того, что нужно подключить заголовочный файл GLUT:

  1. #if defined(linux) || defined(_WIN32)
  2. #include <GL/glut.h>    /*Для Linux и Windows*/
  3. #else
  4. #include <GLUT/GLUT.h>  /*Для Mac OS*/
  5. #endif


Теперь мы уже знаем, что писать в main. Зарегистрируем два обработчика: для рисования содержимого окна и обработки изменения его размеров. Эти два обработчика по сути используются в любой программе, использующей OpenGL и GLUT.

  1. int main (int argc, char * argv[])
  2. {
  3.         glutInit(&argc, argv);
  4.         glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA); /*Включаем двойную буферизацию и четырехкомпонентный цвет*/
  5.        
  6.         glutInitWindowSize(800, 600);
  7.         glutCreateWindow("OpenGL lesson 1");
  8.        
  9.         glutReshapeFunc(reshape);
  10.         glutDisplayFunc(display);
  11.        
  12.         glutMainLoop();
  13.  
  14.         return 0;
  15. }


Теперь надо написать функцию-обработчик изменений размеров окна. Зададим область вывода изображения размером со все окно при помощи команды glViewport (х, у, ширина, высота). Затем загрузим матрицу проекции glMatrixMode (GL_PROJECTION), заменим ее единичной glLoadIdentity () и установим ортогональную проекцию. И наконец загрузим модельно-видовую матрицу glMatrixMode (GL_MODELVIEW) и заменим ее единичной.

В итоге получим:

  1. void reshape(int w, int h)
  2. {
  3.         glViewport(0, 0, w, h);
  4.        
  5.         glMatrixMode(GL_PROJECTION);
  6.         glLoadIdentity();
  7.         gluOrtho2D(0, w, 0, h);
  8.        
  9.         glMatrixMode(GL_MODELVIEW);
  10.         glLoadIdentity();
  11. }


Осталось только написать функцию рисования содержимого окна. Рисовать будем тот квадрат, что я приводил выше в качестве примера. Добавить придется совсем немного кода. Во-первых перед рисованием надо очистить различные буфера при помощи glClear (режим). Используется также как и glutInitDisplayMode. Возможные значения:

  • GL_COLOR_BUFFER_BIT — для очистки буфера цвета
  • GL_DEPTH_BUFFER_BIT — для очистки буфера глубины
  • GL_ACCUM_BUFFER_BIT — для очистки буфера накопления
  • GL_STENCIL_BUFFER_BIT — для очистки трафаретного буфера


В нашем случае нужно очистить только буфер цвета, т.к. другие мы не используем. Во-вторых после рисования нужно попросить OpenGL сменить экранные буфера при помощи glutSwapBuffers (), ведь у нас включена двойная буферизация. Все рисуется на скрытом от пользователя буфере и затем происходит смена буферов. Делается это для получения плавной анимации и для того, чтобы не было эффекта мерцания экрана.

Получаем:

  1. void display()
  2. {
  3.         glClear(GL_COLOR_BUFFER_BIT);
  4.        
  5.         glBegin(GL_QUADS);
  6.         glColor3f(1.0, 1.0, 1.0);
  7.         glVertex2i(250, 450);
  8.         glColor3f(0.0, 0.0, 1.0);
  9.         glVertex2i(250, 150);
  10.         glColor3f(0.0, 1.0, 0.0);
  11.         glVertex2i(550, 150);
  12.         glColor3f(1.0, 0.0, 0.0);
  13.         glVertex2i(550, 450);
  14.         glEnd();
  15.        
  16.         glutSwapBuffers();
  17. }

Итог


Все! Можно компилировать. Должно получиться что-то вроде этого:


Весь код целиком (для кто не осилил статью).

В принципе ничего сложного в этом нет, по крайней мере если вы уже сталкивались с графикой до этого.


OpenGL — удобный инструмент для создания кроссплатформенных приложений, использующий графику. OpenGL легко использовать с тем языком программирования, который вам более удобен. Привязки к OpenGL есть для множества популярных языков, таких как C, C++, C#, Java, Python, Perl, VB и других. Посмотреть информацию о них можно на официальном сайте OpenGL.

via habrahabr

2 comments:

  1. всегда приятно почитать такое

    ReplyDelete
  2. Помню пытался в винде при помощи MinGW собрать программку, использующую OpenGL, так ничего и не вышло.

    ReplyDelete