Моделирование процедурного дискобола в HOUDINI 11
Здравствуйте! Меня зовут Максим, я 5 лет занимаюсь трехмерной графикой, и последние год - полтора изучаю Houdini. В этом уроке для начинающих речь пойдет о моделировании в Houdini 11 процедурного или параметрического дискобола, который понадобился мне для одного онлайн проекта. Процедурность получившейся модели относительная, т.к. не все сложилось, как задумывалось, но, тем не менее, этой процедурности достаточно, чтобы без какой либо ручной работы довольно точно отстроить параметры модели. Хочу отметить, что, в силу недостаточности моего опыта «общения» с Houdini, сетап получился не самым лучшим, и я с радостью его усовершенствую вместе с заинтересовавшимися читателями.
Итак, нам нужно смоделировать объект типа этого:
но так чтобы можно было численно регулировать количество плиток, слоев и размер плиток. Понятно, что нам нужно разместить плитки одинакового размера на горизонтальных сечениях сферы так, чтобы плитки располагались с равным для всех сечений шагом. При этом распределение сечений по вертикали я взял из стандартной полигональной 3D сферы.
Возможно это не лучший вариант, но, опять же, в нашем случае этого вполне достаточно.
Создадим точки с нужным нам распределением по вертикали. Для этого создадим сферу: Ctrl+LMB на инструменте Sphere на панели Create, и зайдем в созданный объект sphere_object1 двойным щелчком на нем в Network editor. Далее мы будем работать внутри этого объекта. Сделаем как на картинке:
Здесь я задал:
удобный в нашем случае режим отображения геометрии во вьюпорте.
нужную нам топологию
Добавим к узлу sphere1 узел трансформации: LMB клик в выход узла сферы, отодвигаем курсор – появилась прерывистая линия, нажимаем Tab, и набираем Transform, два раза Enter. Кликаем в флаг Display нового узла, чтобы он стал синим, и результат его действия начал отображаться во вьюпорте. В поле scale узла xform1 в полях x и z, ставим 0. Включаем отображение точек и их номеров во вьюпорте:
У нас получилось:
Добавляем к xform1 оператор Fuse, не забывая про флаг Display. Убираем галку Remove Degenerate, настраиваем параметр Distance так, чтобы номера точек не наслаивались, т.е. так чтобы точки находящиеся в одном месте соединились в одну. Наши правильно распределенные точки готовы.
Теперь сделаем сечения, вдоль которых будем размещать плитки. Создадим Circle в контексте нашего объекта: курсор в свободном поле Network editor, нажимаем Tab, набираем circle, два раза Enter, клик на флаг Display. В новом узле поменяем значение параметра Primitive Type на Poligon, а Orientation на ZX plane. Скопируем этот объект в полученные ранее точки. Для этого добавим к нему оператор Copy, ко второму входу которого подсоединим выход узла fuse1. Получились такая сеть и такой объект:
тобы полученные сечения повторяли форму сферы, а точки на них при этом шли с равным шагом, используем уравнение сферы x*x + y*y*=1. Точнее это уравнение сферы с единичным радиусом и центром в начале координат. Y в нашем случае это радиус сечения, а X – его положение по вертикали, т.е. радиус сечения равен квадратному корню из 1 минус координата сечения по вертикали в квадрате. Вопрос: откуда взять вертикальную координату сечения, а точнее, точки, в которую мы его копируем? Залезаем в Houdini Help. Набираем в поиске Copy, выбираем Copy Surface Node, прокручиваем текст до Local Variables, и находим в них BBY – относительное положение точки (одной из тех, что идут на второй вход узла copy1) в габаритном контейнере по оси Y. Это то, что нам нужно. Далее небольшой трюк Houdini.
В узле copy1 заходим в закладку Stamp, ставим галку Stamp Inputs, вполе Variable1 пишем scale, вполе Value1 пишем pow(1-pow(fit01($BBY,-1,1),2),1/2). В этом выражении мы рассчитываем Y из уравнения окружности. Относительная координата $BBY, меняющаяся от 0 до 1, с помощью функции fit01 приводится к отрезку [-1,1], т.е. результат fit01 меняется от -1 до 1 ( как и координата x в уравнении сферы единичного радиуса с центром в начале координат ). Далее с помощью функции pow возводится в квадрат, результат отнимается от единицы, и опять с помощью функции pow возводится в степень ½, т.е. берется квадратный корень.
Далее между узлами circle1 и copy1 вставляем оператор Transform, в его поле Uniform Scale пишем stamp("../copy1","scale",0) Эта функция для каждой копии извлекает из указанного оператора значение указанной переменной, а т.к. указанная переменная (scale) зависит от вертикальной координаты точки, в которую происходит копирование, то и копируемые окружности масштабируются в зависимости от вертикального положения. Работает для первого оператора Copy идущего ниже по сети. Третий параметр возвращается, если указанная переменная не существует.
Выражение stamp("../copy1","scale",0) скопируйте в поле Divisions узла circle1, и так как выражение меняется от 0 до 1, а для Divisions этого маловато, умножьте на 30. У нас получилось:
Как видите, точки на сечениях везде идут с одним и тем же шагом. Все это работает, только если в операторе Copy стоит галка Stamp Inputs.
Создадим образец плитки и скопируем в новые точки.
Присоедините к выходу нашей сети оператор Fuse. Оставьте его параметры без изменений. Он удалит ненужные верхнюю и нижнюю точки. В контексте нашего объекта создайте объект Grid. В поле Size поставьте по 0.1. Rows и Columns установите по 2. Присоедините к нему оператор Copy, ко второму входу которого присоедините выход fuse2. Отключите отображение номеров точек.
Теперь нужно развернуть плитки вдоль нормалей поверхности сферы. Выделите узел sphere1. Нажмите Ctrl+C и Ctrl+V. В созданном sphere2 и Rows и Columns сделайте 80. Этот объект станет основой для нашего дискобола. Добавьте к sphere2 оператор Point, он добавит нормали к точкам поверхности sphere2, в его закладке Standard поменяйте Keep Normal на Add Normal. Включите режим отображения нормалей точек.
Отключите режим отображения нормалей. Между узлами fuse2 и copy2 вставьте оператор AttribTransfer. Этот оператор передает атрибуты геометрии от одного объекта другому, основываясь на близости геометрии. Второй вход соедините с выходом узла point1. В закладке Attibutes уберите галку Primitives, а в выпадающем списке напротив Points выберите N. Т.о. атрибут "нормаль" будет передан от второй сферы точкам в которые мы копируем плитки. После узла grid1 вставьте оператор Transform, в его поле Rotate.X введите 90.
Как видите, плитки развернулись, но не совсем так, как нам надо: при движении вдоль сечения, они вращаются вокруг своей локальной оси. Выделите point1, вместо $NY поставьте 0. Плитки развернулись:
Теперь давайте менять угол их наклона, основываясь на уже знакомой нам переменной $BBY. Да простят меня худиньщики-математики, я сделал это таким образом: в copy2 ставим галку Stamp Inputs, в поле Variable 1 пишем rotate, а в поле Value 1 такое выражение: 90-90*fit01($BBY,-1,1). Как видите, его значение меняется от 180 до 0 градусов при изменении $BBY от 0 до 1. В узле xform3 в поле Rotate.X введите: stamp("../copy2","rotate",0). Переведите курсор во вьюпорт, нажмите пробел + 3 , получилось вот что:
Опять не то. Отчасти это связанно с тем, что относительно первоначального вертикального размера сферы изменился размер габаритного контейнера точек, в которые копируются плитки. Чтобы решить эту часть проблемы, отключим fuse2: кликните на его флаг Bypass так, чтобы он стал желтым. К выходу copy2 присоедините оператор Merge, к входу которого также подсоедините sphere2. Переведите курсор во вьюпорт, нажмите пробел+1.
Плитки все еще не лежат на поверхности из-за неправильно рассчитанного угла поворота по x. Варианты решения проблемы: заменить уравнение в copy2 на правильное или выровнять плитки вдоль касательных к sphere2 в точках копирования. Я же воспользовался ломовым методом: между copy2 и merge1 вставьте оператор Ray. Поменяйте значение параметра Method на Minimum distance.
Ну вот, плитки мы развернули как надо, теперь увеличим их количество. В sphere1 сделайте Rows равным 32, а в circle1 в divisions замените 30 на 62.
Как видите, плитки начали пересекаться верхними углами, и тем больше чем ближе они расположены к полюсам. Воспользовавшись уже полюбившейся нам локальной переменной $BBY, будем слегка менять форму плиток, двигаясь от экватора к полюсам. В copy2 в закладке Stamp заведем переменную scale, справа от нее напишем pow(1/(abs(fit01($BBY,-1,1))+1),0.2). При изменении $BBY от 0 до 1, первый аргумент функции pow меняется вначале от 0.5 до 1, затем опять до 0.5. Так как переменную scale мы будем использовать как фактор масштабирования углов плиток, и 0.5 дает слишком сильное сжатие, я возвожу первый аргумент pow в степень подобранную «на глаз». Сразу после grid1 добавьте оператор Transform, в параметре Group укажите 0-1. Это номера точек, которые мы будем сжимать; подобраны опытным путем. Group Type изменитена Points, ив Uniform Scale введите stamp("../copy2","scale",0).
Это сверху, а снизу не получилось, т.к. плитки расположены «головой» вверх:
Исправим это: в copy2 заведем еще одну переменную flip, справа напишем на этот раз простое выражение if($BBY>0.5,1,-1) В xform3 в поле Scale.Z впишем stamp("../copy2","flip",0). Теперь плитки ниже экватора будут ориентированы «головой» вниз. Чтобы сохранить направление граней, еще раз их инвертируем, но по другой оси: в xform3 в поле Scale.X также впишем stamp("../copy2","flip",0)
Удалим ненужные плитки на полюсах.
После ray1 поставьте оператор Delete, во вкладке Number уберите галку Enable, во вкладке Normal ставим Enable, Direction делаем [0,1,0], а Spread Angle равным 7. Мы удалили полигоны, основываясь на их наклоне по отношению к заданному в Direction направлению. Spread Angle подбирается экспериментально. Скопируйте delete2 и подсоедините к выходу delete2. Замените в delete3 в Direction 1 на -1. Теперь с обоих полюсов убраны ненужные плитки.
Итак, основа готова. Далее небольшие улучшения. К выходу delete3 присоедините оператор PolyExtrude, во вкладке Local которого а поле Translate.Z введите 0.005. Во вкладке Groups поставьте галку Create Output Groups. Добавьте еще один PolyExtrude. В Выпадающем списке Group выберите extrudeFront, в поле Translate.Z введите 0.005, а Scale установите в [0.9,0.9]. Теперь наши плитки имеют толщину с фаской.
Сделаем декоративные шайбы. В контексте нашего дискобола создайте объект Tube. Во вкладке Detail поставьте галочку End Caps. Вкладку Tube настройте как на картинке.
К выходу tube1 подсоедините Mirror, в поле Direction.Y поставьте -1. Выходы mirror1, polyextrude2 и sphere2 подсоедините к merge1. Удалите ненужный fuse2. В sphere2 Rows и Columns установите по 100, так наши плитки будут располагаться ровнее. После sphere2 можно поставить Null для улучшения визуального представления сети. Наш шар готов.
Но это еще не конец. Превратим наш объект в Digital Asset. В Network Editor перейдите на уровень Scene с помощью нажатия клавиши U. Переименуйте sphere_object1 в Discoball. Нажмите на нем правой кнопкой мыши, в меню выберите Edit Parameter Interface. В появившемся окне перенесите из вкладки By Type в Existing Parameters два раза Integer, один Float Vector 2, один Float, и один Separator. Расположите их как на картинке:
Настроим новые параметры в панели Parameter Description. Первый параметр по порядку:
Второй параметр:
Третий:
Четвертый:
Мы настроили имена, интервалы, и значения параметров по умолчанию. У нас получилось:
Нажмите Accept. На панели параметров объекта Discoball появились новые параметры:
Эти параметры надо привязать к параметрам дискобола. Наведите курсор на надпись Tiles Count Factor, клик правой кнопкой мыши, в выпавшем меню выберете Copy Parameter. Зайдите внутрь объекта Discoball. В узле circle1 клик правой кнопкой мыши на Divisions, в меню выберите Paste Copied References. Поле Division изменилось, отредактируйте его: к появившемуся выражению добавьте *stamp(“../copy1”,”scale”,0), нажмите enter. Раньше у нас в поле Divisions 62 умножалось на stamp(…), теперь на stamp(…) умножается значение поля параметра Tiles Count Factor.
Вернитесь на уровень сцены. Скопируйте Rows Count из объекта Discoball, зайдите в него. В узле sphere1 клик правой кнопкой мыши на Rows, из меню выберите Paste Copied References. Повторите эту процедуру для параметров Tiles Size и Disk Radius, которые надо скопировать соответственно в параметр Size узла grid1 и каждое из полей Radius узла tube1. Вернитесь на уровень сцены, поиграйте с параметрами. Как видите, наш объект обладает достаточной процедурностью необходимой для отладки модели:
В качестве параметра также можно вынести глубину экструда. Попробуйте сделать это сами.
Но и это еще не все :) Нажмите на + рядом со вкладкой Animation, в появившемся меню выберите New Shelf, дайте какое-нибудь имя новой «полке», например My Asssets, нажмите Accept. Перетащите объект Discoball на новую «полку». Удалите Discoball в Network Editor. Перенесите Discoball из My Assets обратно в Network Editor. Можете закрыть худини, открыть, ваш assets будет находиться там же в MyAssets. Нравится? То-то же).
На этом все. Надеюсь мой урок приоткрыл начинающим исследователям Houdini красоту и удобство этого пакета.
Файл сцены: [urokDiscoball.hipnc]