Модель трехмерной сцены и библиотека OpenGL

Министерствообразования и науки Российской Федерации
ФГАОУ ВПО«Уральский федеральный университет
им. первогоПрезидента России Б.Н.Ельцина»
Теплоэнергетический факультет
КАФЕДРА ПРИКЛАДНОЙ МАТЕМАТИКИ
Курсоваяработа
подисциплине «Компьютерная графика»
Модельтрехмерной сцены и библиотека OpenGL
Студент: Котовский В.В.
Екатеринбург,
2010
Формулировка задачи
Средствами графическойбиблиотеки OpenGL построить динамическую трехмернуюсцену, включающую заданные тело и поверхность вида z=f(x,y). Заданные графические объекты должны быть представлены вследующих видах:
· в виде каркасноймодели, позволяющей видеть контуры примитивов, из которых составлены объекты;
· в видереалистических изображений, построенных с учетом параметров источника освещенияи параметров отражающих свойств материала;
· в виде объектов сналоженной на них текстурой.
Заданноетело: вентилятор.
Заданнаяповерхность: />,
гдеа, b – параметры.
Описаниепредставления тела
Каркасные модели иповерхности могут быть представлены с помощью примитивов OpenGL, таких как:
× GL_LINES
× GL_LINE_STRIP
× GL_LINE_LOOP
× GL_TRIANGLES
× GL_TRIANGLE_STRIP
× GL_TRIANGLE_FAN
× GL_QUADS
× GL_QUAD_STRIP
× GL_POLYGON
× 
Примитивы LINE могут быть использованы только для создание,например, сетки, поскольку нормали к ним не пропишешь и освещение на них небудет правильно отображаться.
Примитивы TRIANGLE и QUAD применимы для создания, пожалуй, всех поверхностей ител – куб, пирамида, параллелепипед, сфера, цилиндр и т.д. С использованием TRIANGLE поверхности и тела получаются верносглаженными при меньшем разбиении, нежели с QUAD.
Примитив POLYGON применим для получения круга.
В данной работеиспользуются примитивы: GL_QUADS, GL_LINES, GL_POLYGON для построение каркасной модели тела, а дляпостроения поверхности используется GL_QUADS.

Составные части моделивентилятора
Сетка
/>
Сетка вентиляторасоставлена из трех основных частей
—  Круговаясоставляющая сетки
/>
 /> /> где bFan+17 – радиус окружности, rWeb – разбиение окружности
Фрагменткода программы круговой составляющей сетки
q=0;
while(q
{
glBegin(GL_LINES);
glVertex3f(0,(bFan+17)*sin(q*2*M_PI/rWeb),(bFan+17)*cos(q*2*M_PI/rWeb));
glVertex3f(0,(bFan+17)*sin((q+1)*2*M_PI/rWeb),(bFan+17)*cos((q+1)*2*M_PI/
rWeb));
glEnd();
q++;
}
—  Дуговаясоставляющая сетки
/>
 /> />  где bFan+2 – радиус полуокружности, rWeb – разбиение окружности
Фрагменткода программы дуговой составляющей сетки
int iWeb=0;
while(iWeb
{
glBegin(GL_LINES);
glVertex3f((bFan+2)*cos(iWeb*M_PI/rWeb),(bFan+2)*sin(iWeb*M_PI/rWeb)+15,0);
glVertex3f((bFan+2)*cos((iWeb+1)*M_PI/rWeb),(bFan+2)*sin((iWeb+1)*M_PI/rWeb)+15,0);
 glEnd();
 iWeb++;
}

—  Косой прут сетки

/>
 />  где a, b – параметры отрезка
Фрагменткода программы косого прута сетки
glBegin(GL_LINES);
glVertex3f(bFan+2,15,0);
glVertex3f(bFan,rFan,0);
glEnd();
1. Винт
/>
Винт вентиляторасоставлен из трех основных частей

—  Лопастьвентилятора
/>
 /> />  где bFan – половина ширины лопасти в основании, lFan – длина лопасти, N – разбиение лопасти
Фрагменткода программы лопасти вентилятора
double bFan=5;
double lFan=15;
glBindTexture(GL_TEXTURE_2D,texture4);
float N=30;
float NNN=100;
int i=0;
while(i
{
glBegin(GL_QUADS);
glTexCoord2d(i/N,0);
glNormal3f(-2*lFan*bFan*i/(N*N)*sin(i*M_PI/(1.5*N)),2*bFan*bFan/(N*log(NNN))*(log(i+26)*i*sin(i*M_PI/(1.5*N))-log(i+25)*(i+1)*sin((i+1)*M_PI/(1.5*N))),2*bFan*lFan*log(i+25)/(N*log(NNN)));
glVertex3f(-bFan*log(i+25)/log(NNN),lFan*i/N,-bFan*(i)/N*sin(i*M_PI/(N*1.5)));
glTexCoord2d(i/N,1);
glNormal3f(-2*lFan*bFan*i/(N*N)*sin(i*M_PI/(1.5*N)),2*bFan*bFan/(N*log(NNN))*(log(i+26)*i*sin(i*M_PI/(1.5*N))-log(i+25)*(i+1)*sin((i+1)*M_PI/(1.5*N))),2*bFan*lFan*log(i+25)/(N*log(NNN)));
glVertex3f(bFan*log(i+25)/log(NNN),lFan*i/N,bFan*(i)/N*sin(i*M_PI/(N*1.5)));
glTexCoord2d((i+1.0)/N,1);
glNormal3f(-2*lFan*bFan*i/(N*N)*sin(i*M_PI/(1.5*N)),2*bFan*bFan/(N*log(NNN))*(log(i+26)*i*sin(i*M_PI/(1.5*N))-log(i+25)*(i+1)*sin((i+1)*M_PI/(1.5*N))),2*bFan*lFan*log(i+25)/(N*log(NNN)));
glVertex3f(bFan*log((i+26))/log(NNN),lFan*(i+1)/N,bFan*(i+1)/N*sin((i+1)*M_PI/(N*1.5)));
glTexCoord2d((i+1.0)/N,0);
glNormal3f(-2*lFan*bFan*i/(N*N)*sin(i*M_PI/(1.5*N)),2*bFan*bFan/(N*log(NNN))*(log(i+26)*i*sin(i*M_PI/(1.5*N))-log(i+25)*(i+1)*sin((i+1)*M_PI/(1.5*N))),2*bFan*lFan*log(i+25)/(N*log(NNN)));
glVertex3f(-bFan*log((i+26))/log(NNN),lFan*(i+1)/N,/>*(i+1)/N*sin((i+1)*M_PI/(N*1.5)));
glEnd();
i++;
}
—  Цилиндр,объединяющий лопасти вентилятора в винт
/>
 /> />  где M – разбиение цилиндра

Фрагменткода программы цилиндра, объединяющего лопасти вентилятора в винт
M=30;
int qRoll=0;
glBindTexture(GL_TEXTURE_2D,texture2);
while (qRoll
{
glBegin(GL_QUADS);
glTexCoord2d(0,qRoll/M);
glNormal3f(0,sin((2*M_PI*qRoll)/M),cos((2*M_PI*qRoll)/M));
glVertex3f(-1,sin((2*M_PI*qRoll)/M),cos((2*M_PI*qRoll)/M));
glTexCoord2d(0,(qRoll+1.0)/M);
glNormal3f(0,sin((2*M_PI*(qRoll+1))/M),cos((2*M_PI*(qRoll+1))/M));
glVertex3f(-1,sin((2*M_PI*(qRoll+1))/M),cos((2*M_PI*(qRoll+1))/M));
glTexCoord2d(1,(qRoll+1.0)/M);
glNormal3f(0,sin((2*M_PI*(qRoll+1))/M),cos((2*M_PI*(qRoll+1))/M));
glVertex3f(1,sin((2*M_PI*(qRoll+1))/M),cos((2*M_PI*(qRoll+1))/M));
glTexCoord2d(1,qRoll/M);
glNormal3f(0,sin((2*M_PI*qRoll)/M),cos((2*M_PI*qRoll)/M));
glVertex3f(1,sin((2*M_PI*qRoll)/M),cos((2*M_PI*qRoll)/M));
glEnd();
qRoll++;
}

—  Крышка на цилиндр
/>
 /> />  где M – разбиение круга
Фрагменткода программы цилиндра, объединяющего лопасти вентилятора в винт
int M=30;
int qFan=0;
glBegin(GL_POLYGON);
while (qFan
{
glNormal3f(1,0,0);
if(qFan
{
glTexCoord2d(0,1-qFan*4/M);
}
if((qFan>=M/4)&&(qFan
{
glTexCoord2d((qFan-M/4)*4/M,0);
}
if((qFan>=M/2)&&(qFan
{
glTexCoord2d(1,(qFan-M/2)*4/M);
}
if(qFan>=3*M/4)
 {
glTexCoord2d(1-(qFan-3*M/4)*4/M,1);
}
glVertex3f(1,sin((2*M_PI*qFan)/M),cos((2*M_PI*qFan)/M));
qFan++;
}
glEnd();
2. Стойка
/>
Стойка вентиляторасоставлена из трех основных частей
—  Верхняя частькорпуса (полусфера)
/>
 /> />  где rFan – наибольший радиус в фигуре, iM, M – разбиения полусферы

Фрагменткода программы верхней части корпуса (полусферы)
M=30;
int iM=30;
double phi, psi;
q=0;
int i=0;
while (q
{
while (i
{
glBindTexture(GL_TEXTURE_2D, texture2);
glBegin(GL_QUADS);
phi=(-(M_PI*(i))/iM); psi=((M_PI*(q))/M);
glNormal3f(-3*rFan*sin(phi),rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
glTexCoord2d((sin(phi)+1)/2,(cos(psi)+1)/2);
glVertex3d(/>*rFan*sin(phi)+bFan,rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
phi=(-(M_PI*(i+1))/iM);psi=((M_PI*(q))/M);
 
glNormal3f(-3*rFan*sin(phi),rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
 glTexCoord2d((sin(phi)+1)/2,(cos(psi)+1)/2);
glVertex3d(/>*rFan*sin(phi)+bFan,rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
 phi=(-(M_PI*(i+1))/iM);psi=((M_PI*(q+1))/M);
 
glNormal3f(-3*rFan*sin(phi),rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
 glTexCoord2d((sin(phi)+1)/2,(cos(psi)+1)/2);
glVertex3d(/>*rFan*sin(phi)+bFan,rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
 phi=(-(M_PI*(i))/iM);psi=((M_PI*(q+1))/M);
 
glNormal3f(-3*rFan*sin(phi),rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
 glTexCoord2d((sin(phi)+1)/2,(cos(psi)+1)/2);
glVertex3d(/>*rFan*sin(phi)+bFan,rFan*cos(phi)*sin(psi),rFan*cos(phi)*cos(psi));
 glEnd();
 i++;
}
i=0;
q++;
}

—  Цилиндрическийэлемент стойки
/>
Этот элемент стойки вентилятора состоит из цилиндров и кругов (крышки для цилиндров). Все они вызываются с помощью функции OpenGL – glCallList. Большая часть кода элементов вентилятора считывается программой лишь однажды, в СallLists, а вызов уже происходит неоднократно, по мере необходимости, в функции RenderGLScene(). Этот способ наиболее эффективен как для скорости работы программы, так и для редактирования готового кода программы, благодаря чему одинаковые примитивы (цилиндр, круг, квадрат, линии и т.д.) было легко использовать вызовом CallList и, применяя элементарные преобразования – поворот, перемещение, масштабирование объектов, видоизменять необходимым образом для получения данных элементов тела.
Элементы цилиндр и круг были описаны выше, поэтому не будем повторяться.
—  Ножка стойки
/> Этот элемент тела строится через функцию CallList, в которой задан квадрат, вызываемый 4 раза и масштабированный по разным координатам по разному.
Фрагменткода программы ножек стойки вентилятора
//— Квадрат
square=basis_leg+1;
glNewList(square,GL_COMPILE);
glBindTexture(GL_TEXTURE_2D,texture1);
glBegin(GL_QUADS);
 glNormal3f(1,0,0);
 glTexCoord2d(0,0);
 glVertex3f(1,-1,-1);
 glTexCoord2d(1,0);
 glVertex3f(1,1,-1);
 glTexCoord2d(1,1);
 glVertex3f(1,1,1);
 glTexCoord2d(0,1);
 glVertex3f(1,-1,1);
 glEnd();
glEndList();
//—Ножка вентилятора
leg=square+1;
glNewList(leg,GL_COMPILE);
glCallList(square);
glRotatef(90,0,0,1);
 glCallList(square);
 glRotatef(90,0,0,1);
 glCallList(square);
 glRotatef(90,0,0,1);
 glCallList(square);
glEndList();

3. Пульт управления
/>
Пульт управлениявентилятором составлен из четырех основных частей
—  Основание пульта
/>
 /> /> 
Элемент строится из цилиндра с разбиением уменьшенным до 4х.
Фрагменткода программы основания пульта
M=4;
float qLeg=0;
glBindTexture(GL_TEXTURE_2D,texture5);
while (qLeg
{
glBegin(GL_QUADS);
 glNormal3f(0,sin(M_PI/4+qLeg*M_PI/2),cos(M_PI/4+qLeg*M_PI/2));
 glTexCoord2d(0,qLeg/M);
 glVertex3f(-1,sin((2*M_PI*qLeg)/M),cos((2*M_PI*qLeg)/M));
 glTexCoord2d(0,(qLeg+1.0)/M);
 glVertex3f(-1,sin((2*M_PI*(qLeg+1))/M),cos((2*M_PI*(qLeg+1))/M));
glTexCoord2d(1,(qLeg+1.0)/M);
 glVertex3f(1,sin((2*M_PI*(qLeg+1))/M),cos((2*M_PI*(qLeg+1))/M));
glTexCoord2d(1,qLeg/M);
 glVertex3f(1,sin((2*M_PI*qLeg)/M),cos((2*M_PI*qLeg)/M));
 glEnd();
 qLeg=qLeg+1.0;
}
—  Крышки для пульта
/>  Элемент состоит из квадрата, фрагмент кода которого содержался еще в описании ножки стойки вентилятора.
—  Кнопки на пульте
/>  Элемент состоит из цилиндра и круга, примитивов описанных ранее. Описание освещенияфигуры
Освещение тела происходитв OpenGL благодаря включению функции SetupLighting() с необходимыми параметрами иусловиями, а также за счет правильной расстановки нормалей к примитивам, изкоторого состоит тело. Чтобы задаваемые нормали нормировались автоматическинеобходимо включить функцию – glEnable(GL_NORMALIZE);
Подробней остановимся нанахождении нормалей к отдельным элементам тела.
Всего нормали былинайдены и прописаны в код программы для 6 примитивов, элементов тела.
1. Лопасть
Для определения нормалейлопасти, поскольку она представляет собой некую поверхность, была использованааналитическая формула для нахождения уравнения поверхности по трём точкам иформула для нахождения нормали к поверхности, что находится через частныепроизводные уравнения поверхности.
/>
/> 
— уравнение поверхности, D не считаем, поскольку оно не влияетна выбор нормали.
Тогда координаты длянормали функции glNormal3f(a1,a2,a3), будет высчитываться по следующимформулам:
/>/>/>
Исходя из записиуравнения поверхности и формулам, выписанные для коэффициентов в этой формулеполучим:

/>/>/>
Проведя расчет по даннымформулам, получим что:
/>
/>
/>
Для одинаково верногоотображения освещения лопасти вентилятора как с одной, так и с другой стороныпришлось прибегнуть к подключению двустороннего освещения с помощью функцииglLightModelf(GL_LIGHT_MODEL_TWO_SIDE, k), где к =1 для включения и к =0 для её вылючения.
2. Цилиндр
Чтобы определить нормальдля цилиндра нужно координату, что изменяется линейно оставить нулевой, а дведругие координаты будут совпадать с соответствующими координатами цилиндраввиду того, что в основании цилиндра лежит окружность.
В итоге получимкоординаты нормали:
(0,sin((2*M_PI*qRoll)/M),cos((2*M_PI*qRoll)/M));
3. Круг
Нормаль для кругаопределяется как перпендикуляр к этой поверхности.
4. Квадрат
Нормаль для квадратаопределяется аналогичным образом как и для круга.
5. Параллелепипед
У параллелепипеда нормальопределяется перпендикуляром к каждой грани и значит для всей грани нормальбудет одна и направлена наружу.
6. Полусфера
Нормаль для полусферыопределяется координатами самой фигуры, поэтому просто переписаны координаты изglVertex3f в glNormal3f.Графическоепредставление тела с освещением
/>Описание наложениятекстуры на тело
Для наложения текстур натела, поверхности применяется функция SetupTextures(); В своей работе я использовал 4 вида текстуры, различныхразмеров. Текстура накладывается на цилиндр, параллелепипед, лопасть, квадрат.Способ наложения примитивно прост. По порядку разберёмся с каждой из фигур.
—  Цилиндр.
Для того, чтобы наложитьтекстуру на фигуру необходимо было абстрактно раскрутить цилиндр в ровнуюповерхность, прямоугольник и сопоставить координаты полученного прямоугольникас координатами текстуры, как показанно на рисунках./> /> /> /> /> /> /> />
Текстура   />

/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>
—  Параллелепипед.
По аналогии с цилиндромабстрактно раскучиваем параллелепипед и наложим текстуру.
—  Лопасть.
На лопасть текстуранакладывается также исходя из особенности посторения. Поскольку лопастьполучается путем видоизменения прямоугольника, т.е. строится из прямоугольникомнакладывающихся сторонами друг на друга и поворачивающихся по мере наложения нанекий угол, то на лопасть текстура накладывается по аналогии с прямоугольником,как это было показано на цилиндре.
—  Квадрат.
Текстура на квадратнакладывается один к одному с существующими координатами.
Графическоепредставление тела с текстурой
/>Описаниепредставления поверхности
Поверхность /> строится переборомкоординат x и y в пределах от -N до N и вычислением для каждой пары (x,y)значения z.
/>/>
Нормаль к поверхности вточке /> находятся через честныепроизводные функции /> по x, y, z
/>
/>

Примечание: Поскольку приобходе циклов по i и j они оба обращаются в ноль, то длятого, чтобы избежать выход из области действительных значений координат ввыражения для вектора нормали по х и по у вписаны незначительные для конечногорезультата добавки.
Текстура на поверхностьнакладывается целиком и растягивается по размерам поверхности. Наложениепроисходит соотношением координат поверхности и координат текстуры так, чтокаждому QUAD, из которого строится поверхность,соотносится часть растрового изображения, разбивая его, свои образом, на сетку.Координаты текстуры для точки />:
/> Графическоепредставление поверхности
/>

/>
Список используемойлитературы
1. А.В. Боресков.Графика Трехмерной Компьютерной Игры на Основе OpenGL. М.: «Диалог-МИФИ»,2004
2. Ю.М. Боянковский,А.В. Игнатенко, А.И. Фролов. Графическая библиотека OpenGL. уч.-мет.пособие. Москва,2003
3. NeHeTutorials