Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Знакомство с методикой разработки приложений на основе OpenGL

Оригинал: An Introduction to OpenGL Programming
Автор: Mihalis Tsoukalos
Дата публикации: 12 ноября 2014 г.
Перевод: А.Панин
Дата перевода: 7 октября 2015 г.

OpenGL является широко известным, мощным и функциональным стандартном генерации трехмерной, а также двухмерной графики. Тексты различных версий стандарта OpenGL разрабатывается и публикуется Ревизионной комиссией по архитектуре OpenGL (OpenGL Architecture Review Board - ARB).

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

На момент написания данной статьи новейшей версией стандарта OpenGL является версия 4.4, в которой используется техника рисования примитивов, отличная от описанной в данной статье. Тем не менее, данная статья предназначена в первую очередь для знакомства читателя с философией OpenGL, а не для обучения читателя разработке приложений на основе новейшей версии OpenGL. Исходя из вышесказанного, представленный в статье исходный код может быть скомпилирован и использован даже на машинах, работающих под управлением дистрибутивов Linux с устаревшими версиями библиотек OpenGL.

Установка компонентов окружения разработки приложений на основе OpenGL

Если вы выполните в системе Debian 7 следующую команду, предназначенную для поиска всех пакетов программного обеспечения со словом "opengl" в названии, вы получите многострочный вывод (Рисунок 1):

$ apt-cache search opengl

Результат выполнения команды apt-cache search opengl

Рисунок 1. Результат выполнения команды apt-cache search opengl

В Linux существует множество свободных реализаций библиотек OpenGL, но нам понадобится только одна из них. Я установил FreeGLUT, так как данная библиотека является самой современной. Библиотека FreeGLUT является свободной альтернативой библиотеки OpenGL Utility Toolkit (GLUT).

root@mail:~# apt-get install freeglut3 freeglut3-dev libglew-dev
Чтение списков пакетов  Готово
Построение дерева зависимостей
Чтение информации о состоянии  Готово
The following package was automatically installed 
 and is no longer required:
  fonts-liberation
Use 'apt-get autoremove' to remove it.
Будут установлены следующие дополнительные пакеты::
  libgl1-mesa-dev libglu1-mesa-dev libice-dev libpthread-stubs0
  libpthread-stubs0-dev libsm-dev libx11-dev libx11-doc 
  libxau-dev libxcb1-dev libxdmcp-dev libxext-dev libxt-dev 
  mesa-common-dev x11proto-core-dev x11proto-input-dev 
  x11proto-kb-dev x11proto-xext-dev xorg-sgml-doctools xtrans-dev
Предлагаемые пакеты:
  libice-doc libsm-doc libxcb-doc libxext-doc libxt-doc
НОВЫЕ пакеты, которые будут установлены:
  freeglut3 freeglut3-dev libgl1-mesa-dev libglu1-mesa-dev 
  libice-dev libpthread-stubs0 libpthread-stubs0-dev libsm-dev 
  libx11-dev libx11-doc libxau-dev libxcb1-dev libxdmcp-dev 
  libxext-dev libxt-dev mesa-common-dev x11proto-core-dev 
  x11proto-input-dev x11proto-kb-dev x11proto-xext-dev 
  xorg-sgml-doctools xtrans-dev
обновлено 0, установлено 22 новых пакетов, для удаления отмечено 0 пакетов, и 0пакетов не обновлено.
Необходимо скачать 7,651 КБ архивов.
После данной операции, объём занятого дискового пространства возрастёт на 24.9 MB.
Хотите продолжить? [Д/н]

Для компиляции кода приложений на основе OpenGL также необходим компилятор языка C++.

Наконец, в некоторых случаях не обойтись без пакета программного обеспечения mesa-utils, так как в процессе разработки приложений в любом случае придется использовать утилиту glxinfo из его состава.

# apt-get install mesa-utils

Утилита glxinfo выводит полезную информацию об установленных компонентах OpenGL в формате, представленном ниже:

...
GLX version: 1.4
GLX extensions:
    GLX_ARB_get_proc_address, GLX_ARB_multisample, 
    GLX_EXT_import_context, GLX_EXT_texture_from_pixmap, 
    GLX_EXT_visual_info, GLX_EXT_visual_rating,
    GLX_MESA_copy_sub_buffer, GLX_MESA_multithread_makecurrent,
    GLX_OML_swap_method, GLX_SGIS_multisample, GLX_SGIX_fbconfig,
    GLX_SGIX_pbuffer, GLX_SGI_make_current_read
OpenGL vendor string: VMware, Inc.
OpenGL renderer string: Gallium 0.4 on llvmpipe 
  (LLVM 3.4, 128 bits)
OpenGL version string: 2.1 Mesa 10.1.3
OpenGL shading language version string: 1.30
OpenGL extensions:
...

Mesa является библиотекой для работы с трехмерной графикой, предоставляющей API, который практически не отличается API OpenGL.

Конвейер OpenGL

Рисунок 2, взятый из книги с описанием языка шейдеров OpenGL ("The Orange Book"), схематично демонстрирует программируемый конвейер OpenGL с вершинными и фрагментными обработчиками. Как вы видите, конвейер OpenGL является достаточно сложным, но вам не нужно понимать принцип работы каждого из его компонентов для того, чтобы успешно использовать OpenGL. Схематичное изображение конвейера иллюстрирует принцип работы библиотек OpenGL. Конвейеры новых версий OpenGL являются еще более сложными!

Архитектура OpenGL

Рисунок 2. Архитектура OpenGL

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

Код на языке шейдеров OpenGL, который предназначен для исполнения с помощью одного из программируемых процессоров OpenGL, называется шейдером (Shader). Язык шейдеров OpenGL уходит своими корнями в язык программирования C (рассмотрение языка шейдеров OpenGL выходит за рамки данной статьи).

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

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

Рисование треугольника

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

Листинг 1. triangle.cc

// Разработчик: Mihalis Tsoukalos
// Дата: Среда, 04 Июня 2014
//
// Простое приложение на основе OpenGL, рисующее треугольник.

#include "GL/freeglut.h"
#include "GL/gl.h"

void drawTriangle()
{
    glClearColor(0.4, 0.4, 0.4, 0.4);
    glClear(GL_COLOR_BUFFER_BIT);

    glColor3f(1.0, 1.0, 1.0);
    glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

        glBegin(GL_TRIANGLES);
                glVertex3f(-0.7, 0.7, 0);
                glVertex3f(0.7, 0.7, 0);
                glVertex3f(0, -1, 0);
        glEnd();

    glFlush();
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("OpenGL - Creating a triangle");
    glutDisplayFunc(drawTriangle);
    glutMainLoop();
    return 0;
}

Код инициализации OpenGL из Листинга 1 является достаточно длинным, но вам придется разобраться с ним лишь однажды, после чего он может повторно использоваться в других приложениях.

В системе Debian 7 следующая команда позволяет скомпилировать исходный код примера работы с OpenGL из файла triangle.cc без единого сообщения об ошибке:

$ g++ triangle.cc -lglut -o triangle

В системе Ubuntu Linux исполнение этой же команды приводит к выводу следующего сообщения об ошибке:

/usr/bin/ld: /tmp/ccTamBdE.o: неопределённая ссылка на символ "glOrtho"
/usr/lib64/libGL.so.1: error adding symbols: DSO missing from command line
collect2: ошибка: выполнение ld завершилось с кодом возврата 1

Решение данной проблемы заключается в компиляции файла исходного кода программы triangle.cc со связыванием исполняемого файла с дополнительной библиотекой (-lGL):

mtsouk@mtsouk-VirtualBox:/media/sf_OpenGL.LJ/code$ g++ triangle.cc -lglut -lGL -o triangle

Библиотека libGL.so принимает команды OpenGL и выполняет действия, необходимые для отражения результатов их исполнения на экране каким-либо образом. В том случае, если ваша графическая карта не поддерживает функции аппаратного ускорения вывода трехмерной графики, библиотека libGL будет использовать функции программного вывода графики средствами оконной системы X. Аналогичным образом работает и библиотека Mesa. Библиотека libGL может также передавать информацию о командах OpenGL оконной системе X при наличии расширения GLX данной системы. После этого оконная система X может либо осуществлять программную обработку команд с помощью библиотеки Mesa, либо использовать функции аппаратного ускорения.

При исполнении результирующего бинарного файла будет показано окно с треугольником, снимок которого приведен на Рисунке 3. Факт корректной компиляции файла исходного кода triangle.cc является доказательством того, что ваша система Linux может использоваться для разработки приложений, работающих с трехмерной графикой.

Рисование треугольника средствами OpenGL

Рисунок 3. Рисование треугольника средствами OpenGL

Следует помнить о том, что существует более одного способа рисования треугольника с помощью OpenGL, причем различные способы используются в зависимости от используемой версии OpenGL. Несмотря на то, что описанный метод является архаичным приемом использования OpenGL в приложениях, я считаю его достаточно прямолинейным и простым для понимания. Помните о том, что вне зависимости от используемого метода координаты вершин треугольника будут одними и теми же.

Примечание: помните о том, что наиболее важной частью данной статьи является код!

Рисование куба с помощью OpenGL

Теперь давайте создадим приложение, которое будет рисовать куб с помощью OpenGL. Нам придется создать этот куб из треугольников. Куб имеет шесть плоскостей, причем каждая плоскость должна быть создана как минимум из двух треугольников. Причина, по которой я говорю "как минимум" заключается в том, что в общем случае вы можете использовать большее количество треугольников, если желаете придать трехмерному объекту более сглаженную или точную форму, но в случае рисования куба это замечание не актуально. Как вы, надеюсь, поняли, нам придется создать в общей сложности 12 треугольников.

Кроме того, куб имеет восемь вершин. При этом каждая из вершин требует наличия трех других вершин.

В Листинге 2 приведен полный исходный код из файла cube.cc.

Листинг 2. cube.cc

// Разработчик: Mihalis Tsoukalos
// Дата: Среда, 04 Июня 2014
//
// Простая программа на основе OpenGL, которая рисует цветной куб,
// вращающийся при нажатии клавиш со стрелками!
//
// g++ cube.cc -lm -lglut -lGL -lGLU -o cube

#define GL_GLEXT_PROTOTYPES
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#include <math.h>

// Угол поворота по оси X
double rX=0;
// Угол поворота по оси Y
double rY=0;

// Координаты вершин куба
double x = 0.6;
double y = 0.6;
double z = 0.6;

void drawCube()
{
    // Установка цвета фона
    glClearColor(0.4, 0.4, 0.4, 1.0);
    // Очистка экрана
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Сброс значений параметров преобразований
    glLoadIdentity();

    // Вращение объекта при нажатии пользователем клавиш и изменении значений rX и rY
    glRotatef( rX, 1.0, 0.0, 0.0 );
    glRotatef( rY, 0.0, 1.0, 0.0 );

    // Задняя поверхность
        glBegin(GL_TRIANGLES);
            glColor3f(0.4, 0.3, 0.5);
                glVertex3f(x, y, z);
                glVertex3f(x, -y, z);
                glVertex3f(-x, y, z);
        glEnd();

        glBegin(GL_TRIANGLES);
            glColor3f(0.5, 0.3, 0.2);
                glVertex3f(-x, -y, z);
                glVertex3f(x, -y, z);
                glVertex3f(-x, y, z);
        glEnd();

        // Передняя поверхность
        // Используются 4 треугольника!
        glBegin(GL_TRIANGLES);
            glColor3f(0.1, 0.5, 0.3);
                glVertex3f(-x, y, -z);
                glVertex3f(0, 0, -z);
                glVertex3f(-x, -y, -z);
        glEnd();

        glBegin(GL_TRIANGLES);
                glColor3f(0.0, 0.5, 0.0);
                glVertex3f(-x, -y, -z);
                glVertex3f(0, 0, -z);
                glVertex3f(x, -y, -z);
        glEnd();

        glBegin(GL_TRIANGLES);
            glColor3f(0.1, 0.3, 0.3);
                glVertex3f(-x, y, -z);
                glVertex3f(x, y, -z);
                glVertex3f(0, 0, -z);
        glEnd();

        glBegin(GL_TRIANGLES);
                glColor3f(0.2, 0.2, 0.2);
                glVertex3f(0, 0, -z);
                glVertex3f(x, y, -z);
                glVertex3f(x, -y, -z);
        glEnd();

        // Левая поверхность
        glBegin(GL_TRIANGLES);
        glColor3f(0.3, 0.5, 0.6);
                glVertex3f(-x, -y, -z);
                glVertex3f(-x, -y, z);
                glVertex3f(-x, y, -z);
        glEnd();

        glBegin(GL_TRIANGLES);
                glColor3f(0.5, 0.5, 0.5);
                glVertex3f(-x, y, z);
                glVertex3f(-x, -y, z);
                glVertex3f(-x, y, -z);
        glEnd();

        // Правая поверхность
        glBegin(GL_TRIANGLES);
        glColor3f(0.2, 0.2, 0.2);
                glVertex3f(x, y, z);
                glVertex3f(x, y, -z);
                glVertex3f(x, -y, z);
        glEnd();

        glBegin(GL_TRIANGLES);
        glColor3f(0.0, 0.0, 0.0);
                glVertex3f(x, -y, -z);
                glVertex3f(x, y, -z);
                glVertex3f(x, -y, z);
        glEnd();

        // Верхняя поверхность
        glBegin(GL_TRIANGLES);
        glColor3f(0.6, 0.0, 0.0);
                glVertex3f(x, y, z);
                glVertex3f(x, y, -z);
                glVertex3f(-x, y, -z);
        glEnd();

        glBegin(GL_TRIANGLES);
        glColor3f(0.6, 0.1, 0.2);
                glVertex3f(-x, y, z);
                glVertex3f(x, y, z);
                glVertex3f(-x, y, -z);
        glEnd();

        // Нижняя поверхность
        glBegin(GL_TRIANGLES);
        glColor3f(0.4, 0.0, 0.4);
                glVertex3f(-x, -y, -z);
                glVertex3f(-x, -y, z);
                glVertex3f(x, -y, z);
        glEnd();

        glBegin(GL_TRIANGLES);
                glColor3f(0.3, 0.0, 0.3);
                glVertex3f(x, -y, -z);
                glVertex3f(-x, -y, -z);
                glVertex3f(x, -y, z);
        glEnd();

    glFlush();
    glutSwapBuffers();
}

void keyboard(int key, int x, int y)
{
    if (key == GLUT_KEY_RIGHT)
        {
                rY += 15;
        }
    else if (key == GLUT_KEY_LEFT)
        {
                rY -= 15;
        }
    else if (key == GLUT_KEY_DOWN)
        {
                rX -= 15;
        }
    else if (key == GLUT_KEY_UP)
        {
                rX += 15;
        }

    // Запрос обновления изображения
    glutPostRedisplay();
}


int main(int argc, char **argv)
{
        // Инициализация библиотеки GLUT и обработка переданных пользователем параметров
        glutInit(&argc, argv);

        // Запрос создания окна с двойной буферизацией, глубиной цвета true color и буфером глубины
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

        glutInitWindowSize(700,700);
        glutInitWindowPosition(100, 100);

        // Создание окна
        glutCreateWindow("Linux Journal OpenGL Cube");

        // Активация буфера глубины
        glEnable(GL_DEPTH_TEST);

        // Установка функций обратного вызова
        glutDisplayFunc(drawCube);
        glutSpecialFunc(keyboard);

        // Делегирование полномочий по отслеживанию событий библиотеке GLUT
        glutMainLoop();

        return 0;
}

На Рисунке 4 показаны два различных снимка окна приложения на основе исходного кода из файла cube.cc. На снимке слева показан простой квадрат, так как при запуске приложения вы видите лишь переднюю поверхность куба и не можете видеть другие пять поверхностей. Таким образом, показанный на экране объект выглядит как двухмерный. После того, как вы начнете вращать куб с помощью клавиш со стрелками, вы можете с определенностью сказать, что объект является трехмерным.

Графический вывод приложения на основе исходного кода из файла cube.cc

Рисунок 4. Графический вывод приложения на основе исходного кода из файла cube.cc

Подробное рассмотрение кода

Каждый треугольник описывается с помощью координат трех вершин. Каждая из этих вершин по своей сути является отдельной точкой (x,y,z). Каждая точка описывается с помощью трех чисел (координат), так как в OpenGL используется трехмерное пространство координат. Для создания куба необходимо описать параметры восьми вершин.

Для иллюстрации возможности создания более сложных трехмерных объектов передняя поверхность куба создана из четырех треугольников. На Рисунке 5 показаны координаты вершин четырех треугольников передней поверхности куба. Центральная точка (0,0,-0.6) была выбрана произвольным образом. Для формирования поверхности подойдет любая точка, находящаяся на этой поверхности.

Координаты вершин треугольников передней поверхности куба

Рисунок 5. Координаты вершин треугольников передней поверхности куба

Описание параметров вершин:

На Рисунке 6 показаны координаты вершин куба для точки отсчета с координатами x=0.6, y=0.6 и z=0.6. Обратите внимание на то, что вершины на концах каждого из ребер куба имеют по две одинаковые координаты из трех.

Вершины куба

Рисунок 6. Вершины куба

Как вы можете видеть на Рисунке 6, координаты четырех вершин передней поверхности куба отличаются от координат четырех вершин задней поверхности куба лишь значениями по оси z.

Описание параметров треугольников:

Треугольники строятся на основании параметров их вершин. Для построения каждого треугольника необходимо описать параметры трех вершин. Для построения каждой из поверхностей куба необходимы два треугольника за исключением передней поверхности, для построения которой были использованы четыре треугольника. Следующие команды предназначены для создания окрашенного треугольника на основе координат x, y и z.

glBegin(GL_TRIANGLES);
glColor3f(0.4, 0.0, 0.4);
        glVertex3f(-x, -y, -z);
        glVertex3f(-x, -y, z);
        glVertex3f(x, -y, z);
glEnd();

Изменение цвета фигуры:

Вы можете изменить цвет фигуры с помощью команды glColor3f(...). Команда glColor3f(...) принимает три параметра, которые представляют RGB-составляющие желаемого цвета.

Изменение перспективы:

Вы можете изменить перспективу сцены с помощью следующих команд:

glRotatef(rX, 1.0, 0.0, 0.0);
glRotatef(rY, 0.0, 1.0, 0.0);

Угол перспективы меняется в соответствии с тем, как пользователь нажимает клавиши со стрелками.

Рисование куба:

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

После того, как вы получите корректные координаты треугольников, их рисование не будет представлять особых сложностей. Рисование каждого треугольника должно начинаться с вызова команды glBegin(GL_TRIANGLES) и заканчиваться вызовом команды glEnd(). GL_TRIANGLES является идентификатором примитива OpenGL. Существуют и другие идентификаторы примитивов: GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP и GL_POLYGON. При этом в конечном счете каждый из примитивов OpenGL формируется из одного или нескольких треугольников.

Если вы рисуете фигуры с помощью треугольников (GL_TRIANGLES), порядок рисования вершин не важен. Если же вы рисуете фигуры с помощью прямоугольников (GL_POLYGON), рисование четырех вершин должно осуществляться в корректной последовательности, причем не важно, будет ли осуществляться обход вершин по часовой стрелке или против часовой стрелки. В том случае, если порядок рисования вершин будет нарушен, ваш прямоугольник не будет целостным.

Использование клавиш со стрелками:

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

void keyboard(int key, int x, int y)
{
    if (key == GLUT_KEY_RIGHT)
        {
                rY += 15;
        }
    else if (key == GLUT_KEY_LEFT)
        {
                rY -= 15;
        }
    else if (key == GLUT_KEY_DOWN)
        {
                rX -= 15;
        }
    else if (key == GLUT_KEY_UP)
        {
                rX += 15;
        }

    // Запрос обновления изображения
    glutPostRedisplay();
}

Функция обратного вызова keyboard(...) зарегистрирована в рамках функции main(...) с помощью следующей строки кода:

glutSpecialFunc(keyboard);

Автоматическое вращение куба

В качестве бонуса давайте рассмотрим пример автоматического вращения куба (Рисунок 7).

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

Примечание: любой объект может быть разделен на отдельные треугольники, но треугольник не может быть разделен на что-либо, кроме треугольников.

Графический вывод приложения на основе исходного кода из файла rotateCube.cc

Рисунок 7. Графический вывод приложения на основе исходного кода из файла rotateCube.cc

В Листинге 3 приведен исходный код приложения из фала rotateCube.cc

Листинг 3. rotateCube.cc

// Разработчик: Mihalis Tsoukalos
// Дата: Среда, 04 Июня 2014
//
// Простая программа на основе OpenGL, которая рисует
// автоматически вращающийся куб.
//
// g++ rotateCube.cc -lm -lglut -lGL -lGLU -o rotateCube

#include <iostream>
#include <stdlib.h>

// должно осуществляться корректное связывание библиотек GLUT и OpenGL
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

using namespace std;

// Координаты вершин куба
double x = 0.6;
double y = 0.6;
double z = 0.6;

float angle = 0.0;

void drawCube()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Сброс значений параметров преобразований
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glTranslatef(0.0, 0.0, -5.0);

        // Добавление рассеянного освещения
        GLfloat ambientColor[] = {0.2, 0.2, 0.2, 1.0};
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);

        // Добавление точечного освещения
        GLfloat lightColor0[] = {0.5, 0.5, 0.5, 1.0};
        GLfloat lightPos0[] = {4.0, 0.0, 8.0, 1.0};
        glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0);
        glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);

        glTranslatef(0.5, 1.0, 0.0);
        glRotatef(angle, 1.0, 1.0, 1.0);

    glRotatef( angle, 1.0, 0.0, 1.0 );
    glRotatef( angle, 0.0, 1.0, 1.0 );
        glTranslatef(-0.5, -1.0, 0.0);

        // Создание трехмерного куба

    // Задняя поверхность
    glBegin(GL_POLYGON);
    glColor3f(0.5, 0.3, 0.2);
    glVertex3f(x, -y, z);
    glVertex3f(x, y, z);
    glVertex3f(-x, y, z);
    glVertex3f(-x, -y, z);
    glEnd();

        // Передняя поверхность
        glBegin(GL_POLYGON);
        glColor3f(0.0, 0.5, 0.0);
        glVertex3f(-x, y, -z);
        glVertex3f(-x, -y, -z);
        glVertex3f(x, -y, -z);
        glVertex3f(x, y, -z);
        glEnd();

        // Левая поверхность
        glBegin(GL_POLYGON);
        glColor3f(0.5, 0.5, 0.5);
        glVertex3f(-x, -y, -z);
        glVertex3f(-x, -y, z);
        glVertex3f(-x, y, z);
        glVertex3f(-x, y, -z);
        glEnd();


        // Правая поверхность
        glBegin(GL_POLYGON);
        glColor3f(0.0, 0.0, 0.0);
        glVertex3f(x, -y, -z);
        glVertex3f(x, -y, z);
        glVertex3f(x, y, z);
        glVertex3f(x, y, -z);
        glEnd();

        // Верхняя поверхность
        glBegin(GL_POLYGON);
        glColor3f(0.6, 0.0, 0.0);
        glVertex3f(x, y, z);
        glVertex3f(-x, y, z);
        glVertex3f(-x, y, -z);
        glVertex3f(x, y, -z);
        glEnd();


        // Нижняя поверхность
        glBegin(GL_POLYGON);
        glColor3f(0.3, 0.0, 0.3);
        glVertex3f(-x, -y, -z);
        glVertex3f(-x, -y, z);
        glVertex3f(x, -y, z);
        glVertex3f(x, -y, -z);
        glEnd();

        glFlush();
    glutSwapBuffers();
}

// Функция для плавного увеличения значения переменной угла поворота, 
// которыое находится в диапазоне <= 360
// Данная функция может быть реализована с помощью оператора деления с остатком.
void update(int value)
{
        angle += 1.0f;
        if (angle > 360)
                {
                        angle -= 360;
        }

        glutPostRedisplay();
        glutTimerFunc(25, update, 0);
}

// Инициализация системы вывода трехмерной графики
void initRendering()
{
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_COLOR_MATERIAL);

        // Установка цвета фона
        glClearColor(0.7f, 0.8f, 1.0f, 1.0f);
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_NORMALIZE);
}


// Вызывается при изменении размера окна
void handleResize(int w, int h)
{
        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0, (double)w / (double)h, 1.0, 200.0);
}


int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(700, 700);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("OpenGL - Rotating a Cube");
        initRendering();

    glutDisplayFunc(drawCube);
        glutReshapeFunc(handleResize);

        // Добавление таймера, при срабатывании которого будет вызываться функция update(...)
    glutTimerFunc(25, update, 0);

    glutMainLoop();
    return 0;
}

Обратите внимание на то, что реализации функции main(...) в файлах исходного кода triangle.cc, cube.cc и rotateCube.cc очень схожи, несмотря на то, что три программы предназначены для выполнения различных задач.

Наиболее важным аспектом данного приложения является использование функции glutTimerFunc(...). Данная функция позволяет зарегистрировать функцию обратного вызова таймера update(...), которая будет периодически вызываться по прошествии заданного количества миллисекунд. Функция update(...) изменяет угол сцены при каждом вызове.

Заключение

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

Благодарности

Я хотел бы выразить признательность доктору Nikos Platis за то, что но поделился со мной небольшой частью своих знаний, относящихся к технологии OpenGL.

Дополнительные ресурсы

Официальный веб-сайт OpenGL: http://www.opengl.org

Книга "Learning Modern 3D Graphics Programming": http://www.arcsynthesis.org/gltut

Книга "OpenGL Superbible, 6th edition, Graham Sellers, Richard S. Wright and Nicholas Haemel, Addison Wesley, ISBN: 0321902947"

Веб-сайт библиотеки GLEW: http://glew.sourceforge.net

Веб-сайт библиотеки для работы с трехмерной графикой Mesa 3D: http://www.mesa3d.org