3D Графика Обучение 3DSMax Анимация жидкости


Удачной тебе охоты, о Великий Алл! Вот и пересеклись снова наши пути дорожки. Сегодня я намерен рассказать о том, как можно анимировать жидкость, налитую в некую посудину. Что и говорить, скрипты будут, но немного и не больно :), так что пугаться не надо. Ну все, поехали.

Давайте для начала просто построим произвольный цилиндр и представим, что это жидкость в некоторой емкости. Емкость в настоящий момент нам глубоко неинтересна, потому будем работать только с жидкостью. Наша задача - на этом подопытном цилиндре найти и отработать метод изображения анимации жидкости.

Рассматривать мы будем только случай анимации жидкости в емкости с параллельными стенками. Кто знает, почему - молодец, шагом марш за пирожками с верхней полки, ну а кто не догадался, - узнает в конце урока.

Присвойте цилиндру модификатор Taper, указав в качестве primary axis X, а в качестве Effect - Z. Давайте разберемся, что нам это дает. В принципе, этого уже достаточно для анимации жидкости в стакане, так как данный модификатор позволяет создать односторонний скос (при наклоне стакана установить поверхность горизонтально) и имитировать запаздывание жидкости вследствие инерции. Проверить это утверждение просто - наклоните цилиндр, к примеру, на 30 градусов вдоль оси Y и постепенно увеличивайте значение amount в параметрах модификатора. Получили жидкость в наклоненном стакане. У меня это вышло при значении 0.5, но здесь все зависит от соотношения длины и ширины цилиндра, чем "толще" цилиндр, тем большее значение придется ввести.

Давайте представим, что этот стакан еще и двигается вдоль оси Х в отрицательном направлении. Жидкость сначала запаздывает, поднимаясь по стенке стакана, затем догоняет, потом поднимается на другую стенку, и этот процесс повторяется, постепенно затухая. Попробуйте задать несколько произвольных значений кривизны curve и убедитесь, что одного модификатора Taper вполне достаточно для задания такого движения.

Ну а что делать, если наклон не вдоль оси Y, да и движение не вдоль оси X? Можно, конечно, сделать два модификатора Taper - по одному на каждую ось и затем управлять ими, но мы пойдем другим путем. Мы вспомним, что модификатор Taper имеет gizmo, которое так же можно анимировать. Воспользуемся поворотом габаритного контейнера относительно оси Z локальной системы координат, - это даст нам возможность имитировать наклон/движение стакана в любом направлении. Самостоятельно убедитесь в правильности последнего заявления.

Анимировать все это безобразие вручную - нелегкий труд, следовательно, заставим компьютер делать это за нас. Для начала вспомним, что появился такой модификатор, как Flex, позволяющий задавать "запаздывание" геометрии. Параметры его довольно просты, потому описывать не буду. Кому интересно, создайте кубик, повесьте на него модификатор, задайте кубику какое-либо движение и просмотрите анимацию. Пять минут на разбор полета методом оценки изменений параметров модификатора и поехали дальше. Легко понять, что просто применить модификатор Flex к нашей жидкости мы не можем - нам нужно, чтобы жидкость оставалась в посуде. Однако мы можем создать систему, управляющую параметрами модификатора Taper используя модификатор Flex.

Удалите цилиндр, поскольку нам он больше не понадобится. Постройте куб со стороной, равной единице, и назовите его Inerbox. Примените к нему модификатор Flex. Настройте видовые экраны, выполнив Zoom Extents All. Что нам это дает? Так как наш бокс под воздействием модификатора flex будет изменять геометрию, то и расстояния между точками противолежащих граней будут изменяться, причем в соответствии с настройками модификатора flex, проще говоря, - по законам инерции, что нам и нужно. Мы не можем напрямую использовать значения смещений между этими точками, но, привязав с помощью контроллера Attachment вспомогательный объект к точке на грани, можем получить значение положения опорной точки этого объекта. Простое выражение, определяющее разность положений между опорными точками бокса и вспомогательного объекта даст нам параметр, управляющий анимацией жидкости. Постройте объект Dummy произвольного размера, назовите его Inerdum. Из этих двух объектов мы и создадим управляющую систему. Для начала привяжем объект Inerdum к боксу, используя контроллер Attachment, - этот контроллер позволяет привязать положение объекта к конкретной точке на грани другого объекта.
Выделив объект Inerdum, перейдите на панель Motion, выберите в списке Assign Controller контроллер положения и нажмите на кнопку изменения контроллера. В раскрывшемся окне выберите контроллер Attachment и нажмите Ok. Раскроется свиток параметров контроллера Attachment.

Нажмите кнопку выбора объекта привязки Pick Object и укажите мышью на Inerbox. Имя бока появится над кнопкой выбора объекта. Активируйте режим установки позиции на грани Set Position и снова укажите на Inerbox. В параметрах позиции появятся значения положения Inerdum относительно той грани, на которую вы указали мышью. Исправьте значения: face=2, A=0.5, B=0. Мы выставили вспомогательный объект по центру верхней стороны бокса, обеспечив, тем самым, максимальную амплитуду колебаний опорных точек объектов относительно друг друга.

Отключите режим Set Position. Обратите внимание,- значениями положения для контроллера Attachment являются величины A и B, определяющие размещение объекта на грани. Нас же интересует положение опорной точки объекта в пространстве, - для этого измените контроллер позиции на List, оставив в списке Attachment. Выберите новый появившийся трек Available и присвойте ему контроллер Безье. Дважды щелкните по нему, чтобы сделать активным.

Остался последний штрих - если просмотреть значение положения вспомогательного объекта, выбрав инструмент Select and Move, и щелкнув по нему правой кнопкой мыши при выделенном объекте Inerdum, то можно увидеть, что это значение по оси Z равно единице. Запомним этот момент, он нам пригодится в дальнейшем. Сохраните сцену. Данная заготовка может пригодиться во многих анимациях, имитирующих инерцию, так что можете не уничтожать ее, закончив урок.

Все, с приготовлениями покончено, давайте делать стакан и воду. Спрячьте на время объекты Inerbox и Inerdum, выполните Zoom Extents All для пустых экранов. Поскольку наша задача заключается в том, чтобы разобраться с анимацией, модели объектов мы будем делать чисто схематически. На экране Front постройте две линии, представляющие собой сечения стакана и фужера.

Спрячьте на время сечение фужера, а к сечению стакана примените модификатор Lathe относительно оси Y, указав выравнивание по максимуму. Включите флажки Weld Core и, если нужно, Flip Normal, снимите флажки раздела capping. Перейдите на панель Hierarchy, активируйте вкладку Pivot и выберите режим модификации опорной точки объекта - Affect Pivot Only. Разместите опорную точку по центру объекта, нажав Center to Object. Это позволит легко расположить опорную точку в центре основания стакана, просто переместив ее вниз на виде слева вдоль вертикальной оси. Далее нажмите кнопку активации инструмента Select and Rotate, щелкните по этой кнопке правой кнопкой мыши - откроется окно клавиатурного ввода трансформации. Установите значения поворота локальных осей объекта в разделе Absolute World в ноль, -это обеспечит совпадение ориентации локальной и мировой системы координат в нулевом кадре анимации. Отключите кнопку Affect Pivot Only.

Активируйте инструмент Select and Move и щелкните по нему правой кнопкой мыши. В раскрывшемся окне укажите нулевые значения по всем осям, - объект расположился в начале координат. Создание стакана будем считать законченным. Введите имя объекта - stakan.

Максимизируйте окно Top. Используя привязку к узлам сетки, постройте окружность с началом в центре координат. Отмечу, что оно совпадает с осью вращения стакана. Радиус окружности выберите чуть меньше внутреннего радиуса стакана. Вообще-то их можно сделать равными, но в этом случае нужно значительно увеличить количество сегментов для модификатора Lathe, иначе появляются баги и в окнах проекций, и при визуализации. Это вызвано взаимным пересечением граней разных объектов. Когда большая точность не требуется, например, при нашей анимации, проще (и нам, и компьютеру ;-)) сделать небольшой зазор, благо, что виден он все равно не будет:

Выдавите окружность вверх примерно на половину стакана с помощью модификатора Extrude, число сегментов установите равным пяти. При необходимости поднимите полученную жидкость, чтобы она полностью находилась внутри емкости. Откройте редактор материалов и назначьте стакану и жидкости материалы произвольных цветов. Для наглядности прозрачность стакана установите в 30%, жидкости - в 70%. Назовите объект-жидкость vine. Привяжите vine к стакану с помощью Select and Link (дальше я буду в таком случае писать - прилинкуйте объект:).

Примените к жидкости модификатор Taper, раскройте стек модификаторов, нажав на кнопку Edit Stake, и измените название модификатора на Taper-Inertia.

Разверните окно редактора треков, - основная работа у нас теперь будет происходить в нем. Найдите трек габаритного контейнера модификатора Taper-Inertia и выделите трек вращения. Измените ему контроллер на Local Euler XYZ.
Трек вращения разобьется на составляющие по локальным осям. Выберите трек, соответствующий вращению относительно оси Z и измените контроллер на Float Script. Щелкните правой кнопкой мыши по названию трека для доступа к свойствам контроллера. Давайте введем в открывшемся окне выражение:

Vrot = normalize( $Inerdum.pos-$Inerbox.pos )
ang = degToRad ( acos (dot Vrot [1,0,0]) )
if Vrot.y < 0 do ang *= -1
ang

А вот теперь немного поразмышляем. Мы создаем вектор между опорными точками Inerbox и вспомогательного объекта Inerdum. Длина получившегося вектора нас не интересует, а вот направление - очень даже, поскольку позволяет определить поворот gizmo модификатора Taper, определяющего наклон жидкости. Если мы возьмем нормализованный вектор (вектор, совпадающий по направлению с нашим, но с длиной, равной единице) на плоскости XY, то, вспомнив начала тригонометрии, увидим, что его проекции на оси X и Y равны, соответственно, значениям косинуса и синуса угла. В нашем случае - угла поворота габаритного контейнера модификатора.

В первой строке мы воспользовались возможностями MAXScript для описания вектора. Разберем чуть подробнее. Inerdum и Inerbox - объекты нашей сцены. Чтобы не определять переменных мы обращаемся к объектам с помощью префикса $.

Одним из базовых свойств любого узла сцены, каковыми являются и объекты сцены, является его позиция (точка в пространстве, определяющая положение опорной точки - Pivot объекта), доступ к параметрам которой осуществляется записью имени переменной (либо $+имя объекта сцены) и, через точку, - свойства pos. Таким образом, в скобках у нас записана разность двух точек в пространстве, что и является вектором из одной точки в другую. Функция normalize преобразует этот вектор в нормализованный, длина которого равна единице.

В следующей строке мы определили угол поворота как арккосинус проекции вектора Vrot на единичный вектор вдоль оси X. Длина проекции, как мы уже определили, равна косинусу угла поворота, следовательно, арккосинус даст нам значение самого угла. Осталось только перевести его в радианы, что мы и сделали с помощью функции degToRad.

Строка условия if проверяет, не направлен ли вектор в отрицательном направлении оси Y, и если да, то умножает значение угла поворота на минус единицу. Это объясняется тем, что арккосинус, к примеру, 60 и -60 градусов равен 0.5, следовательно, нужно учесть значение координаты Y, что и делает данная строка. Самая последняя строчка - дань уважения ;) разработчикам контроллера. Дело в том, что скрипт контроллер в качестве значения параметра выбирает самую последнюю строку кода, потому последней строкой и является обычно просто запись переменной, хранящей требуемые значения.

С самым сложным покончили. Нам осталось определить параметры amount и curve для данного модификатора. Мы назначим в качестве элемента управления длину вектора Vrot, предварительно вычтя из нее единицу. Как Вы помните, в начальный момент времени значения положений объектов Inerbox и Inerdum равны по осям X и Y (нулевые), а по оси Z отличаются на единицу. Соответственно, длина вектора Vrot в состоянии покоя равна единице. Вычтя единицу, мы получим нулевое воздействие в состоянии покоя и некоторое значение, определяемое параметрами модификатора flex, при анимации. Я специально не даю конкретных параметров модификаторов и, далее, множителей выражений, для того, чтобы Вы сами попробовали найти подходящие значения и, заодно, разобрались, что же они дают:

Нет никакой принципиальной разницы, каким контроллером воспользоваться для записи управляющего параметра - Expression или Script, потому для разнообразия присвойте треку amount модификатора Taper-Inertia контроллер Expression. Определите две векторные переменные, совпадающие по именам с нашими объектами - Inerbox и Inerdum. Для этого наберите имя переменной в поле Name, поставьте переключатель в значение vector и нажмите Create. Введите новое имя переменной и снова нажмите Create. Выделите в списке переменных Inerbox, нажмите кнопку Assign to Controller и, в раскрывшемся окне выберите трек Inerbox. Повторите операцию для Inerdum, указав трек Inerdum.

В поле expression введите выражение:

(( length (inerdum-inerbox)-1 )/25 )*1

здесь length() - функция, возвращающая значение длины вектора, из этого значения мы вычли единицу. Делитель -коэффициент, зависящий от траектории движения стакана.

Для трека curve (curvature) установите контроллер Script и введите выражение:

vrot = ( $Inerdum.pos - $Inerbox.pos )
curv = ( length(vrot)-1 )/( -10 )
curv

Данная запись идентична, за исключением множителя, выражению предыдущего контроллера, только выполнена в виде сценария - script. Что лучше - не знаю, кому как удобнее, потому показал оба варианта.

Прилинкуйте Inerbox к стакану, жидкость уже должна быть прилинкована, а Inerdum привязан к Inerbox через контроллер Attachment, то есть при перемещении стакана должны двигаться все объекты сцены.

Примечание: поскольку инерция - штука тонкая, то для конкретного движения придется настраивать множители, чтобы не было перехлестов жидкости через край. Для моей сцены подошли значения от 10 до 35, но это сильно зависит от изломанности траектории и скорости движения. Нужно только смотреть, чтобы значение curve было противоположно по знаку amount - для этого я просто разделил curve на отрицательное число. Не забывайте, что есть еще и параметры модификатора Flex. Я бы советовал устанавливать значения параметра flex близкими к нулю (0,05-0,2), чтобы избежать больших пиковых значений, strength - от 10 до 20, а sway - от 3 до 15, чем выше последняя цифра, тем скорее будут затухать колебания жидкости.

Постройте на виде Top произвольную кривую и назначьте ее в качестве пути для объекта stakan. Для этого выделите стакан, перейдите на панель Motion, выделите контроллер положения и измените его на Path, нажмите в свитке параметров контроллера кнопку Pick Path и укажите на Ваш сплайн. Разверните окно редактора треков и найдите трек позиции для стакана. Уменьшите диапазон действия контроллера, передвинув концевые маркеры. Просмотрите анимацию. При необходимости измените множители выражений. Я для отладки запускаю анимацию, разворачиваю окно свойств контроллера и меняю множитель прямо при проигрывании - легче видеть результат в действии. Для того, чтобы внесенные в код изменения стали действовать, необходимо нажать кнопку Evaluate. Пример сцены с состоянием на данный момент Вы можете скачать здесь - Inerflex.max (131kb)

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

Для того, что бы применить шум к поверхности жидкости, выделите объект vine, и присвойте ему модификатор Volume Select. Войдите в окно редактирования стека модификаторов. Выделите этот модификатор и нажмите кнопку Cut, выделите модификатор Extrude и нажмите кнопку Paste. Мы перенесли модификатор ниже по стеку. Переименуйте Volume Select в Vselect Open Mesh. Нажмите кнопку Ok, и выберите в списке модификаторов Vselect Open Mesh. В разделе параметров укажите Stack Selection Level = face. Нажмите кнопку Sub - Object для работы с gizmo модификатора. Переместите габаритный контейнер на виде фронт вверх, до состояния, указанного на рисунке - в этом случае мы выделим только грани, принадлежащие "поверхности жидкости". Отключите уровень Sub - Object.

Итак, Vselect (Open Mesh)открывает подобъектный выбор и передает в стек информацию о выборе граней верхней поверхности. Назначьте модификатор Noise. Модификатор Noise использует полученную информацию, воздействуя только на выделенные грани, потому его габаритный контейнер расположен в плоскости поверхности жидкости.

Закройте подобъектный выбор граней, назначив еще один модификатор Volume Select. Измените ему название на Vselect Close Mesh. Как Вы знаете, операции на подобъектном уровне помечаются в стеке модификаторов звездочкой, давайте проверим стек:

Если у Вас получилось то же самое, то все в порядке - шум действует на уровне граней, а модификатор изгиба - на уровне всего объекта. раскройте окно редактора треков и найдите трек модификатора Noise, разверните его и измените контроллер трека Strength на Point3 Expression. Это один из видов контроллеров выражения, оперирующий векторными величинами, например цветовыми составляющими (отсюда и название Point3) или составляющими по осям XYZ, как в случае параметра Strength. Логика действий прежняя, определить две векторные переменные и записать выражение. Кто не справится сам, смотрите на рисунок, или пишите письма ;-), чем смогу, помогу.

Если Вас не устраивает сила шума, увеличьте множитель, если мало граней и эффект плохо просматривается, то либо измените тип выходной поверхности у модификатора Extrude на Path, либо воспользуйтесь модификатором Edit Mesh и разбейте грани с помощью tessellate.

Теперь обещанные пряники. Что, если нам нужно иметь жидкость в сосуде другой формы, ну, к примеру, в ведре? Как известно, у большинства ведер форма напоминает не цилиндр, а усеченный конус. Нет ничего проще,- воспользуемся тем, что модификации осуществляются последовательно и могут назначаться сразу нескольким объектам. То есть, взяв сейчас наш стакан и жидкость, мы можем применить к ним еще один модификатор, и изменить их форму, что никак не отразится на анимации, - сначала будет просчитываться движение жидкости, а уже потом изменяться форма полученного объекта. Выделите объекты vine и stakan и примените модификатор Taper. Обратите внимание, этот модификатор назначился сразу обоим объектам, причем копии модификатора связные - reference. Следовательно, изменяя одну копию, мы изменим и другую. Не снимая выделения, откройте окно редактирования стека,- там присутствует только один модификатор Taper, поскольку он один общий для обоих выделенных объектов. Переименуйте его в Taper - Stakan. Измените значение amount для получения необходимой кривизны. Просмотрите анимацию, если хотите, можете скачать файл сцены с состоянием на настоящий момент - inerflex3.max (136kb)

А если нам нужно получить жидкость, скажем в фужере? Удалите объект stakan, и заодно удалите модификатор Taper-Stakan для объекта vine. Откройте созданное сечение фужера и повторите операции создания емкости - выравнивание опорной точки и применение модификатора lathe. Разместите фужер так, чтобы жидкость оказалась внутри емкости. Если размеры не совпадают, измените радиус окружности, из которой создавалась жидкость. Поскольку у меня сечения стакана и фужера совпадают, за исключением ножки, то мне просто нужно было правильно расположить объекты. Чтобы не переписывать контроллеры задайте фужеру то же имя - stakan и привяжите к нему Inerbox и vine. Назначьте фужеру контроллер движения по пути и укажите путь, тот же, что был у стакана. Просмотрите анимацию - все должно работать.

Выделите объект stakan и вернитесь на уровень работы со сплайном, включите уровень вершин и несколько подправьте форму фужера, не касаясь внутренней части.

Вернитесь на уровень работы с объектом, и далее на уровень модификатора Lathe. Выделите объекты vine и stakan и назначьте выделению модификатор FFD-Cyl. Измените форму по вкусу. Если все делали правильно, должно выйти что-то вроде этого - это кадр анимации, которую делал при написании данного урока.

Hosted by uCoz