Текстуры Наложение текстуры на поверхность объектов сцены повышает ее реалистичность, однако при этом надо учитывать, что этот процесс требует значительных вычислительных затрат. Под текстурой будем понимать некоторое изображение, которое надо определенным образом нанести на объект. Для этого следует выполнить следующие этапы: выбрать изображение и преобразовать его к нужному формату загрузить изображение в память определить, как текстура будет наноситься на объект и как она будет с ним взаимодействовать. Рассмотрим каждый из этих этапов. Подготовка текстуры Принятый в OpenGL формат хранения изображений отличается от стандартного формата Windows DIB только тем, что компоненты (R,G,B) для каждой точки хранятся в прямом порядке, а не в обратном и выравнивание задается программистом. Считывание графических данных из файла и их преобразование можно проводить и вручную, однако удобней воспользоваться функцией, входящей в состав библиотеки GLAUX (для ее использования надо дополнительно подключить glaux.lib), которая сама проводит необходимые операции. Это функция AUX_RGBImageRec* auxDIBImageLoad(string file) где file– название файла с расширением *.bmp или *.dib. В качестве результата функция возвращает указатель на область памяти, где хранятся преобразованные данные. При создании образа текстуры в памяти следует учитывать следующие требования. Во-первых, размеры текстуры как по горизонтали, так и по вертикали должны представлять собой степени двойки. Это требование накладывается для компактного размещения текстуры в памяти и способствует ее эффективному использованию. Использовать только текстуры с такими размерами конечно неудобно, поэтому перед загрузкой их надо преобразовать. Изменение размеров текстуры проводится с помощью команды void gluScaleImage(GLenum format, GLint widthin, GL heightin, GLenum typein, const void *datain, GLint widthout, GLint heightout, GLenum typeout, void *dataout) В качестве значения параметра format обычно используется значение GL_RGB или GL_RGBA, определяющее формат хранения информации. Параметры widthin, heightin, widhtout, heightout определяют размеры входного и выходного изображений, а с помощью typein и typeout задается тип элементов массивов, расположенных по адресам datain и dataout. Как и обычно, то может быть тип GL_UNSIGNED_BYTE, GL_SHORT, GL_INT и так далее. Результат своей работы функция заносит в область памяти, на которую указывает параметр dataout. Во-вторых, надо предусмотреть случай, когда объект по размерам значительно меньше наносимой на него текстуры. Чем меньше объект, тем меньше должна быть наносимая на него текстура и поэтому вводится понятие уровней детализации текстуры. Каждый уровень детализации задает некоторое изображение, которое является как правило уменьшенной в два раза копией оригинала. Такой подход позволяет улучшить качество нанесения текстуры на объект. Например, для изображения размером 2mx2n можно построить max(m,n)+1 уменьшенных изображений, соответствующих различным уровням детализации. Эти два этапа создания образа текстуры в памяти можно провести с помощью команды void gluBuild2DMipmaps(GLenum target, GLint components, GLint width, GLint height, GLenum format, GLenum type, const void *data) где параметр target должен быть равен GL_TEXTURE_2D, components определяет количество цветовых компонент текстуры, которые будут использоваться при ее наложении и может принимать значения от 1 до 4 (1-только красный,2-красный и alpha, 3-красный, синий, зеленый, 4-все компоненты). Параметры width, height, data определяют размеры и расположение текстуры соответственно, а format и type имеют аналогичный смысл, что и в команде gluScaleImage(). В OpenGL допускается использование одномерных текстур, то есть размера 1xN, однако это всегда надо указывать, используя в качестве значения target константу GL_TEXTURE_1D. Существует одномерный аналог рассматриваемой команды- gluBuild1DMipmaps(), который отличается от двумерного отсутствием параметра height. При использовании в сцене нескольких текстур, в OpenGL применяется подход, напоминающий создание списков изображений. Вначале, с помощью команды void glGenTextures(GLsizei n, GLuint*textures) надо создать n идентификаторов для используемых текстур, которые будут записаны в массив textures. Перед началом определения свойств очередной текстуры следует вызвать команду void glBindTexture(GLenum target, GLuint texture) где target может принимать значения GL_TEXTURE_1D или GL_TEXTURE_2D, а параметр texture должен быть равен идентификатору той текстуры, к которой будут относиться последующие команды. Для того, чтобы в процессе рисования сделать текущей текстуру с некоторым идентификатором, достаточно опять вызвать команду glBindTexture() c соответствующим значением target и texture. Таким образом, команда glBindTexture() включает режим создания текстуры с идентификатором texture, если такая текстура еще не создана, либо режим ее использования, то есть делает эту текстуру текущей. Методы наложения текстуры При наложении текстуры, как уже упоминалось, надо учитывать случай, когда размеры текстуры отличаются от размеров объекта, на который она накладывается. При этом возможно как растяжение, так и сжатие изображения, и то, как будут проводиться эти преобразования может серьезно повлиять на качество построенного изображения. Для определения положения точки на текстуре используется параметрическая система координат (s,t), причем значения s и t находятся в отрезке [0,1]. Для изменения различных параметров текстуры применяются команды: void glTexParameter[i f](GLenum target, GLenum pname, GLenum param) void glTexParameter[i f]v(GLenum target, GLenum pname, GLenum *params) При этом target имеет аналогичный смысл, что и раньше, pname определяет, какое свойство будем менять,а с помощью param или params устанавливается новое значение. Возможные значения pname: GL_TEXTURE_MIN_FILTER параметр param определяет функцию, которая будет использоваться для сжатия текстуры. При значении GL_NEAREST будет использоваться один (ближайший), а при значении GL_LINEAR четыре ближайших элемента текстуры. Значение по умолчанию: GL_LINEAR. GL_TEXTURE_MAG_FILTER параметр param определяет функцию, которая будет использоваться для увеличения (растяжения) текстуры. При значении GL_NEAREST будет использоваться один (ближайший), а при значении GL_LINEAR четыре ближайших элемента текстуры. Значение по умолчанию: GL_LINEAR. GL_TEXTURE_WRAP_S параметр param устанавливает значение координаты s, если оно не входит в отрезок [0,1]. При значении GL_REPEAT целая часть s отбрасывается, и в результате изображение размножается по поверхности. При значении GL_CLAMP используются краевые значения: 0 или 1, что удобно использовать, если на объект накладывается один образ. Значение по умолчанию: GL_REPEAT. GL_TEXTURE_WRAP_T аналогично предыдущему значению, только для координаты t. Использование режима GL_NEAREST значительно повышает скорость наложения текстуры, однако при этом снижается качество, так как в отличие от GL_LINEAR интерполяция не производится. Для того, чтобы определить, как текстура будет взаимодействовать с материалом, из которого сделан объект, используются команды void glTexEnv[i f](GLenum target, GLenum pname, GLtype param) void glTexEnv[i f]v(GLenum target, GLenum pname, GLtype *params) Параметр target должен быть равен GL_TEXTURE_ENV, а в качестве pname рассмотрим только одно значение GL_TEXTURE_ENV_MODE, которое наиболее часто применяется. Параметр если param может быть равен: GL_MODULATE конечный цвет находится как произведение цвета точки на поверхности и цвета соответствующей ей точки на текстуре. GL_REPLACE в качестве конечного цвета используется цвет точки на текстуре. GL_BLEND конечный цвет находится как сумма цвета точки на поверхности и цвета соответствующей ей точки на текстуре с учетом их яркости. Координаты текстуры Перед нанесением текстуры на объект осталось установить соответствие между точками на поверхности объекта и на самой текстуре. Задавать это соответствие можно двумя методами: отдельно для каждой вершины или сразу для всех вершин, задав параметры специальной функции отображения. Первый метод реализуется с помощью команд void glTexCoord[1 2 3 4][s i f d](type coord) void glTexCoord[1 2 3 4][s i f d]v(type *coord) Чаще всего используется команды вида glTexCoord2..(type s, type t), задающие текущие координаты текстуры. Вообще, понятие текущих координат текстуры аналогично понятиям текущего цвета и текущей нормали, и является атрибутом вершины. Однако даже для куба нахождение соответствующих координат текстуры является довольно трудоемким занятием, поэтому в библиотеке GLU помимо команд, проводящих построение таких примитивов, как сфера, цилиндр и диск, предусмотрено также наложение на них текстур. Для этого достаточно вызвать команду void gluQuadricTexture(GLUquadricObj*quadObject, GLboolean textureCoords) с параметром textureCoords равным GL_TRUE, и тогда текущая текстура будет автоматически накладываться на примитив. Второй метод реализуется с помощью команд void glTexGen[i f d](GLenum coord, GLenum pname, GLtype param) void glTexGen[i f d]v(GLenum coord, GLenum pname, const GLtype *params) Параметр coord определяет для какой координаты задается формула и может принимать значение GL_S, GL_T; pname определяет тип формулы и может быть равен GL_TEXTURE_GEN_MODE, GL_OBJECT_PLANE, GL_EYE_PLANE. С помощью params задаются необходимые параметры, а param может быть равен GL_OBJECT_LINEAR, GL_EYE_LINEAR, GL_SPHERE_MAP. Рассмотрение всех возможных комбинаций значений аргументов этой команды заняло бы слишком много места, поэтому в качестве примера рассмотрим, как можно задать зеркальную текстуру. При таком наложении текстуры изображение будет как бы отражаться от поверхности объекта, вызывая интересный оптический эффект. Для этого сначала надо создать два целочисленных массива коэффициентов s_coeffs и t_coeffs со значениями (1,0,0,1) и (0,1,0,1) соответственно, а затем вызвать команды: glEnable(GL_TEXTURE_GEN_S); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGendv(GL_S, GL_EYE_PLANE, s_coeffs); и такие же команды для координаты t с соответствующими изменениями. Приложение Приложение содержит информацию в основном прикладного характера, которая может понадобиться при изучении описанного выше материала. Стандартные геометрические примитивы Рассмотрим стандартные команды построения примитивов, которые реализованы в библиотеках GLU и GLUT. Как уже было сказано, чтобы построить примитив из библиотеки GLU, надо сначала создать указатель на quadric- объект с помощью команды gluNewQuadric(), а затем вызвать одну из команд gluSphere(), gluCylinder(), gluDisk(), gluPartialDisk(). Рассмотрим эти команды отдельно: void gluSphere(GLUquadricObj*qobj, GLdouble radius, GLint slices, GLint stacks) Строит сферу с центром в начале координат и радиусом radius. При этом число разбиений сферы вокруг оси z задается параметром slices, а вдоль оси z параметром stacks. void gluCylinder(GLUquadricObj*qobj, GLdouble baseRadius, GLdouble topRadius, GLdouble height, GLint slices, GLint stacks) Строит цилиндр без оснований (то есть кольцо), продольная ось параллельна оси z, заднее основание имеет радиус baseRadius , и расположено в плоскости z=0, переднее основание имеет радиус topRadius и расположено в плоскости z=height. Если задать один из радиусов равным нулю, то будет построен конус. Параметры slices и stacks имеют тот же смысл, что и в предыдущей команде. void gluDisk(GLUquadricObj*qobj, GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint loops) Строит плоский диск (то есть круг) с центром в начале координат и радиусом outerRadius. При этом если значение innerRadius ненулевое, то в центре диска будет находиться отверстие радиусом innerRadius. Параметр slices задает число разбиений диска вокруг оси z, а параметр loops –число концентрических колец, перпендикулярных оси z. void gluPartialDisk(GLUquadricObj *qobj, GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint loops, GLdouble startAngle, GLdouble sweepAngle); Отличие этой команды от предыдущей заключается в том, что она строит сектор круга, начальный и конечный углы которого отсчитываются против часовой стрелки от положительного направления оси y и задаются параметрами startAngle и sweepAngle. Углы измеряются в градусах. Команды, проводящие построение примитивов из библиотеки GLUT, реализованы через стандартные примитивы OpenGL и GLU. Для построения нужного примитива достаточно произвести вызов соответствующей команды. void glutSolidSphere(GLdouble radius, GLint slices, GLint stacks) void glutWireSphere(GLdouble radius, GLint slices, GLint stacks) Команда glutSolidSphere() строит сферу, а glutWireSphere() -каркас сферы радиусом radius. Остальные параметры имеют тот же смысл, что и в предыдущих командах. void glutSolidCube(GLdouble size) void glutWireCube(GLdouble size) Эти команды строят куб или каркас куба с центром в начале координат и длиной ребра size. void glutSolidCone(GLdouble base, GLdouble height, GLint slices, GLint stacks) void glutWireCone(GLdouble base, GLdouble height, GLint slices, GLint stacks) Эти команды строят конус или его каркас высотой height и радиусом основания base, расположенный вдоль оси z. Основание находится в плоскости z=0. Остальные параметры имеют тот же смысл, что и в предыдущих командах. void glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings) void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings) Эти команды строят тор или его каркас в плоскости z=0. Внутренний и внешний радиусы задаются параметрами innerRadius, outerRadius. Параметр nsides задает число сторон в кольцах, составляющих ортогональное сечение тора, а rings- число радиальных разбиений тора. void glutSolidTetrahedron(void) void glutWireTetrahedron (void) Эти команды строят тетраэдр (правильную треугольную пирамиду) или его каркас, при этом радиус описанной сферы вокруг него равен 1. void glutSolidOctahedron(void) void glutWireOctahedron(void) Эти команды строят октаэдр или его каркас, радиус описанной вокруг него сферы равен 1. void glutSolidDodecahedron(void) void glutWireDodecahedron(void) Эти команды строят додекаэдр или его каркас, радиус описанной вокруг него сферы равен квадратному корню из трех. void glutSolidIcosahedron(void) void glutWireIcosahedron(void) Эти команды строят икосаэдр или его каркас, радиус описанной вокруг него сферы равен 1. Создание приложения в среде MS Visual C++ 5.0 Перед началом работы необходимо скопировать файлы glut.h, glut32.lib glut32.dll в каталоги ..\MSVC\Include\Gl, ..\MSVC\Lib, ..\Windows\System соответственно. Также в этих каталогах надо проверить наличие файлов gl.h, glu.h, opengl32.lib, glu32.lib, opengl32.dll, glu32.dll, которые обычно входят в состав Visual C++ и Windows. При использовании команд из библиотеки GLAUX к перечисленным файлам надо добавить glaux.h, glaux.lib. Для создания приложения надо выполнить следующие действия: Создание проекта: для этого надо выбрать File->New->Projects->Win32 Console Application, набрать имя проекта, OK. В появившемся окне выбрать ‘ An empty project ’, Finish , OK . Текст программы можно либо разместить в созданном текстовом файле(выбрав File->New->Files->Text File ), либо добавив файл с расширением *.c или *.cpp в проект (выбрав Project->Add To Project->Files ). Подключить к проекту библиотеки OpenGL. Для этого надо выбрать Project->Settings->Link и в поле Object/library modules набрать названия нужных библиотек: opengl32.lib, glu32.lib, glut32.lib и, если надо, glaux.lib. Для компиляции выбрать Build->Build program.exe , для выполнения – Build->Execute program.exe . Чтобы при запуске не появлялось текстовое окно, надо выбрать Project->Settings->Link и в поле Project Options вместо ‘subsystem:console’ набрать ‘subsystem:windows’,и набрать там же строку ‘/entry:mainCRTStartup’ Когда программа готова, рекомендуется перекомпилировать ее в режиме ‘ Release ’ для оптимизации по быстродействию и объему. Для этого сначала надо выбрать Build->Set Active Configuration… и отметить -Win32 Release, а затем заново подключить необходимые библиотеки. Создание приложения в среде Borland C++ 5.02 Как и для Visual C++, сначала надо обеспечить наличие файлов glut.h, glut32.lib, glut32.dll в каталогах ..\BorlandC\Include\Gl, ..\BorlandC\Lib, ..\Windows\System соответственно. Также в этих каталогах надо проверить наличие файлов gl.h, glu.h, opengl32.lib, glu32.lib, opengl32.dll, glu32.dll, которые обычно входят в состав BorlandC++ и Windows. При этом надо учитывать, что версии Microsoft файлов opengl32.lib, glu32.lib, glut32.lib для Borland C++ не подходят и следует использовать только совместимые версии. Чтобы создать такие версии, надо использовать стандартную программу ‘implib, которая находится в каталоге ..\BorlandC\Bin. Для этого надо выполнить команды вида implib ..\BorlandC\Lib\filename.lib ..\filename.dll для перечисленных файлов, которые создают нужный *.lib файл из соответствующего *.dll файла. Кроме того, надо отметить, что компилятор BorlandC не может по неизвестным причинам использовать файл glaux.lib, входящий в состав BorlandC++5.02, при компиляции приложения, использующего библиотеку GLAUX, поэтому возможно от этой библиотеки придется отказаться. Для создания приложения надо выполнить следующие действия: Создание проекта: для этого надо выбрать Project->New Project и заполнить поля в окне Target Expert следующим образом: в поле Platform выбрать Win32,в поле Taget Model выбрать Сonsole, нажать Advanced и отменить выбор пунктов *.rc ‘и *.def. Подключить к проекту библиотеки OpenGL. Для этого надо выбрать в окне проекта название исполняемого файла проекта (*.exe) и нажав правую кнопку мыши выбрать в контекстном меню пункт Add node. Затем надо определить положение файлов opengl32.lib, glu32.lib, glut32.lib. Для компиляции выбрать Project->Build All, для выполнения Debug->Run. ОбычныйТерминСписокопределенийАдресЦитатыФорматированныйПример программы Результатом выполнения этой программы является построение тетраэдра с вращающимися вокруг него кольцами, на которые нанесена текстура. В среде MS Visual C++ программа может компилироваться без изменений, а при компиляции в Borland C++ придется закомментировать вызов и описание функции TextureInit(), после чего не будет проводиться наложение текстур. Как было сказано выше, попытка использовать функции из библиотеки GLAUX приводит к сообщению об ошибке при компиляции программы. При компиляции программы в MS Visual C++ файл ‘texture.bmp’ надо поместить в каталог проекта или указать полный путь к нему, используя символ ‘/’. Если путь не указан, то при запуске исполняемого файла из операционной системы, файл с текстурой должен находиться в том же каталоге. #include #include #include #define TETR_LIST 1 GLfloat light_col[] = {1,1,1}; float mat_diff1[]={0.8,0.8,0.8}; float mat_diff2[]={0.0,0.0,0.9}; float mat_amb[]= {0.2,0.2,0.2}; float mat_spec[]={0.6,0.6,0.6}; float shininess=0.7*128, CurAng=0, RingRad=1, RingHeight=0.1; GLUquadricObj* QuadrObj; GLuint TexId; GLfloat TetrVertex[4][3], TetrNormal[4][3]; //--Вычисление нормали к плоскости, задаваемой точками a,b,c----------// void getnorm (float a[3],float b[3],float c[3],float *n) { float mult=0; n[0]=(b[1]-a[1])*(c[2]-a[2])-(b[2]-a[2])*(c[1]-a[1]); n[1]=(c[0]-a[0])*(b[2]-a[2])-(b[0]-a[0])*(c[2]-a[2]); n[2]=(b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1]); //--Определение нужного направления нормали: от точки (0,0,0)---------// for (int i=0;i<3;i++) mult+=a[i]*n[i]; if (mult<0) for (int j=0;j<3;j++) n[j]=-n[j]; } //--Вычисление координат вершин тетраэдра-----------------------------// void InitVertexTetr() { float alpha=0; TetrVertex[0][0]=0;TetrVertex[0][1]=1.3;TetrVertex[0][2]=0; //--Вычисление координат основания тетраэдра--------------------------// for (int i=1;i<4;i++) { TetrVertex[i][0]=0.94*cos(alpha); TetrVertex[i][1]=0; TetrVertex[i][2]=0.94*sin(alpha); alpha+=120.0*3.14/180.0; } } //--Вычисление нормалей сторон тетраэдра------------------------------// void InitNormsTetr() { getnorm(TetrVertex[0],TetrVertex[1],TetrVertex[2],TetrNormal[0]); getnorm(TetrVertex[0],TetrVertex[2],TetrVertex[3],TetrNormal[1]); getnorm(TetrVertex[0],TetrVertex[3],TetrVertex[1],TetrNormal[2]); getnorm(TetrVertex[1],TetrVertex[2],TetrVertex[3],TetrNormal[3]); } //--Создание списка построения тетраэдра------------------------------// void MakeTetrList() { glNewList (TETR_LIST,GL_COMPILE); //--Задание сторон тетраэдра------------------------------------------// glBegin(GL_TRIANGLES); for (int i=1;i<4;i++) { glNormal3fv(TetrNormal[i-1]); glVertex3fv(TetrVertex[0]);glVertex3fv(TetrVertex[i]); if (i!=3) glVertex3fv(TetrVertex[i+1]);else glVertex3fv(TetrVertex[1]); } glNormal3fv(TetrNormal[3]); glVertex3fv(TetrVertex[1]); glVertex3fv(TetrVertex[2]); glVertex3fv(TetrVertex[3]); glEnd(); glEndList(); } void DrawRing() { //Построение цилиндра (кольца), расположенного параллельно оси z//--Второй и третий параметры задают радиусы оснований, четвертый-----// //высоту,последние два-число разбиений вокруг и вдоль оси z//--При этом дальнее основание цилиндра находится в плоскости z=0-----// gluCylinder (QuadrObj,RingRad,RingRad,RingHeight,30,2); } void TextureInit() { char strFile[]="texture.bmp"; //--Выравнивание в *.bmp по байту-------------------------------------// glPixelStorei(GL_UNPACK_ALIGNMENT,1); //--Создание идентификатора для текстуры- ----------------------------// glGenTextures(1,&TexId); //--Загрузка изображения в память-------------------------------------// AUX_RGBImageRec *pImage = auxDIBImageLoad(strFile); int BmpWidth= pImage->sizeX; int BmpHeight = pImage->sizeY; void* BmpBits = pImage->data; //--Начало описания свойств текстуры----------------------------------// glBindTexture (GL_TEXTURE_2D,TexId); //--Создание уровней детализации и инициализация текстуры ------------// gluBuild2DMipmaps(GL_TEXTURE_2D,3,BmpWidth, BmpHeight,GL_RGB,GL_UNSIGNED_BYTE,BmpBits); //--Разрешение наложения этой текстуры на quadric-объекты-------------// gluQuadricTexture(QuadrObj, GL_TRUE); //--Задание параметров текстуры---------------------------------------// //--Повтор изображения по параметрическим осям s и t------------------// glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); //--Не использовать интерполяцию при выборе точки на текстуре---------// glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameteri (GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); //--Совмещать текстуру и материал объекта-----------------------------// glTexEnvi (GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); } void Init(void) { InitVertexTetr(); InitNormsTetr(); MakeTetrList(); //--Определение свойств материала-------------------------------------// glMaterialfv (GL_FRONT_AND_BACK,GL_AMBIENT,mat_amb); glMaterialfv (GL_FRONT_AND_BACK,GL_SPECULAR,mat_spec); glMaterialf(GL_FRONT,GL_SHININESS,shininess); //--Определение свойств освещения-------------------------------------// glLightfv(GL_LIGHT0, GL_DIFFUSE, light_col); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); //--Проводить удаление невидимых линий и поверхностей-----------------// glEnable(GL_DEPTH_TEST); //--Проводить нормирование нормалей-----------------------------------// glEnable(GL_NORMALIZE); //--Материалы объектов отличаются только цветом диффузного отражения--// glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); //--Создания указателя на quadric-объект для построения колец---------// QuadrObj=gluNewQuadric(); //--Определение свойств текстуры--------------------------------------// TextureInit(); //--Задание перспективной проекции------------------------------------// glMatrixMode(GL_PROJECTION); gluPerspective(89.0,1.0,0.5,100.0); //--Далее будет проводиться только преобразование объектов сцены------// glMatrixMode(GL_MODELVIEW); } void DrawFigures(void) { //--Включение режима нанесения текстуры-------------------------------// glEnable(GL_TEXTURE_2D); //--Задаем цвет диффузного отражения для колец------------------------// glColor3fv(mat_diff1); //--Чтобы не проводить перемножение с предыдущей матрицей загружаем единичную матрицу// glLoadIdentity(); //--Определяем точку наблюдения---------------------------------------// gluLookAt(0.0, 0.0, 2.5,0.0, 0.0, 0.0, 0.0, 1.0, 0.0); //--Сохраняем видовую матрицу, так как дальше будет проводиться поворот колец// glPushMatrix(); //--Производим несколько поворотов на новый угол (это быстрее,--------// //--чем умножать предыдущую видовую матрицу на матрицу поворота с-----// //--фиксированным углом поворота)-------------------------------------// glRotatef (-CurAng,1,1,0); glRotatef (CurAng,1,0,0); //--Для рисования колец каждое из них надо преобразовать отдельно,----// //--поэтому сначала сохраняем видовую матрицу, затем восстанавливаем--// glPushMatrix(); glTranslatef (0,0,-RingHeight/2); DrawRing(); glPopMatrix(); glPushMatrix(); glTranslatef (0,RingHeight/2,0); glRotatef (90,1,0,0); DrawRing(); glPopMatrix(); glPushMatrix(); glTranslatef (-RingHeight/2,0,0); glRotatef (90,0,1,0); DrawRing(); glPopMatrix(); //--Восстанавливаем матрицу для поворотов тетраэдра--------------------// glPopMatrix(); //--Выключаем режим наложения текстуры--------------------------------// glDisable(GL_TEXTURE_2D); //--Проводим повороты-------------------------------------------------// glRotatef(CurAng,1,0,0); glRotatef(CurAng/2,1,0,1); //--Чтобы тетраэдр вращался вокруг центра, его надо сдвинуть вниз по оси oz// glTranslatef(0,-0.33,0); //--Задаем цвет диффузного отражения для тетраэдра--------------------// glColor3fv(mat_diff2); //--Проводим построение тетраэдра-------------------------------------// glCallList(TETR_LIST); } void Display(void) { //--Инициализация (очистка) текущего буфера кадра и глубины-----------// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //--Построение объектов-----------------------------------------------// DrawFigures(); //--Перестановка буферов кадра----------------------------------------// glutSwapBuffers(); } void Redraw(void) { //--Увеличение текущего угла поворота---------------------------------// CurAng+=1; //--Сигнал для вызова процедуры создания изображения (для обновления)-// glutPostRedisplay(); } int main(int argc, char **argv) { //--Инициализация функций библиотеки GLUT-----------------------------// glutInit(&argc, argv); //--Задание режима с двойной буферизацией, представление цвета в формате RGB,--// //--использование буфера глубины --// glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); //--Создание окна приложения-----------------------------------------// glutCreateWindow("Example of using OpenGL"); //--Регистрация функции построения изображения-----------------------// glutDisplayFunc(Display); //--Регистрация функции обновления изображения-----------------------// glutIdleFunc(Redraw); //--Инициализация функций OpenGL-------------------------------------// Init(); //--Цикл обработки событий-------------------------------------------// glutMainLoop(); return 0; } Результат работы программы: В программе используется только файл glut.h, который содержит обращения к файлам gl.h и glu.h, поэтому отдельно подключать их не нужно. Большим достоинством OpenGL является независимость большинства команд. Например, чтобы отключить наложение текстуры, достаточно закомментировать вызов функции TextureInit(), а чтобы получить статичное изображение достаточно не регистрировать функцию обновления изображения вызовом функции glutIdleFunc(). В этом случае можно использовать режим с одним буфером, заменив GL_DOUBLE на GL_SINGLE в команде glutInitDisplayMode() и добавив команду glFlush() в конце процедуры Display() для очистки этого буфера.
Источник: КЛИК» |