Шаг 15 - Созданние сложных тел, узел IndexedFaceSet

Прежде чем разбираться с узлом IndexedFaceSet нам надо разобраться с узлом Coordinate. Определяется он следующим образом:

Coordinate { 
  exposedField MFVec3f point []
}

Этот узел предельно прост в определении. Его единственный параметр-массив point[] определяет набор точек в трехмерном пространстве. Пример:

Coordinate {
	point [
		0 0 0	#1 точка
		1 0 0	#2
		0 1 0	#3
		0 0 1	#4
		1 1 1	#5
	]
}

Данный пример создаст в трехмерном пространстве такой набор точек:

15_1.gif (917 b)

Теперь приступим к самому интересному. В прошлом шаге мы познакомились с сеткой точек, координаты которых жестко закреплены параметрами ячеек сетки. Узел IndexedFaceSet позволяет построить поверхность (а вернее практически трехмерное тело) по заданному набору точек. Узел описывается следующим образом:

IndexedFaceSet { 
  eventIn       MFInt32 set_colorIndex
  eventIn       MFInt32 set_coordIndex
  eventIn       MFInt32 set_normalIndex
  eventIn       MFInt32 set_texCoordIndex
  exposedField  SFNode  color             NULL
  exposedField  SFNode  coord             NULL
  exposedField  SFNode  normal            NULL
  exposedField  SFNode  texCoord          NULL
  field         SFBool  ccw               TRUE
  field         MFInt32 colorIndex        []
  field         SFBool  colorPerVertex    TRUE
  field         SFBool  convex            TRUE
  field         MFInt32 coordIndex        []
  field         SFFloat creaseAngle       0
  field         MFInt32 normalIndex       []
  field         SFBool  normalPerVertex   TRUE
  field         SFBool  solid             TRUE
  field         MFInt32 texCoordIndex     []
}

Как Вы видите этот узел содержит много параметров, но для задания простейшего тела нужны не все. Мы с Вами познакомимся только с некоторыми из них, а остальные рассмотрим позже.

Чтобы все было более понятным давайте создадим реальное трехмерное тело. Но для простоты не будем увлекаться трехмерными "лицами", а создадим достаточно простое всем знакомое платоново тело - икосаэдр (он имеет 20 граней, 30 ребер, 12 вершин). Нам надо с вами задать координаты для 12 точек. Строиться икосаэдр не сложно. Сначала берется окружность и делится на 5 равных частей, при этом получаются точки разбиения, которые будут точками икосаэдра. Вот так выглядит эта поделенная окружность:

15_2.gif (1594 b)

Для того. чтобы получить "объем" берется вторая окружность поделенная таким же образом, но только повернутая в противоположную сторону. Добавляются две вершины: сверху и снизу. Все эти точки соединяются ребрами. Получается следующая фигура:

15_3.gif (1721 b)

Плоскости, в которых лежат окружности расположены в плоскостях Y=-0.5 и Y=+0.5, при этом центр икосаэдра совпадает с центром координат. На рисунках специально указаны все координаты и пронумерованы вершины, для того, чтобы можно было не запутаться при создании массива точек.

Прежде чем создавать грани экосаэдра надо понять процесс задания этих граней. Грань (плоскость) в VRML задается перечислением как минимум трех точек лежащих на этой плоскости. Всем известно, что через три точки в пространстве можно провести только одну плоскость. Вы можете строить плоскости хоть из 10 вершин, но дело в том, что точность задания координат каждой вершины может привести к тому, что некоторые точки не будут лежать на задаваемой плоскости и иметь мельчайшие отклонения. Поэтому, чтобы не ломать голову расчетами уточненных координат плоскости лучше всегда задавать только тремя точками (вершинами). Те для кого линейная алгебра знакома, сразу же могут задать вопрос о нормали плоскости, а именно "куда направлена нормаль к плоскости" ? Правильный вопрос, ведь именно по нормали программа высчитывает освещенность плоскости, ее видимость для обозревателя и многие другие параметры. Чтобы указать направление нормали в VRML применяется порядок задания точек. Три точки определяющие плоскость задаются по правилу "буравчика". Давайте сначала посмотрим на рисунок:

15_4.gif (1096 b)

На рисунке изображены две плоскости и нормали к ним, направленные таким образом, чтобы эти плоскости были видимыми, т.е. нормальные вертора должны быть направлены в сторону наблюдателя. Еще вы видите направления вращения руки при вкручивании "буравчика". Теперь задание левой плоскости будет таким: 5 3 2 1, т.е. точки перечислены в порядке "вращения" вокруг нормали. Конечно же перечисление точек может начаться с любой точки, т.е. записи 3 2 1 5 и 1 5 3 2 задают одну и ту же плоскость, самое главное какая точка за какой идет по ходу вращения. Правая плоскость задается, как 3 5 4, и как видите для нее используется рекомендованное количество точек - три. В связи с тем, что количество точек может варьироваться от плоскости к плоскости разработчики VRML придумали признак-разделитель "-1", который следует после определения каждой плоскости. Например, определение двух этих плоскостей выглядело бы так: 3 2 1 5 -1 3 5 4.

Ну, а теперь давайте созадим массив точек икосаэдра и определим какие грани из каких точек состоят.

#VRML V2.0 utf8
WorldInfo { 
  info  ["IndexedFaceSet example"]
  title "Step 15 Example"
}

Background{
	skyColor 0 0 0
}

Group {
	children[
		IndexedFaceSet {
# определяем набор из 12 вершин с помощь узла Coordinate
			coord Coordinate {
				point [
					0 1 0 #0
					.951 .5 -.309 #1
					.587 .5 .809 #2
					-.587 .5 .809 #3
					-.951 .5 -.309 #4
					0 .5 -1 #5
					.951 -.5 .309 #6
					0 -.5 1 #7
					-.951 -.5 .309 #8
					-.587 -.5 -.809 #9
					.587 -.5 -.809 #10
					0 -1 0 #11
				]
			}
#Определяем 20 граней икосаэдра
			coordIndex [
				0 2 1 -1  0 3 2 -1  0 4 3 -1  0 5 4 -1   0 1 5 -1
				1 2 6 -1  2 7 6 -1  2 3 7 -1  3 8 7 -1   3 4 8 -1
				4 9 8 -1  4 5 9 -1  5 10 9 -1 5 1 10 -1  1 6 10 -1
				7 11 6 -1 7 8 11 -1 9 11 8 -1 9 10 11 -1 10 6 11 -1
				]
		}
	]
}

Как Вы можете увидеть из примера, вершины перечисляются в параметре coord при помощи узла Coordinate. Грани же задаются в массиве coordIndex, где каждая грань отделяется от других -1. Получается такое вот тело:

15_5.gif (1771 b)

Я специально обвел ребра черными линиями, чтобы они были четко видны.

Некоторые поля узла IndexedFaceSet вам известны из предыдущих шагов. К таким полям можно отнести creaseAngle, solid.

С помощью creaseAngle Вы можете сделать тело сглаженным. Но если мы будем сглаживать икосаэдр, то просто получим из него нечто очень сильно похожее на обычную сферу, кстати можете посмотреть на нее в шаге 13 и увидите, что сфера в действительности просто сильно сглаженное платоново тело с большим количеством граней.

После возни с нормалями тела можно также понять смысл поля solid. В переводе это "целое" (твердое, прочное и т.д.), т.е. когда этот признак равен TRUE, то тело считается целиковым и значит в нем нет пустоты. По этому нормали всех граней тела должны быть направлены из его центра наружу. При этом считается, что грани имеют правильно направленные нормали, и если вдруг наблюдатель окажется внутри тела, то ни одной грани он не увидит, так как векторы нормали направлены от него. Если же вдруг поле solid будет равно FALSE и у тела не будет определена какая-то одна грань, то через эту "дырку" можно будет видеть все то, что творится с другой стороны, так как в таком случае грани будут считаться двух сторонними (иметь две нормали в обе стороны) и тело будет считаться пустым внутри.

Совместно с solid можно также и разобраться с полем convex, которое означает, что трехмерное тело выпуклое (TRUE). Это означает, что все грани плоские, не пересекают друг друга и внутренние углы между гранями тела меньше 180 градусов, иначе тело будет считаться вогнутым или очень сложным (позже я думаю посвятим этому отдельный шаг).

На этом пока все, остальные параметры мы с Вами рассмотрим чуть позже, когда вплотную займемся окраской и текстурированием. А пока вы можете тренироваться в создании сложных трехмерных тел.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Кузин Андрей.