на начало
На главную страницу
Форум   

Статья :: Строим икосаэдр

 

Строим икосаэдр

Для иллюстрации работы с массивами вершин создадим более сложный объект — икосаэдр. Это такой дссятистенный дом с острой пятиугольной крышей и таким же полом, но углы пола смещены (повернуты) на л/5 относительно углов потолка.

Икосаэдр имеет 20 треугольных граней и 12 вершин (1 + 5 на потолке и 1 + 5 на полу). Благодаря своей правильности он может быть задан с помощью всего лишь двух чисел, которые лучше вычислить один раз и запомнить. Этими числами является косинус и синус угла в три пятых окружности, то есть

static double

//====== atan(l.) - это пи/4

angle = 3. * atan(1.)/2.5, //====== 2 характерные точки

V = cos(angle), W = sin(angle);

Этот код мы вставим внутрь функции рисования, чтобы не плодить глобальные переменные и не нарываться на конфликты имен. Вот новая версия функции DrawScene:

void DrawScene() { static double

//====== 2 характерные точки

angle = 3. * atan(l.)/2.5, V = cos(angle), W = sin(angle),

//=== 20 граней икосаэдра, заданные индексами вершин

static GLuint id[20][3] =

(0,1, 4), (8,1,10), (7,3,10), (6,10,1),

(0,4, 9), (8,10,3), (7,10,6), (9,11,0),

(9,4, 5), (5,8, 3), (7,6,11), (9,2,11),

(4,8, 5), (5,3, 2), (11,6,0), (9,5, 2),

(4,1,8), (2,3,7), (0,6,1), (7,11,2)

//====== Начинаем формировать список команд

glNewList (1,GL_COMPILE) ;

//====== Выбираем текущий цвет рисования

glColor3d (1., 0.4, 1 . ) ;

glBegin (GLJTRIANGLES) ;

for (int i = 0; i < 20; i++)

{

//====== Грубый подход к вычислению нормалей

glNorma!3dv(v[id[i] [0] ] ) ;

glVertex3dv(v[id[i] [0] ] ) ;

glNorma!3dv(v[id[i] [1] ] ) ;

glVertex3dv(v[id[i] [1] ] ) ;

glNorma!3dv(v[id[i] [2] ] ) ;

glVertex3dv(v[id[i] [2] ] ) ;

}

glEnd() ;

//====== Конец списка команд

glEndList ();

}

Точное вычисление нормалей

Проверьте результат и обсудите качество. В данном варианте нормали в вершинах заданы так, как будто изображаемой фигурой является сфера, а не икосаэдр. Это достаточно грубое приближение. Если поверхность произвольного вида составлена из треугольников, то вектор нормали к поверхности каждого из них можно вычислить точно, опираясь на данные о координатах вершин треугольника. Из $ курса векторной алгебры вы, вероятно, помните, что векторное произведение двух векторов а и b определяется как вектор п, перпендикулярный к плоскости, в которой лежат исходные векторы. Величина его равна площади параллелограмма, построенного на векторах а и b как на сторонах, а направление определяется так, что векторы a, b и п образуют правую тройку. Последнее означает, что если представить наблюдателя на конце вектора п, то он видит поворот вектора а к вектору b, совершаемый по кратчайшему пути против часовой стрелки. На рис. 6.4. изображена нормаль п (правая тройка) при различной ориентации перемножаемых векторов а и b.

Рис. 6.2. Ориентация вектора нормали

Если координаты векторов а и b известны, то координаты нормали вычисляю по следующим формулам. Длина вектора нормали п зависит от длин вектор сомножителей и величины угла между ними:

Nx=AxBz-AzBy

Ny=AzBx-AxBz

Nz=AxBy-AyBx

Примечание

Можно потерять много времени на осознание того факта, что не только правление нормали, но и ее модуль влияют на величину освещенности (и та) вершины, так как сопровождающая документация (Help) не содер; явных указаний на это. Отметьте также, что цвета вершин полигона влияю цвета точек заполнения полигона, так как цвета вновь генерируемых то интерполируются, то есть принимают промежуточные значения между з чениями цвета вершин.

Чтобы нивелировать зависимость цвета вершины от амплитуды нормали, обыч вектор нормали масштабируют (или нормируют), то есть делают его длину р; ной единице, оставляя неизменным направление. С учетом сказанного создал две вспомогательные функции. Первая масштабирует, а вторая вычисляет н< маль к плоскости треугольника. Алгоритм вычисления использует координа двух сторон, прилегающих к текущей вершине треугольника:

//====Нормирование вектора нормали (или любого другого)

void Scale(double v[3])

{

double d = sqrt(v[0]*v[0]+v[l]*v[l]+v[2]*v[2]);

if (d == 0.)

{

MessageBox(0,"Zero length vector","Error",MB_OK);

return;

}

void getNorm(double vl[3], double v2[3], double out[3])

{

//===== Вычисляем координаты вектора нормали

//====== по формулам векторного произведения

out[0] = vl[l]*v2[2] - vl[2]*v2[l];

out[l] = vl[2]*v2(0] - vl[0]*v2[2] ;

out[2] =vl[0]*v2[l] - vl[l]*v2[0];

Scale(out);

}

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

void DrawScene()

{

static double

angle - 3. * atanfl.)/2.5, V = cos(angle), W = sin(angle),

v[12] [3] = {

{-V,0.,W}, {V,0.,W}, {-V,0.,-W},

{V,0.,-W}, {0.,W,V}, {0.,W,-V},

{0.,-W,V}, {0. ,-W,-V}, {W,V, 0.},

{-W,V,0.}, {W,-V,0.}, {-W,-V,0.}

};

static GLuint id[20][3] = {

(0,1, 4), {0,4, 9}, (9,4, 5), (4,8, 5}, (4,1,8),

(8,1,10), (8,10,3), (5,8, 3), (5,3, 2), (2,3,7),

(7,3,10), (7,10,6), (7,6,11), (11,6,0), (0,6,1),

(6,10,1), (9,11,0), (9,2,11), (9,5, 2), (7,11,2) 1;

glNewList(l,GL_COMPILE); glColorSd (1., 0.4, 1.) ;

glBegin(GLJTRIANGLES);

for (int i = 0; i < 20; i++)

{

double dl[3], d2[3], norm[3];

for (int j = 0; j < 3; j++)

{

dl[j] =v[id[i][0]] [j] -v[id[i][l]J [j];

d2[j] =v[id[i][l]] [j] -v[id[i][2J] [j];

}

//====== Вычисление и масштабирование нормали

getNorm(dl, d2, norm);

glNormal3dv(norm);

glVertexSdv(v [ id[i] [1]]);

glVertex3dv(v[id[i] [1] ] glVertex3dv(v[id[i] [2] ]

glEnd() ;

}

glEndList () ;

}

Примечание

Функцию нормировки всех нормалей можно возложить на автомат OpenGL, если включить состояние GL_NORMALIZE, но обычно это ведет к замедлению перерисовки и, как следствие, выполнения приложения, если изображение достаточно сложное. В нашем случае оно просто, и поэтому вы можете проверить действие настройки, если вставите вызов glEnable (GL_NORMALIZE); в функцию Init (до вызова OrawScene) и временно выключите вызов Scale(out); производимый в функции getNorm. Затем вернитесь к исходному состоянию.

 

Строим икосаэдр

страницы в данном разделе 
Урок 6. Графика OpenGL Графика OpenGL
Обзор возможностей библиотеки OpenGL Подключаемые библиотеки
Ограничения Microsoft Примитивы OpenGL
OpenGL — автомат с конечным числом состояний Конвейер передачи OpenGL
Основные этапы Анимация
Другие функции OpenGL Контекст передачи изображения
Подготовка окна Создание консольного проекта
Штриховка линий Штриховка полигонов
Как убирать внутренние линии Перспективная проекция
Вносим свет Интерактивное управление положением и ориентацией
Двойная буферизация Использование списков
Интерполяция цвета Строим икосаэдр
Как создать сферу Выбор способа вычисления нормалей
Рекурсивное деление Массивы вершин, нормалей и цветов
Создание сферы >  


Содержание сайта (выборка)
Apache
Протоколы TCP/IP (принципы, протоколы и архитектура)



PHP, PELR, JSP
PHP
JavaServer Pages (JSP)

Базы данных
Основы mysql
СУБД INFORMIX
СУБД POSTGRES
Основы проектирования реляционных баз данных

HTML, javascript
Спецификация HTML 4.01
Каскадные Таблицы Стилей, Уровень 2
Клиентский JavaScript. Справочник.
JavaScript руководство пользователя
Серверный JavaScript 1.4. Руководство по Использованию.

Паскаль, C, C++, C#
GCC (примеры)
FAQ Валентинa Озеровa DELPHI
C



 
© faq.pp.ru, справочник программиста