8. Трехмерное моделирование полигональными сетками
8.1. Полигональная сетка
Полигональная сетка – набор полигонов или «граней», которые в совокупности образуют форму объекта. Полигональная сетка является (практически во всех графических системах) стандартным способом представления широкого класса объемных форм.
П олигональная сетка задается списком полигонов и информацией о направлении, куда обращен каждый полигон. Информация о направлении задается в виде нормали к плоскости грани. Нормаль указывает внешнее направление от объекта. Используется, в частности, для моделирования освещенности объекта.
Если грань плоская, то принципиально достаточно одного вектора нормали для задания внешней стороны грани. Однако оказывается алгоритмически выгоднее связывать нормальный вектор с каждой вершиной грани, чем задавать одну нормаль для каждой грани (рис. 8.1).
glBegin(GL_POLYGON);
glNormal3f (x, y, z); // n1
glVertex3f (x, y, z); // v1
glNormal3f (n1);
glVertex3f (v2);
glNormal3f (n1);
glVertex3f (v3);
glNormal3f (n1);
glVertex3f (v4);
glEnd();
Функция glNormal3f(x, y, z) устанавливает вектор нормали текущим. В режиме glBegin(GL_POLYGON) текущий вектор нормали посылается в конвейер вместе с каждой вершиной полигона. Поэтому предыдущую конструкцию можно записать так:
glBegin(GL_POLYGON);
glNormal3f (x, y, z); // n1
glVertex3f (v1);
glVertex3f (v2);
glVertex3f (v3);
glVertex3f (v4);
glEnd();
Таким образом, полигональная сетка – совокупность полигонов вместе с нормальными векторами, связанными с каждой вершиной этих полигонов. При этом вектора нормалей всегда должны показывать внешнее направление от граней.
OpenGL не содержит средств моделирования геометрических объектов полигональной сеткой. Это – забота программиста. OpenGL также не содержит средств расчета нормалей. Это тоже забота программиста.
Вектора нормалей должны быть единичными (нормированными). Функция glEnable() с параметром GL_NORMALIZE включает нормирование векторов нормалей, функция glDisable() с тем же параметром – отключает.
Вектора нормалей подвергаются преобразованию матрицей VM.
8.2. Нахождение нормальных векторов
Если грань плоская, достаточно найти нормальный вектор к самой грани и связать его с каждой вершиной. Один из прямых способов показан на рис. 8.2:
После этого вектор n может быть нормирован. Здесь две проблемы:
1) если векторы (v1–v2) и (v3–v2) почти параллельны, то |n| будет малым и возможна погрешность вычислений;
2) полигон может оказаться неплоским и тогда за нормаль необходимо принимать усредненные значения.
Обе эти проблемы решает метод, разработанный Мартином Ньюэллом. Этот метод рассчитывает компоненты nx, ny и nz по следующим формулам:
N
Рис. 83. Нормальный
вектор показывает направление наружу
от грани при обходе вершин против
часовой стрелки
Грани тетраэдра являются его полигонами. Описание тетраэдра как списка полигонов приведено в листинге 8.1.
Листинг 8.1. Описание тетраэдра в OpenGL
//грань 0
glBegin(GL_POLYGON);
glNormal3f(0.577, 0.577, 0.577);
glVertex3f(1, 0, 0);
glVertex3f(0, 1, 0);
glVertex3f(0, 0, 1);
glEnd();
//грань 1
glBegin(GL_POLYGON);
glNormal3f(0, 0, -1);
glVertex3f(0, 0, 0);
glVertex3f(0, 1, 0);
glVertex3f(1, 0, 0);
glEnd();
//грань 2
glBegin(GL_POLYGON);
glNormal3f(-1, 0, 0);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, 1);
glVertex3f(0, 1, 0);
glEnd();
//грань 3
glBegin(GL_POLYGON);
glNormal3f(0, -1, 0);
glVertex3f(1, 0, 0);
glVertex3f(0, 0, 1);
glVertex3f(0, 0, 0);
glEnd();