ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 12.01.2024
Просмотров: 189
Скачиваний: 3
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
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 будет рисовать кадры в зависимости от этой переменной.
2.9 Упражнение:" Игра Арканоид"
Напишите игру "Arconoid" - летающий шарик, снизу подставка, пользователь стрелками или мышкой управляет подставкой и отбивает шарик.
Исходный файл смотрите здесь. Исполняемый файл здесь.
2.10 Резюме
На этом эта глава книги закончена. Вы научились рисовать трехмерные объекты и делать интерактивную анимацию, используя эти объекты. Этого, в общем-то, достаточно для написания примитивных программ.
Chapter 3
Рисуем простые объекты
3.1 Общие положения
Точки, линии, треугольники,четырехугольники, многоугольники - простые объекты, из которых состоят любые сложные фигуры. Все они рисуются следующим образом:
glBegin(GLenum type); // указываем, что будем рисовать
glVertex[2 3 4][s i f d](...); // первая вершина
………..
// тут остальные вершины
glVertex[2 3 4][s i f d](...); // последняя вершина
glEnd(); // закончили рисовать примитив
Сначала вы говорите, что будете рисовать - glBegin с соответсвующим параметром. Возможные значения type см. ниже. Далее вы указываете вершины, определяющие объкты указанного типа. И наконец, вы вызваете glEnd, чтобы указать, что вы закончили рисовать объекты типа, указанного в glBegin.
3.2 Точки
\\
// рисуем точки
glPointSize(2);
glBegin(GL_POINTS);
glColor3d(1,0,0);
glVertex3d(-4.5,4,0); // первая точка
glColor3d(0,1,0);
glVertex3d(-4,4,0); // вторая точка
glColor3d(0,0,1); // третья
glVertex3d(-3.5,4,0);
glEnd();
glPointSize(5);
glBegin(GL_POINTS);
glColor3d(1,0,0);
glVertex3d(-2,4,0); // первая точка
glColor3d(0,1,0);
glVertex3d(-1,4,0); // вторая точка
glColor3d(0,0,1); // третье
glVertex3d(0,4,0);
glEnd();
glPointSize(10);
glEnable(GL_POINT_SMOOTH);