©2018 Учебные документы
Рады что Вы стали частью нашего образовательного сообщества.

Opengl игорь Тарасов Часть I основы OpenGL


OpenGL

Игорь Тарасов

Часть I
Основы OpenGL

Глава 1. Введение.


Глава 2. Быстрый старт
Глава 3. Рисуем простые объекты
Глава 4. Полезные мелочи
Глава 5. Работа с картинками
Глава 6. Освещение и все, что с ним связано
Глава 7. Инициализация или как написать приложение с нуля
Глава 8. Примеры интересных программ
Приложение A. Где взять OpenGL и другое ПО
Библиография
Загрузка исходных файлов и поддержка книги

Chapter 1
Введение.

1.1  Что это такое и кому это нужно?

OpenGL - Open Graphics Languge, открытый графический язык. Термин "открытый" - значит независимый от производителей. Имеется спецификация OpenGL, где все четко задокументировано и описано. Библиотеку OpenGL может производить кто-угодно. Главное, чтобы библиотека удовлетворяла спецификации OpenGL и ряду тестов. Как следствие, в библиотеке нет никаких темных мест, секретов, недокументированных возможностей и т.п. Библиотеку выпускают такие корпорации, как Microsoft, Silicon Graphics, а также просто группы программистов. Одним из таких примеров служит реализация Mesa. Эту библиотеку написали целый ряд программистов, главным автором является Brian Paul. Библиотека Mesa распространяется в исходных текстах на языке Си и собирается почти для любой операционной системы. Стандарт OpenGL развивается с 1992 года. Он разрабатывается фирмой Silicon Graphics. С тех пор библиотека завоевала огромную популярность и была интегрирована со множеством языков и систем разработки приложений. Вы можете писать программу на Си,С++,Pascal,Java и многих других языках. Существуют также объектно-ориентированные библиотеки OpenGL.

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

1.2  О книге

Эта книга является самоучителем по популярной библиотеке OpenGL. Изначально задумывалась некая брошюра, что-то типа небольшого самоучителя. Потом проект сильно разросся, и я подумал: "А почему бы не написать книгу?". Я рассчитываю, что вы уже неплохо знакомы с программированием, в частности, с языком Си. Здесь, в основном, будет рассматриваться написание программ в среде Windows. Рассмотрю, конечно же, и особенности использования OpenGL в операционной системе Unix. Я опишу несколько различных типов приложений. Первый тип - это консольное приложение win32 с использованием библиотеки glaux.lib. Второе - обычное оконное приложение(Win32 Application). Третье - это приложение на базе MFC с архитектурой документ/вид. Четвертое - я покажу, как писать Java-апплеты с использованием библиотеки Magician. Magician - это библиотека java-классов с функциями, идентичными OpenGL. И пятое - Unix-приложение. Различия в написании этих приложений проявляются в начальной инициализации, префиксах и суффиксах названий функций. В остальном, на 90% все везде одинаково. Книга состоит из двух частей - "Основы OpenGL" и "OpenGL для профессионалов". Первая часть является скорее учебником, вторая - справочником по малоизвестным функциональным возможностям OpenGL. Я хотел сделать каждую главу книги самодостаточной. Насколько мне это удалось, судить вам. Но я бы рекомендовал читать книгу последовательно. Главное, на что я делал упор, так это на простоту и понятность. Все примеры очень простые, и их размер не превышает нескольких килобайт. Все написано на чистом Си. В основу книги положен реальный опыт работы. И последнее, что надо сказать здесь, я не претендую на полноту и детальную точность изложения материала. Книга ставит задачей научить читателя элементарным вещам, и поэтому здесь все объясняется простым и понятным языком на конкретных примерах с точки зрения практика, а не теоретика.Более точную информацию вы сможете найти в спецификации по OpenGL и на сайте Silicon Graphics http://www.sgi.com/software/opengl. Книга написана при помощи пакета LATEX2e в ОС Linux.


1.3  Об авторе

Со мной можно связаться по электронной почте:

* FidoNet: 2:5020/370.2 620.20 1103.5

* Inet: itarasov@rtuis.miem.edu.ru

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

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

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


Chapter 2
Быстрый старт

2.1  Устанавливаем OpenGL

Начнем с самого главного, установим необходимое программное обеспечение. Я предполагаю, что Windows у Вас уже установлен. Во-первых, установите MSVisualC++6.0 и jdk113 или выше. Во-вторых, нам понадобится реализация библиотеки OpenGL. Она входит в поставку Windows95/NT - это билиотеки opengl32.dll & glu32.dll. Вы также можете взять библиотеки OpenGL от Silicon Graphics. Инструкция по установке там прилагается. Вам придется скопировать opengl.dll и glu.dll в windows\system и положить opengl.lib, glu.lib в подкатолог Lib, где установлено MSVisualC++. В-третьих, вам понадобятся четыре моих программы-шаблона, которые представляют собой начальный скелет, который мы потом будем наполнять функциональностью. Где взять OpenGL от Silicon Graphics, Magician, jdk и мои программы-шаблоны, смотри в приложение 'А'.

2.2  Давайте что-нибудь изобразим


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

Запустите MSVisualC++6.0

Щелкните меню File->New->Win32 Console Application.

Выберете каталог и имя проекта, впишите - sphere, щелкните OK.
Я все свои проекты держу на диске D в каталоге Projects. Projects ветвится дальше на подкатологи с базами данных, утилитами, графикой и Java-приложениями. В общем, старайтесь присваивать разумные имена и вести разумную структуру каталогов. Это очень серьезная проблема. У меня некоторые ребята не учли моих советов, и через две недели у них в домашних директориях образовался такой беспорядок, что они сами перестали понимать, что и где у них лежит.
Выберете An Empty Project, щелкните Finish.

Cкопируйте в каталог вашего проекта мой шаблон console.c и переименуйте его в sphere.c

Присоедините его к проекту. Project->Add To Project->Files

Щелкните Build->Set Active Configuration и установите тип проекта sphere - Win32 Release

Далее, щелкайте Project->Settings->Link->Object/library modules и добавьте туда opengl32.lib, glu32.lib и glaux.lib

Вставьте в функцию display следующий код:


glColor3d(1,0,0);

auxSolidSphere(1);

Теперь откомпилируйте и запустите Вашу программу.
Меню Build->Execute Sphere.exe

Исходный файл смотрите здесь. Исполняемый файл здесь.


glColor3d устанавливает текущий цвет, которым будут рисоваться фигуры. Тут нужно пояснить, как устанавливается цвет и общюю философию в названии функций OpenGL. Цвет устанавливается четырьмя параметрами: красный, синий, зеленый и прозрачность. Эти параметры вариируются в диапозоне от нуля до единицы. Четвертый параметр нам пока не нужен, поэтому мы вызвали glColor с тремя параметрами. В этом случае, значение четвертого параметра, прозрачности, по умолчанию считается равным единице, т.е. абсолютно непрозрачным, ноль - будет абсолютно прозрачным. Так как в языке Си нет перегрузки функций, то применяется следующий синтаксис вызова функций - FuncionName[n=число параметров][тип параметров]. Доступны следующие типы:


  • b - GLbyte байт

  • s - GLshort короткое целое

  • i - GLint целое

  • f - GLfloat дробное

  • d - GLdouble дробное с двойной точностью

  • ub - GLubyte беззнаковый байт

  • us - GLushortбеззнаковое короткое целое

  • ui - GLuint беззнаковое целое

  • v - массив из n параметров указанного типа

В нашем случае - glColor3d - означает, что в функцию передается три параметра типа GLdouble. Также можно было вызвать glColor3i, т.е. три параметра типа GLint. Если тип параметров короткое целое, целое или байт, то компонента цвета приводится к диапазону [0,1]. Или же glColor3dv означает, что в качестве параметров передается массив из трех элементов типа GLdouble. Например:


double array[] = {0.5, 0.75, 0.3, 0.7};

...


glColor3dv(array);

glColor3ub(200,100,0);// приводится к 200/256, 100/256, 0,256

auxSolidSphere - рисует сферу в начале координат и единичным радиусом.

2.3  Упражнение: "Трехмерные фигуры"

Замените функцию auxSolidSphere на функцию, из указанных ниже с соответсвующими параметрами. Значения параметров устанавливайте порядка единицы - 0.5-1.7. Если вы укажете слишком маленький размер, фигуру будет плохо видно; если слишком большой, то она получится урезанной. Это связано с тем, что ее край, как бы вылезет из монитора.


  • auxSolidCube(width) // куб

  • auxSolidBox(width, height, depth) // коробка

  • auxSolidTorus(r,R) // тор

  • auxSolidCylinder(r,height) // цилиндер

  • auxSolidCone(r,height) // конус

  • auxSolidIcosahedron(width) // многогранники

  • auxSolidOctahedron(width)

  • auxSolidTetrahedron(width)

  • auxSolidDodecahedron(width)

  • auxSolidTeapot(width) // рисует чайник

С помошью выше указанных функций вы можете рисовать сплошные фигуры. Если вам надо нарисовать проволочную, то вместо Solid пишите Wire. Пример: auxWireCube(1) // рисует проволочный куб.


2.4  Переход к новым координатам

Продолжим рисовать трехмерные фигуры. В предыдущем параграфе вы научились рисовать примитивные трехмерные объекты. Но проблема в том, что они рисуются только в начале координат, т.е. в точке (0,0,0). Для того чтобы изобразить сферу в точке ( x0,y0,z0 ), надо переместить начало координат в эту точку, т.е. надо перейти к новым координатам. Эта процедура довольно распространенная при программировании графики и анимации. Часто, бывает очень удобно, сместить координаты в новую точку и повернуть их на требуемый угол, и ваши расчеты резко упростятся. Конктреный пример мы рассмотрим ниже, когда научимся программировать анимацию. А пока вы узнаете, как переходить к новым координатам. Для перехода к новым координатам в OpenGL есть две функции:


  • glTranslated( Dx,Dy,Dz )

  • glRotated(j,x0,y0,z0 )

Первая функция сдвигает начало системы координат на ( Dx,Dy,Dz ). Вторая - поворачивает на угол j против часовой стрелки вокруг вектора (x0,y0,z0)

Теперь, стоит сказать еще о двух очень важных функциях:


  • glPushMatrix()

  • glPopMatrix()

Они предназначены для сохранения и восстановления текущих координат. Я не стал здесь приводить пример на то, как неудобно переходить от одной системы координат к другой и помнить все ваши переходы. Гораздо удобнее с помощью glPushMatrix() сохранить текущие координаты, потом сдвигаться, вертеться, как вам угодно, а после, вызывом glPopMatrix вернуться к старым координатам.

Итак, настало время поэкспериментировать. Создайте новый проект, повторив пункты из раздела 2.2. Только назовите его sphere2. Вставьте в функцию display следующий код:
glPushMatrix(); // сохраняем текущие координаты
glTranslated(1.4,0,0); // сдвигаемся по оси Х на 1.4

glColor3d(0,1,0);

auxSolidSphere(0.5); // рисуем сферу в (1.4,0,0) в абсолютных координатах
glTranslated(1,0,0); // еще раз сдвигаемся

glColor3d(0,0,1);

auxSolidSphere(0.3);
glPopMatrix(); // возвращаемся к старой системе координат
glColor3d(1,0,0);

auxSolidSphere(0.75); // рисуем сферу в точке (0,0,0) в абсолютных координатах


Теперь, откомпилируйте и запустите вашу программу.
Меню Build->Execute Sphere.exe

Исходный файл смотрите здесь. Исполняемый файл здесь.


В общем-то, из комментариев многое понятно. Обращаю ваше внимание только на два последних вызова auxSolidSphere. Перед вызовом glPopMatrix сфера рисуется в точке (2,0,0), а после, в точке (0,0,0).

2.5  Упражнение: "Cписок трехмерных фигур"

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

Примечание: тут хочу заметить, что в версии glaux.lib от фирмы Microsoft имеется следующий баг: цилиндр и конус рисуются всегда либо проволочными, либо сплошными. Если вы первый цилиндр/конус в программе нарисовали проволочным, то далее все цилиндры/конусы будут проволочными. Соответсвенно, если первой была сплошная фигура, то далее все будут сплошные. Поэтому, не стоит паниковать. Это ошибка Microsoft. Могу также вас порадовать, что ниже я расскажу, как обойти эту проблему.

Исходный файл смотрите здесь. Исполняемый файл здесь.

2.6  Поворот координат

Создайте новый проект с именем Rotate. Переименуйте glaux.c в rotate.c В функцию display вставьте следующий код:


glColor3d(1,0,0);

auxSolidCone(1, 2); // рисуем конус в центре координат
glPushMatrix(); // сохраняем текущие координаты

glTranslated(1,0,0); // сдвигаемся в точку (1,0,0)

glRotated(75, 1,0,0); // поворачиваем систему координат на 75 градусов

glColor3d(0,1,0);

auxSolidCone(1, 2); // рисуем конус

glPopMatrix(); // возвращаемся к старым координатам


Как видите, конус повернулся в абсолютных координатах. Так что, для того, чтобы нарисовать фигуру

не

в начале координат, надо:
сохранить текущие координаты

сдвинуть(glTranslated), повернуть(glRotated)

нарисовать то, что хотели

вернуться к старым координатам


Вызовы glPushMatrixglPopMatrix могут быть вложенными, т.е.:
glPushMatrix();

...


glPushMatrix();

glPopMatrix();

...

glPopMatrix();


Естественно число вызовов glPopMatrix должно соответствовать числу вызовов glPushMatrix.

2.7  Упражнение: "Снеговик"

Используя функцию glRotate, нарисуйте снеговика. Три сферы, шапка - конус, нос - тоже конус, глаза - сфера, рот можно квадратным сделать - glBox.

Исходный файл смотрите здесь. Исполняемый файл здесь.
Примечание: Имеется еще один баг в glaux.lib от Microsoft. Кажется, последний из известных мне. Функция aux[Solid/Wire]Cylinder прежде, чем нарисовать цилиндр, сдвигает и поворачивает координаты. Так что, если вы уже сместили и повернули координаты, то цилиндр нарисуется совсем не там, где вы рассчитывали. Люди из Microsoft, конечно же, будут уверять, что это особенность, и предложат вам скачать очередной ServicePack.;-) А я ниже расскажу, как более правильно рисовать цилиндры и конусы. Если вам некогда ждать, то далее приведен исправленный код этих функций. Большинство из вас сейчас пропустят его и перейдут к следующему очень интересному разделу - "Анимация". И правильно сделаете. К исправлению ошибок вернетесь, когда немного освоитесь. Но я все же решил привести код по исправлению ошибок Microsoft именно здесь. Можете пересобрать glaux.lib, заменив соответсвующий код в файле shapes.c. Где взять исходники, смотрите в приложение 'A'. По-моему, они есть в MSDN.

void auxWireCylinder (GLdouble radius, GLdouble height)

{

GLUquadricObj *quadObj;

GLdouble *sizeArray, *tmp;

GLuint displayList;


sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);

tmp = sizeArray;

*tmp++ = radius;

*tmp++ = height;

displayList = findList (CYLINDERWIRE, sizeArray, 2);
if (displayList == 0) {

glNewList(makeModelPtr (CYLINDERWIRE, sizeArray, 2),

GL_COMPILE_AND_EXECUTE);

quadObj = gluNewQuadric ();

gluQuadricDrawStyle (quadObj, GLU_LINE);

gluCylinder (quadObj, radius, radius, height, 12, 2);

glEndList();

}

else {

glCallList(displayList);

free (sizeArray);

}

}
void auxSolidCylinder (GLdouble radius, GLdouble height)

{

GLUquadricObj *quadObj;

GLdouble *sizeArray, *tmp;

GLuint displayList;


sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);

tmp = sizeArray;

*tmp++ = radius;

*tmp++ = height;

displayList = findList (CYLINDERSOLID, sizeArray, 2);
if (displayList == 0) {

glNewList(makeModelPtr (CYLINDERSOLID, sizeArray, 2),

GL_COMPILE_AND_EXECUTE);

quadObj = gluNewQuadric ();

gluQuadricDrawStyle (quadObj, GLU_FILL);

gluQuadricNormals (quadObj, GLU_SMOOTH);

gluCylinder (quadObj, radius, radius, height, 12, 2);

glEndList();

}

else {


glCallList(displayList);

free (sizeArray);

}

}
void auxSolidCone (GLdouble base, GLdouble height)

{

GLUquadricObj *quadObj;

GLdouble *sizeArray, *tmp;

GLuint displayList;


sizeArray = (GLdouble *) malloc (sizeof (GLdouble) * 2);

tmp = sizeArray;

*tmp++ = base;

*tmp++ = height;

displayList = findList (CONESOLID, sizeArray, 2);
if (displayList == 0) {

glNewList(makeModelPtr (CONESOLID, sizeArray, 2),

GL_COMPILE_AND_EXECUTE);

quadObj = gluNewQuadric ();

gluQuadricDrawStyle (quadObj, GLU_FILL);

gluQuadricNormals (quadObj, GLU_SMOOTH);

gluCylinder (quadObj, base, (GLdouble)0.0, height, 15, 10);

glEndList();

}

else {


glCallList(displayList);

free (sizeArray);

}

}

2.8  Анимация

Давайте оживим нашего снеговика и добавим интерактивность. Для этого надо отрисовывать кадры и реагировать на внешние события от клавиатуры или мыши.

Для отрисовки кадров их надо как-то различать. Для этого мы в функции display вводим переменную time типа int с модификатором static. Создайте новый проект и в функцию display введите следующее: Яtatic int time=0;". Модификатор static означает, что значение переменной будет сохраняться при выходе из функции. Начальное значение мы устанавливаем в ноль. Если функция display не зависит от времени, то счетчик кадров можно и не вести. Теперь добавьте следующие строчки:


glPushMatrix();

glTranslated( ((double)time)/100.0 ,0,0);

... // здесь код из предыдущего упражнения "Cнеговик"

glPopMatrix();


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

Далее мы рассмотрим пример с более сложной анимацией. Здесь вращается тор и его ось вращения. Я приведу исходный текст с подробными комментариями. Пример программы "Гироскоп":


Исходный файл смотрите здесь. Исполняемый файл здесь.


glPushMatrix(); // сохраняем текущие координаты, т.к. при выходе

// из функции нам нужно вернуться к абсолютным координатам

// попробуйте закомментировать вызов glPushMatrix

// здесь и glPopMatrix в конце и вы увидите, что из этого получится
glRotated(time/2, 0.0, 1.0, 0.0); // поворачиваем координаты

glLineWidth(5); // устанавливаем толщину линии - пять

glColor3f(1,0,0); // устанавливаем текущий цвет - красный
glBegin(GL_LINES); // рисуем красную ось

glVertex3d(-0.3,0,0); // т.к. мы повернули координаты,

glVertex3d(1.5,0,0); // ось будет повернута в абсолютных

glEnd(); // координатах, и т.к. функция display вызывается в цикле, и переменная

// time увеличивается на единицу

// при каждом вызове display, мы

// получаем анимацию - вращающуюся ось
glPushMatrix(); // теперь относительно повернутых координат

// мы переходим к новым координатам

glRotated(2*time, 1,0,0); // поворачиваем их на угол 2*time вокруг красной оси

glTranslated(-0.3,0,0); // и сдвигаем на край оси

glColor3f(0,0,1); // устанавливаем синий цвет

glPushMatrix(); // теперь еще раз переходим к новым координатам,

glRotated(90,0,1,0); // чтобы развернуть тор на 90 градусов

// у вас крыша еще не поехала?

// немного?

// то-то же, тут не так все просто, но если понять что и как, то

// очень удобно программировать графику

glLineWidth(1);

auxWireTorus(0.2, 0.7);

glPopMatrix(); // нарисовав тор, мы возвращаемся к предыдущим

// координатам, если забыли, начало этих координат

// лежит на конце красной оси


glLineWidth(7);

glColor3f(0,1,0);


glBegin(GL_LINES); // теперь рисуем три зеленых линиии

glVertex3d(0,0,0); // что тут и как рисуется, я поясню в

glVertex3d(0,1,0); // следующей

glVertex3d(0,0,0); // главе, сейчас это не так важно

glVertex3d(0,-0.5,1);

glVertex3d(0,0,0);

glVertex3d(0,-0.5,-1);

glEnd();


glPopMatrix(); // теперь возвращаемся к повернутым координатам на угол time/2

glPopMatrix(); // ну и переходим к абсолютным координатам

time++; // увеличиваем счетчик кадров или вермя, называйте как хотите, на единицу

Как вы, наверное, догадались, надо создать очередной проект, скопировать туда мой шаблон, добавить его в проект, указать библиотеки opengl32.lib glu32.lib glaux.lib в Project->Setting->Link->Settings->Link->Object/library modules:, вставить этот код в функцию display. Еще вам нужно в начале функции display вставить строку static int time=0; и закомментировать строчку glEnable(GL_LIGHTING) в функции main.

Запустив это приложение, вы увидите, как оно работает. Теперь закомментируйте соответсвующие друг другу вызовы glPushMatrix и glPopMatrix и посмотрите на результат. Я бы рекомендовал такой способ для изучения и понимания работы программы: вы комментируете куски кода и смотрите, что получается.

Для того, чтобы реагировать на клавиатуру или мышь, вы должны определить функцию, которая будет вызываться при поступление событий от клавиатуры или мыши. Для кнопки клавиатуры вы определяете функцию со следующим прототипом void CALLBACK FunctionName(void) и устанавливаете ее как обработчик определенной кнопки - auxKeyFunc(key_code, FunctionName); key_code смотри в glaux.h. Пример: auxKeyFunc(AUX_LEFT, FunctionName) Здесь вы устанавливаете FunctionName как функцию, которая будет вызываться при нажатии на клавиатуре клавиши "стрелка влево".

Для мыши вы устанавливаете свою функцию обработки событий мыши вызовом функции auxMouseFunc(int button,int action,AUXMOUSEPROC). Переменная button может принимать значения - AUX_LEFTBUTTON, AUX_MIDDLEBUTTON, AUX_RIGHTBUTTON. Переменная action принимает следующие значения - AUX_MOUSEDOWN, AUX_MOUSEUP, AUX_MOUSELOC. Функция AUXMOUSEPROC должна иметь прототип - void CALLBACK FunctionName(AUX_EVENTREC *event), где AUX_EVENTREC определено как
typedef struct _AUX_EVENTREC

{

GLint event;

GLint data[4];

}AUX_EVENTREC;


Для более детальной информации смотрите справочные руководства и исходные тексты библиотеки OpenGL Auxiliary library. Эта книга об OpenGL, а не о программировании интерфейсов пользователя. Поэтому, за подобной информацией вам придется лезть в соответсвующие справочные руководства по Win API, MFC, Java и т.п.

В FunctionName вы можете изменить какую-нибудь глобальную переменную, и, соответсвенно, функция display будет рисовать кадры в зависимости от этой переменной.


?


operating-instructions-16.html

operating-instructions-20.html

operating-instructions-25.html

operating-instructions-3.html

operating-instructions-8.html