Строим
икосаэдр
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 (); }
Точное
вычисление нормалей
Рис. 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. Затем вернитесь к исходному состоянию.
Назад | Содержание | Вперед |