Этот урок создавался для 3dcenter.ru
Результат выполнения урока


Морфинг по Вороному

“Морфинг по Вороному” - эффект преобразования одного объекта в другой при помощи Particle Flow, MAXScript и прикладной математики.
Всем привет, в этом уроке я снова буду демонстрировать возможности MAXScript применительно к эффектам Particle Flow. Кстати, могу сказать что Particle Flow это самый “скриптабельный” компонент 3ds max. Скрипты под него простые и короткие, а эффекты выглядят великолепно. Но в этом уроке я одними скриптами для Particle Flow не обойдусь. Мне понадобится еще и просто скрипт – так сказать, скрипт “сам в себе и для себя”. Прочитав название урока вы, наверное, сразу подумали – а кто такой, собственно, Вороной? Это, скажу я вам без преувеличений великий математик, имеющий к компьютерной графике самое непосредственное отношение. Хотя в его время (начало прошлого века) еще и калькуляторов-то не изобрели. Но не буду пока забегать вперед (а точнее назад) и лучше начну с главного, а именно с идеи.

Идея урока в том, чтобы разделить на реалистичные осколки один объект, а потом из этих осколков собрать второй. Понятно, что не ко всякому объекту подойдут осколки от другого, поэтому в процессе преобразований и анимации первые осколки нужно трансформировать во вторые и сделать этот морфинг по возможности незаметно, т.к. чем незаметнее, тем реалистичнее и эффектнее будет выглядеть наша Particle Flow анимация. Выглядеть должно “как будто так и было”, а морфингом я назвал этот эффект потому, что в данном уроке меняется геометрия осколков, но сохраняется их количество (чтобы не усложнять, хотя в принципе от количества частиц-осколков ничего не зависит).

Обдумываю идею и разделяю урок на четыре основных этапа:

  1. Разделение первого объекта на осколки
  2. Перемещение осколков к местоположению второго объекта
  3. Изменение геометрии осколков
  4. Сбор второго объекта
Вообще задача сбора одного объекта из осколков другого считается в компьютерной анимации классической. Смотрим, что предлагает нам по умолчанию 3ds max для разбиения на осколки – в системе частиц PArray можно создать осколки из случайных наборов граней как фрагментов объекта (свиток Particle Type - Objects Fragments / Number of Chunks) все осколки выдавливаются на заданную одинаковую величину, получается вот такое убожество (для сферы как пример):


Для реализма вариант неудовлетворительный, так как осколки одинаковы по толщине, предсказуемы по форме, кроме того, если делать этим способом, внутри объекта получается пустота, которой там быть не должно. Возникает задача – как разбить объект на разнообразные осколки приемлемой формы, общий объем которых равен объему объекта, т.е. так, как в реальности. И вот тут мне пригодятся идеи Георгия Федосеевича Вороного (украинский математик 1868-1908) из области теории квадратичных форм. Тем, кто сейчас испугался и думает, что дальше начнется высшая математика и будет скучно, хочу сразу сказать, что не начнется и не будет. Будет определение многогранника Вороного и примеры, а дальше опять частицы, алгоритмы и скрипты. Итак, трехмерный многогранник Вороного это совокупность точек трехмерного пространства, каждая из которых находится не дальше от точки О данного пространства (назову ее центром), чем от других таких центров. Пусть вас не смущает слово пространство, оно в нашем случае ограничено поверхностью объекта, который бьется на осколки, а многогранники Вороного и будут являться собственно осколками. В картинках это выглядит так:


На плоскости (многоугольники Вороного)



В пространстве куба (я немного сдвинул дальние многогранники для наглядности)

Многогранники Вороного покрывают пространство без щелей и наложений, их грани равноудалены от соседних центров, многогранники выпуклые, каждый можно построить, отсекая лишнее от общего объема, плоскости сечений проходят через середины отрезков, соединяющих соседние центры и перпендикулярны этим отрезкам. Вот и все, математика кончилась, теперь надо написать алгоритм разбиения объекта (любого) на такие вот многогранники. Дальше описывается мой алгоритм построения многогранников, точнее оптимизация существующего алгоритма (его еще до меня придумали) и в общем-то я делаю это для mir-vadim, Shiva и megavitus, которые тоже были озадачены проблемой скорости построения многогранников и медленной работой существующих скриптов для данной задачи. Короче, следующий раздел можете пропустить, если кому неинтересно. Скрипт в конце урока будет выложен уже готовый и вам не придется ломать над ним голову.


Алгоритм поиска стопроцентных соседей

Математически решить задачу построения многогранника мне оказалось не под силу и промучавшись четыре дня я вернулся к истокам, а именно к скриптам для построения ячеек Вороного (Voronoi Cells, Voronoi Fracture – пользуясь случаем выражаю огромную благодарность их автору GARP), которые давно уже выложены на сайте scriptspot. Почему вернулся, а не начал с них? Потому что эти скрипты работают буквально так, как я описал раньше, т.е. отрезают от основного объема куски модификатором Slice, потом закрывают пустоту модификатором Cap Holes и делают это ровно n*(n-1) раз, где n – требуемое количество осколков. В результате получаются отличные многогранники Вороного, но для 1000 многогранников это выливается в 999000 операций обрезания. Зависимость от числа точек квадратичная - алгоритм работает слишком медленно, в программировании такой подход называется brute force (грубая сила), который используют, когда нет другого выхода. А я, увлекшись математикой, неделю изучал теорию и пытался найти решение другими способами, которые позволяли бы сразу определить соседние точки и пропустить оставшиеся, не перебирая весь массив центров для каждого многогранника. На пятую бессонную ночь скриптописательства и тестирования вероятных алгоритмов такой способ я нашел. Эврика, решение оказалось элементарным.

Создаю двумерный массив (у меня используется конструкция struct), в первом элементе которого содержится номер текущего центра, а во втором – массив номеров всех остальных центров, отсортированный по расстоянию до текущего (по возрастанию). То есть по уменьшению вероятности влияния этой точки на многогранник. Ведь чем дальше находится точка, тем меньше у нее возможностей отрезать кусок от рабочего объема, так как сначала отрезают ближайшие соседние точки и если они уже все отрезали, то секущая плоскость дальней по расстоянию точки не пересечется с создаваемым многогранником и отрежет пустоту. И теперь нужно определить только этот предел – за которым точки всегда будут резать пустоту и тогда их можно смело пропустить и перейти к построению нового многогранника. Итак, начинаем резать: первая точка отрезала часть от объема базовой фигуры, рабочий объем уменьшился, дальше вторая, объем опять уменьшился, третья и т.д. до тех пор, пока новая точка уже не сможет ничего отрезать. Критерий простой – эта точка будет та, половина расстояния от которой до центра многогранника больше, чем длина радиуса описанной вокруг многогранника сферы. Я даже делаю проще – беру сферу, описанную вокруг габаритного контейнера многогранника, ибо ее радиус заведомо больше, а поиск описанной сферы минимального радиуса – та еще задачка и ее внедрение в алгоритм его только замедлит. Радиус сферы, описанной вокруг габаритного контейнера находится элементарно – он равен половине расстояния между максимальной и минимальной точками контейнера. Габаритный контейнер с каждым обрезанием уменьшается вместе с многогранником и как только он будет достаточно малым, чтобы секущая плоскость новой точки не пересекла сферу – это значит, что дальше резать не нужно и бесполезно, текущий многогранник готов, можно пропустить оставшиеся точки и строить следующий многогранник Вороного. Написал много слов, поясняю на картинке:


идея алгоритма

Точки в массиве отсортированы по расстоянию, и идут в порядке красная, желтая, голубая. Зеленая сфера описана вокруг розового габаритного контейнера. Начинаю строить, красная точка отрезает объем от многогранника и формирует одну из граней. Идем дальше и видим, что плоскость от желтой точки не пересекает сферу габаритного контейнера. Ага, значит все, красная точка была последняя, многогранник готов и по плоскостям желтой и голубой точки можно не резать – новых пересечений все равно не будет и голубая алгоритмом даже вообще не проверяется. У себя в скрипте я не делаю эту проверку каждый проход, так как гарантированно первые 4 точки образуют грани и в принципе, если точек много, то можно начинать проверять примерно после 16-ой, а до этого отрезать не задумываясь. Вот такой простой и главное быстрый алгоритм. Например для 1000 точек будет около 20000 обрезаний. 20000 и 999000 – разница более чем заметная. Опишу фрагменты кода, разбив их на части, сначала идет код, потом его описание. Комментарии я пишу русскими большими буквами намеренно, так как маленькая русская буква “я” приведет к ошибке при запуске скрипта. Ее употреблять нельзя, даже в комментариях, это баг, и чтобы не думать об этом я приучился писать заглавными буквами.


Часть 1. Начальные данные
global NumChunks=100 --СТО ОСКОЛКОВ
global fragmentMesh_Destination --МЕСТО ХРАНЕНИЯ ОСКОЛКОВ
struct pointprops (id, coord) --СТРУКТУРА ДЛЯ ХРАНЕНИЯ КООРДИНАТ
struct simplexprops (verts, num) --СТРУКТУРА ДЛЯ ХРАНЕНИЯ УПОРЯДОЧЕННЫХ НОМЕРОВ ЦЕНТРОВ

Задаю глобальные переменные для хранения количества осколков и объектов самих осколков. Слово global означает, что эти переменные можно вызвать из любого скрипта в текущей сессии 3ds max. Конструкции pointprops и simplexprops позволяют создать многомерные массивы и обращаться к их элементам по ключевым словам, а не только по индексам.

fn compareFN v1 v2 valArray: centerpoint: = ( --ФУНКЦИЯ ИНДЕКСИРОВАННОЙ СОРТИРОВКИ ПО ВОЗРАСТАНИЮ РАССТОЯНИЯ ДО ЗАДАННОЙ ТОЧКИ
local v1i = valArray[v1].coord - centerpoint
local v2i = valArray[v2].coord - centerpoint
local d = (length v1i)-(length v2i)
case of (
(d < 0.): -1
(d > 0.): 1
default: 0
)
)

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


Часть 2. Заполнение массивов
obj=selection[1] --СКРИПТ РАБОТАЕТ ДЛЯ ВЫДЕЛЕННОГО ОБЪЕКТА

--СОЗДАЮ ОБЛАКО ЧАСТИЦ ВНУТРИ ВЫДЕЛЕННОГО ОБЪЕКТА
a = pcloud name: "PCloud_chunks" emitter:cd formation:3 quantityMethod:1 total_number: NumChunks viewPercent:100 seed:(random 0 1000) isHidden:true 

--ПУСТОЙ МАССИВ ДЛЯ ОСКОЛКОВ НА БУДУЩЕЕ
fragmentMesh_Destination=#()
fragmentMesh_Destination[NumChunks]=0 --ТАКИМ ОБРАЗОМ СОЗДАЕТСЯ ПУСТОЙ МАССИВ ФИКСИРОВАННОГО РАЗМЕРА (ТАКЖЕ СОЗДАЮТСЯ МАССИВЫ И В ДАЛЬНЕЙШЕМ)

--КОПИРУЮ ОБЪЕКТ, ОБНУЛЯЮ ЕГО ТРАНСФОРМАЦИИ
tempmesh=copy obj
temp_trans = tempmesh.transform
tempmesh.transform = (matrix3 [1,0,0] [0,1,0] [0,0,1] [0,0,0])
addModifier tempmesh (XForm())
tempmesh.XForm.gizmo.transform = temp_trans
collapseStack tempmesh
temp=tempmesh.mesh
rotPlane = plane isHidden:true --ВРЕМЕННЫЙ ОБЪЕКТ Plane

--ПУСТОЙ МАССИВ КООРДИНАТ
pointsarr = #()
pointsarr[NumChunks]=0
curver=0

for i = 1 to NumChunks do pointsarr[i]=pointprops id: i coord: (particlePos a i) --ЗАПОЛНЯЮ МАССИВ ТОЧКАМИ ЦЕНТРОВ

--ГОТОВЛЮ МАССИВ ЦЕНТРОВ
pp=pointsarr.count
simplexarr = #()
simplexarr[pp] = 0

for k=1 to pp do ( --ЗАПОЛНЯЮ МАССИВ ЦЕНТРОВ ОТСОРТИРОВАННЫМИ НОМЕРАМИ
cp=pointsarr[k].coord --КООРДИНАТА ТЕКУЩЕГО ЦЕНТРА
indexArray = #()
indexArray[pp] = 0
indexArray = for i = 1 to pointsarr.count collect pointsarr[i].id --СОБИРАЮ ВО ВРЕМЕННЫЙ МАССИВ НОМЕРА ЦЕНТРОВ
qsort indexArray compareFN valArray: pointsarr centerpoint: cp --СОРТИРУЮ НОМЕРА ПО РАССТОЯНИЮ ОТ ТЕКУЩЕГО ЦЕНТРА
deleteItem indexArray 1 --УДАЛЯЮ ПЕРВЫЙ ЭЛЕМЕНТ МАСИВА, ТАК КАК ИМ БУДЕТ ЯВЛЯТЬСЯ ТЕКУЩИЙ ЦЕНТР
simplexarr[k]=simplexprops verts: indexArray num: indexArray.count --ЗАПОЛНЯЮ МАССИВ НОМЕРАМИ ЦЕНТРОВ
txt="Sorting: "+(k as string) + " of "+(pp as string)
pushPrompt txt --ПЕЧАТАЮ В СТАТУСНОЙ СТРОКЕ ИНФОРМАЦИЮ О ХОДЕ ПРОЦЕССА
)

Итак, во второй части происходят все необходимые приготовления и создаются рабочие массивы с данными. Массив с центрами получается, например, такого вида:

A[1]=verts: #(2, 3, 4) num:3
A[2]=verts: #(1, 3, 4) num:3
A[3]=verts: #(4, 1, 2) num:3
A[4]=verts: #(3, 1, 2) num:3

Третья часть работает одновременно с двумя исходными массивами – массивом координат центров и массивом отсортированных индексов вершин.


Часть 3. Создание осколков

--ПОШЛО ПОСТРОЕНИЕ ОСКОЛКОВ, ЗА ОДИН ПРОХОД ЦИКЛА СТРОИТСЯ ОДИН МНОГОГРАННИК ВОРОНОГО
    for k=1 to simplexarr.count do if not keyboard.ESCpressed then ( --ЦИКЛ ПО ВСЕМ ЭЛЕМЕНТАМ МАССИВА ЦЕНТРОВ ОТ ПЕРВОГО ДО ПОСЛЕДНЕГО ЦЕНТРА
            cp=pointsarr[k].coord --КООРДИНАТА ТЕКУЩЕГО ЦЕНТРА
            thePart = editable_mesh pos: cp --СОЗДАЮ ПУСТОЙ МЕШ
            thePart.pivot=cp --ЗАДАЮ ЕМУ ОПОРНУЮ ТОЧКУ В ЦЕНТРЕ
            thePart.name=uniqueName "Chunks_Destination" --ИМЯ НОВОЕ
            thePart.mesh = tempmesh.mesh --ГЕОМЕТРИЮ КОПИРУЮ С ИСХОДНОГО ОБЪЕКТА
            addModifier thePart (XForm()) --ОБНУЛЯЮ ТРАНСФОРМАЦИИ
            thePart.XForm.gizmo.pos = tempmesh.pos - cp
            collapseStack thePart --СВОРАЧИВАЮ СТЕК МОДИФИКАТОРОВ
           
            mSlice = sliceModifier slice_type:2 --МОДИФИКАТОР Slice ЗАНОШУ В ПЕРЕМЕННУЮ
           
            radius=length (pointsarr[simplexarr[k].verts[simplexarr[k].num]].coord - cp) --ОЧЕНЬ БОЛЬШОЙ РАДИУС ПО УМОЛЧАНИЮ
            distanceplane=0.5*radius --РАССТОЯНИЕ ДО ПЛОСКОСТИ ЗАВЕДОМО МЕНЬШЕ РАДИУСА
            i=0-ИНДЕКС ТОЧКИ, ОТ КОТОРОЙ БЕРЕТСЯ СЕКУЩАЯ ПЛОСКОСТЬ
            do ( --ВНУТРЕННИЙ ЦИКЛ ПО ОТСОРТИРОВАННЫМ ЦЕНТРАМ ЦЕНТРА. ЗА ОДИН ПРОХОД ОДНА ОПЕРАЦИЯ ОТРЕЗАНИЯ. РЕЖУ ПОКА РАДИУС БОЛЬШОЙ
                        i+=1 --УВЕЛИЧИВАЮ ИНДЕКС НА ЕДИНИЦУ
                        cv=simplexarr[k].verts[i] --НОМЕР СОСЕДНЕЙ ТОЧКИ, ПО ЭТОМУ СОСЕДУ РЕЖЕМ
                if cv!=k then ( --ПРОВЕРКА НА СОВПАДЕНИЕ РЕЖУЩЕЙ ТОЧКИ С НОМЕРОМ ЦЕНТРА, ЧТОБЫ САМО ПО СЕБЕ НЕ РЕЗАЛО ХОТЯ И НЕ ДОЛЖНО
                        theVec = pointsarr[cv].coord – cp --ВЕКТОР ОТ КООРДИНАТ СОСЕДА ДО ТЕКУЩЕГО ЦЕНТРА
                        rotPlane.dir = theVec --ОРИЕНТАЦИЯ ВРЕМЕННОЙ ПЛОСКОСТИ ПО ВЕКТОРУ
                        addModifier thePart mSlice --ДОБАВЛЯЮ МОДИФИКАТОР
                        mSlice.slice_plane.rotation = rotPlane.rotation --ПОВОРОТ РЕЖУЩЕЙ ПЛОСКОСТИ ПО ВЕКТОРУ
                        mSlice.slice_plane.pos = theVec / 2 --СТАВЛЮ РЕЖУЩУЮ ПЛОСКОСТЬ НА СЕРЕДИНУ ОТРЕЗКА (ОБРЕЗАЛ)
                        addModifier thePart (cap_holes()) --ДОБАВЛЯЮ МОДИФИКАТОР Cap Holes, ЗАКРЫВАЮ ПУСТОТУ НОВОЙ ГРАНЬЮ
                        collapseStack thePart --СВОРАЧИВАЮ СТЕК МОДИФИКАТОРОВ
                if i>16 then ( --16 РАЗ ОТРЕЗАЛ И НАЧИНАЮ ПРОВЕРЯТЬ УСЛОВИЕ ВНУТРЕННЕГО ЦИКЛА
                    distanceplane=0.5*(length theVec) --РАССТОЯНИЕ ДО РЕЖУЩЕЙ ПЛОСКОСТИ
                    mi=[thePart.min.x, thePart.min.y, thePart.min.z] --МИНИМУМ ГАБАРИТНОГО КОНТЕЙНЕРА
                    ma=[thePart.max.x, thePart.max.y, thePart.max.z]-МАКСИМУМ ГАБАРИТНОГО КОНТЕЙНЕРА
                    d=0.6*(distance mi ma) --БОЛЬШЕ ПОЛОВИНЫ ОТ РАДИУСА ГАБАРИТНОГО КОНТЕЙНЕРА
                    if d<radius then radius=d --УМЕНЬШАЮ РАДИУС
                )
                    txt="BUILD: "+(k as string)+" "+(i as string)+" of "+(simplexarr[k].num as string)
                pushPrompt txt --ПЕЧАТАЮ ИНФОРМАЦИЮ О ПРОЦЕССЕ В СТАТУСНОЙ СТРОКЕ
                )
            )
            while radius>distanceplane and i<simplexarr[k].num –-КОНЕЦ ЦИКЛА ОТРЕЗАНИЯ
                simplexarr[k].num=0 --ОБНУЛЯЮ ВТОРОЙ ЭЛЕМЕНТ СТРУКТУРЫ (ДЛЯ СПРАВКИ ЧТО ПРОШЕЛСЯ ПО ВСЕМ СОСЕДЯМ ТЕКУЩЕГО ЦЕНТРА)
   
    convertTo thePart (Editable_Mesh) --КОНВЕРТИРУЮ ОСКОЛОК В МЕШ
    fragmentMesh_Destination[k]=thePart --ЗАНОШУ ТЕКУЩИЙ ОСКОЛОК В МАССИВ ОСКОЛКОВ
           
    )-КОНЕЦ ЦИКЛА ОСКОЛКОВ

--ПОДЧИЩАЮСЬ, УДАЛЯЮ ТЕПЕРЬ УЖЕ НЕНУЖНЫЕ ВРЕМЕННЫЕ ОБЪЕКТЫ
delete rotPlane
delete tempmesh
delete a

redrawViews() --ПЕРЕРИСОВЫВАЮ ВИДОВЫЕ ЭКРАНЫ

Непосредственное создание осколков состоит из двух циклов, одного внутри другого. Первый цикл for идет по всем центрам, выбирая текущий центр вокруг которого строится текущий осколок, второй do...while – по всем остальным центрам, отсортированным относительно текущего. Второй цикл выполняет проверку алгоритма поиска стопроцентных соседей, если она выполняется – цикл прекращает свою работу.


Описание начальной сцены и скрипт “сам в себе”

Для создания эффекта морфинга по Вороному я использую три скрипта. Два из них работают непосредственно в Particle Flow, третий нужен, чтобы создать осколки для второго объекта – который собирается из осколков первого. Для краткости и удобства назову первый объект $Source (т.е. исходный), а второй $Destination (т.е. целевой). Знак $ перед именем объекта означает, что я в скрипте обращаюсь к нему по имени – это вам пригодится, когда будете разбираться в коде скриптов или править их под себя. Вообще в этом уроке названия играют не последнюю роль. Для справки - просто пустой $ в MAXScript означает текущее выделение, иными словами $=selection. Итак, в эффекте участвуют:

  1. Объект Sphere Radius: 150 Segments: 32 Name: "Source". Координаты: [-923.11,-179.699,150.005] (Мыхыхы, у себя можете округлять).
  2. Объект СhamferBox Length: 300 Width: 300 Height: 600 Fillet: 20 Name: "Destination" Координаты в нулевом кадре: [-580.835,-179.699,0.00500488]. В 220-ом: [1307.76,-179.699,0.00500488]
  3. Объект $TDestination01 - копия целевого объекта, которая находится в точке собирания его осколков. Копия нужна потому, что объект $Destination анимируется по позиции, а координаты статичного объекта и объекта с координатами, анимированными по ключам это для MAXScript две большие разницы и чтобы лишний раз не морочиться, я сразу определил конечное положение осколков через дополнительный объект. Координаты: [1307.76,-179.699,0.005]
  4. Набор объектов $Chunks_Destination* задают финальное положение осколков (символ * означает, что вместо него могут быть любые другие символы – у меня это цифры от 01 до 200, стало быть всего двести осколков). Эти объекты делает из $TDestination01 скрипт “сам в себе”.
  5. Объект PF Source – система частиц, с которой происходят все перетурбации. Вначале я думал, а получится ли обойтись одной системой? Получится.
  6. Объект Gravity Force: 2.0 Decay: 0.01 – пространственная деформация гравитации, которая сдувает частицы с насиженных мест (с исходного объекта) в самом начале эффекта.
  7. Рабочая область окна Particle View и все, что в ней находится.
  8. Скрипты.

Если для объектов указаны не все параметры, значит, остальные берутся по умолчанию. Еще используется анимированная камера и два источника света - для наведения “марафета”, описывать их не буду, все равно у вас будет по-другому. В сцене 270 кадров (по 30 штук в одной секунде), единицы измерения – миллиметры. Значки объектов гравитации и системы частиц расположены слева от исходного объекта (если смотреть в окне Front), их стрелки направлены слева направо, т.е. в сторону, куда происходит все действие.


Справа результат работы скрипта

Первым делом я выделяю объект $TDestination01, запускаю скрипт “сам в себе”, смотрю что получилось и скрываю все это вместе с объектом. Больше мы их не увидим. Ах да, я же забыл рассказать, откуда скрипт узнает центры многогранников для разбиения. Очень просто – он создает внутри объекта облако частиц PCloud и берет координаты частиц. Потом сортирует по расстоянию, копирует общий объект и начинает резать. В статусной строке печатается ход процесса. Подробнее написано в комментариях внутри скрипта. Вот что получилось в итоге - крупным планом многоугольники Вороного themselves:


Теперь самое главное – Particle View и описание всего того, что я в нем использую. На частицы из источника PF Source 01 последовательно одно за другим влияют три события (events): Разбиение, Перемещение и Собирание, в которых находятся операторы (синие квадраты) и тесты (желтые ромбы) перехода к следующему событию. Вообще раз уж я берусь за описание операторов Particle Flow это получается и не урок по MAXScript, и не по Particle Flow, а по им обоим одновременно. Ну это вам судить, кому что понравится, тот там и прочитает. Описание операторов тоже можно пропустить, если вы это все уже знаете.




СОБЫТИЕ ПЕРВОЕ: Разбиение

Оператор Birth Script особенный хотя бы потому что он представляет собой не синий квадрат как все остальные, а зеленый круг :-D. На деле это означает что его нельзя двигать вверх и вниз по событию, так как он “рождает” частицы. Как Birth Script это делает – он работает примерно также как и скрипт “сам в себе” но применительно к Particle Flow - разбивает объект $Source на осколки и присваивает частицам их геометрию, иными словами создает осколки из частиц. Частицы рождаются в нулевом кадре - единственный кроме скрипта параметр оператора оставляю по умолчанию. Это первый скрипт непосредственно для Particle Flow, второй и последний будет в третьем событии (видите, не все так сложно, скриптов оказывается нужно совсем чуть-чуть, чтобы добиться нужного эффекта). Скажу по секрету, когда я делал урок и тестировал различные варианты, у меня было 5 событий и в каждом по скрипту. Перестарался. Идем дальше, продолжаю описывать операторы и их интерфейс. Если операторы в событиях повторяются, например оператор Speed я использую дважды, а Spin трижды, или параметры в операторах повторяются, например Uniqueness присутствует едва ли не в каждом операторе, то я опишу их для первого оператора, в котором они встречаются, а дальше буду пропускать, т.к. принципы их работы везде одинаковые.



Speed (Скорость)

Speed - cкорость одной частицы в мм/с (у меня системные единицы миллиметры, а скорость измеряется в системных единицах на одну секунду). Положительные значения скорости двигают частицы в направлении, определяемом параметрами группы Direction, а отрицательные в противоположном направлении. Примечание: скорость для частицы задается один раз при поступлении частицы в событие (или при рождении, как в данном случае). Анимация параметра не даст никакого эффекта, так как каждая частица, поступившая в событие, получает постоянную скорость в соответствии с параметрами Speed и Variation.

Variation (отклонение) – величина, на которую меняется скорость каждой частицы относительно величины Speed (измеряется в системных единицах на секунду). Чтобы определить скорость каждой частицы, система умножает значение Variation на случайное число из диапазона от -1.0 до 1.0, а потом добавляет результат к значению параметра Speed. Например, если скорость 300, а отклонение 100, то каждая частица получит случайную скорость из диапазона от 200 до 400.

Direction (направление) - группа параметров, задающих направление скорости движения частиц. В большинстве случаев направление зависит от ориентации значка PF Source, которое, кстати, и я использую, т.к. из списка выбран вариант Along Icon Arrow. Но, тем не менее, расскажу о всех пунктах этого списка. Да, и еще о направлении – как правило, направление это всегда прямая линия, если только на частицу не влияют другие факторы в других операторах. Итак, варианты направления:

  1. Along Icon Arrow – частицы двигаются параллельно стрелке значка источника. Чтобы изменить это направление, достаточно повернуть источник.
  2. Icon Center Out – каждая частица двигается вдоль воображаемой линии, направленной от центра источника до положения частицы. Когда источник плоский (Rectangle или Circle), частицы двигаются в одной плоскости (если только параметр Divergence равен нулю). Если же он объемный (Box или Sphere) и у значков есть высота, то частицы двигаются от него во всех трех измерениях.
  3. Icon Arrow Out – каждая частица двигается вдоль воображаемой линии от точки нормали к стрелке значка до положения частицы. Эта линия перпендикулярна вектору стрелки. Частицы двигаются по плоскости, если источник плоский и отклонение (Divergence) равно нулю и вдоль радиусов цилиндра, если он объемный.
  4. Random 3D – частицы двигаются во всех направлениях от центра. На этот вариант влияет параметр Uniqueness (уникальность).
  5. Random Horizontal – каждая частица двигается в случайном горизонтальном направлении, т.е. параллельно мировой плоскости XY. На этот вариант влияет параметр Uniqueness (уникальность).
  6. Inherit Previous – использует текущее направление движения. Если вы выберете этот вариант, не двигая частицы никаким оператором заранее, то скорость и направление будут неизвестны и частицы встанут на месте.

Reverse (наоборот) – флажок, во включенном состоянии меняет направление скорости частиц на противоположное. Отключен по умолчанию. Включение флажка эквивалентно умножению значения параметра Speed на -1. Флажок недоступен, если для направления выбран вариант Random 3D или Random Horizontal.

Divergence (расхождение) – угол конуса распыления в градусах. Значения больше нуля распыляют частицы в стороны от направления движения. Диапазон от 0 до 180. Этот параметр можно анимировать. Параметр недоступен, если для направления выбран вариант Random 3D.

Uniqueness (уникальность) – инструменты группы задают случайность для отклонения и вариантов направления Random 3D и Random Horizontal. Параметр Seed определяет величину случайности, а кнопка New запускает для системы новый расчет на основе этой новой случайности.


Rotation (Поворот)

Оператор поворота позволяет задать и анимировать ориентацию частиц с дополнительными случайными отклонениями во время события. В данном уроке я не меняю никакие значения по умолчанию для этого оператора и поэтому описывать его не буду, тем более параметры Rotation во многом совпадают с параметрами следующего оператора Spin.


Spin (Вращение)

Вращение сообщает частицам угловую скорость с дополнительными случайными отклонениями во время события. Вращение применяется к каждой частице один раз в текущем событии, за исключением варианта Speed Space Hollow. Но, тем не менее, параметры вращения можно анимировать.

Spin Rate (скорость вращения) – скорость вращения, измеряемая в градусах на секунду. Т.е. 360 означает, что частица за секунду сделает один полный оборот.

Variation (отклонение) – максимальное отклонение в градусах на секунду, на которое скорость вращения может меняться. Реальное случайное значение вычисляется единожды для каждой частицы.

Spin Axis (ось вращения) – группа параметров, определяющих конкретные или случайные оси вращения с дополнительными случайными отклонениями. По умолчанию выбран вариант Random 3D. Варианты:

  1. Random 3D – каждая частица будет вращаться вокруг произвольной случайной 3D-оси.
  2. World Space – ось вращения задается в системе мировых координат параметрами X/Y/Z.
  3. Particle Space – ось вращения задается в системе локальных координат для каждой частицы параметрами X/Y/Z.
  4. Speed Space – система координат для вращения вычисляется исходя из направления движения частицы сразу после начала события. Ось X совпадает с вектором направления движения, а ось Z ей перпендикулярна и направлена вверх таким образом, чтобы максимально приближаться к мировой оси Z. Параметры X/Y/Z задают конкретную ось в этом пространстве координат.
  5. Speed Space Hollow – система координат для вращения вычисляется на основе направления движения частицы во врем всего события. Этот вариант позволяет частицам, меняющим во время движения свое направление, сохранять ось вращения всегда точно выровненной относительно движения. Ось X совпадает с вектором направления движения, а ось Z ей перпендикулярна и направлена вверх таким образом, чтобы максимально приближаться к мировой оси Z. Параметры X/Y/Z задают конкретную ось в этом пространстве координат.

X/Y/Z – параметры для задания конкретной оси вращения. Недоступны при варианте Random 3D. По умолчанию равны 0,0,1. Диапазон для каждого от -1.0 до 1.0. Чтобы ось вращения совпадала с единичным вектором данной системы координат, задайте соответствующей параметр равным любому числу больше нуля, и поставьте нули в двух остальных. Отрицательные значения переворачивают ось и меняют направление вращения на противоположное. Числовые значения начинают работать, когда вы задаете ненулевые значения более чем для одной оси, в этом случае эффект от них будет суммироваться. Например, если вам нужно, чтобы ось вращения была посередине между положительными направлениями осей X и Y, задайте для них одинаковые положительные величины. Какие конкретно не имеет значения. Аналогично, если хотите, чтобы угол между осью вращения и осью X был равен 30 градусам в направлении оси Y (одна треть от угла между X и Y), задайте величину для Y в два раза больше, чем для X. Например, X=0.2 и Y=0.4 или X=0.5 и Y=1.0.

Divergence (расхождение) – задает предел расхождения для осей вращения в градусах. Реальное расхождение равно случайной величине из заданного диапазона. Общий диапазон от 0 до 180. По умолчанию значение равно нулю. Параметр недоступен для варианта Random 3D.

Uniqueness – уникальность влияет на отклонение скорости вращения, ось вращения для варианта Random 3D и на расхождение для остальных вариантов.


Force (Сила)

Оператор позволяет влиять на движение частиц посредством одной или нескольких пространственных деформаций (Space Warps) из категории Forces. Я использую Gravity для эффекта “сдувания” частиц. В списке отображаются названия используемых пространственных деформаций. Если деформацию удалить в сцене, на ее месте в списке оператора Force будет слово deleted. Примечание: Particle Flow применяет силы к движению частиц в таком же порядке, в каком деформации находятся в списке. Эффект суммируется сверху вниз. Сначала к движению частиц применяется верхняя деформация, потом следующая применяется к результату действия верхней и так далее вниз по списку. Меняя порядок деформаций можно повлиять на финальный результат. Описание кнопок:
Add – нажмите и выберите в сцене пространственную деформацию, чтобы добавить ее в конец списка.
By List – нажмите и из появившегося окна Select Force Space Warps выберите нужные деформации. Они занесутся в список в том же порядке, в каком были в окне.
Remove – выделите деформацию в списке и нажмите кнопку, чтобы ее удалить. Любая удаляемая деформация остается в сцене.

Поскольку у меня в сцене только одна деформация, радиокнопки группы Force Field Overlapping (пересечение силовых полей) неактивны. А вообще они нужны для определения способа влияния нескольких деформаций на один объем частиц. Если включен Additive, то деформации суммируются в соответствии с их средними силами. Если включен Maximum, на частицы влияет только та деформация, сила которой максимальна. Например, у вас в сцене есть две примененные к частицам деформации Wind (ветер) и Gravity (сила тяжести), у которых параметр Strength равен 1.5 и 1.0 соответственно. Если выбрать Additive, то влияние Wind на частицы будет примерно на 50% больше влияния Gravity. Но если выбрать Maximum, то на частицы будет влиять только Wind.

Influence (влияние) – задает мультипликатор, с которым силы пространственных деформаций применяются к частицам в процентах. По умолчанию 1000.0. Отрицательные значения параметра разворачивают силы в противоположную сторону.

Группа параметров Offset Influence задает синхронизацию для времени анимации. Пригодятся, если вы анимируете какие-либо параметры операторов Particle Flow. Я в данном уроке этого не делаю. Синхронизировать можно по:

  1. Absolute Time (абсолютному времени) – любые ключи для параметров применяются в те же самые кадры в которых они были поставлены.
  2. Particle Age (возрасту частицы) – любые ключи для параметров применяются в кадры, соответствующие времени жизни каждой частицы. Т.е., допустим, параметр меняется в 20-ом кадре и каждая частица, прожив 20 кадров, попадет под его новое значение.
  3. Event Duration (продолжительности события) – любые ключи для параметров применяются к каждой частице отсчитывая время с момента ее вхождения в событие. Например, частица живет в событии 20 кадров и если ключ для параметра установлен на 20, то она именно в это время попадет под его новое значение.


Display (отображение)

Оператор позволяет вам определять способ отображения частиц в окнах проекций. По умолчанию режим Ticks (отметка – выглядит как знак +) самый простой и быстрый способ отображения, полезный для анимации с участием большого количества частиц. Его противоположность – режим Geometry (геометрия, который у меня кстати и используется), который отображает реальную геометрию частиц. Дополнительно оператор Display предлагает ряд простых форм частиц для быстрого обзора при тестировании анимации и возможность легко различать частицы в разных событиях. Это я вам скажу очень удобно, когда можно по цвету (щелкните на цветном квадратике, чтобы поменять цвет частиц для данного события) или форме легко отличить частицы в разных событиях, особенно если частица по каким-то причинам не может попасть из одного события в другое. Еще в операторе есть возможность управлять числом видимых в окнах проекций частиц, задавая процентное соотношение относительно их общего числа. Теперь обо всем этом по порядку:

Type (тип) – список с вариантами отображения частиц в окнах проекций. Двухразмерные маркеры показывают только положение частиц. Geometry показывает как частицы будут выглядеть при визуализации, в трех измерениях. Lines (линии) показывают скорость и направление движения. Bounding Boxes (габаритные контейнеры) демонстрируют масштаб и ориентацию. Варианты (в скобках размерность и перевод):

  1. None (нет) – частицы не отображаются в окнах проекций
  2. Dots (0D точки) – каждая частица отображается как один пиксель
  3. Ticks (2D отметки) – каждая частица отображается как символ +
  4. Circles (2D круги) – каждая частица отображается как маленький круг
  5. Lines (1D линии) – каждая частица отображается как линия толщиной в пиксель. Длина линии показывает скорость частицы, а ее наклон – направление движения. Этот вариант очень полезен и дает быстрое и точное представление о движении частиц при экспериментах с операторами Speed. Примечание: когда используется экранный драйвер OpenGL, медленно двигающиеся частицы могут не отображаться в окнах проекции при выборе этого варианта. Если такое произошло, то чтобы увидеть все частицы добавьте в это же событие второй оператор Display и поставьте для него тип Dots.
  6. Bounding Boxes (3D габаритные контейнеры) – вместо каждой частицы отображается ее габаритный контейнер. Дает хорошее понимание финальной анимации малой ценой вычислительных ресурсов.
  7. Geometry (3D геометрия) – отображается реальная геометрия каждой частицы. Наилучшее качество отображения и соответственно самая медленная скорость вычислений.
  8. Diamonds (2D ромбы) – каждая частица отображается в виде ромба.
  9. Boxes (2D квадраты) - каждая частица отображается в виде маленького квадрата.
  10. Asterisks (2D звездочки) - каждая частица отображается в виде символа (*).
  11. Triangles (2D треугольники) - каждая частица отображается в виде маленького треугольника.

Visible % (видимость) – определяет процент отображаемых в окнах проекций частиц от общего их количества. Параметр позволяет увеличить скорость прорисовки частиц за счет уменьшения количества видимых.

Show Particle Ids (показывать индексы частиц) – если флажок включен, уникальный номер индекса для каждой частицы становится видимым в окнах проекций. Частицы нумеруются в порядке рождения, начиная с 1 для перворожденной частицы.

Selected – список с вариантами отображения выделенных частиц. Варианты те же самые, как и в списке Type.

Ну вот почти все с первым событием, операторы-квадратики в нем закончились, остался только желтый ромбик Age Test, но перед его описанием показываю, что же произошло с системой частиц в первом событии:


Кадры слева направо: 5-й, 10-й и 15-й соответственно


Переход к следующему событию через Age Test (тест возраста)

Тест возраста, включенный в систему частиц, заставляет ее проверять, сколько времени прошло с начала анимации или с рождения частицы, или сколько времени частица находится в текущем событии и направлять частицы по веткам событий в соответствии с результатом проверки. Список вверху интерфейса позволяет выбрать один из типов возраста на тестирование:

  1. Absolute Age (абсолютный возраст) – тестирует номер текущего кадра анимации
  2. Particle Age (возраст частицы) – тестирует текущий возраст каждой частицы в кадрах. Этот тип выбирается по умолчанию.
  3. Event Age (возраст события) – тестирует продолжительность текущего события в кадрах.

Test True if Particle Value – группа радиокнопок позволяет вам установить, в какой момент тест направит частицы в следующее событие если проверка по возрасту даст положительный или отрицательный результат. По умолчанию – Is Greater Than Test Value (если больше, чем значение теста). Т.е. по умолчанию Age Test возвращает истину (True) если тестируемый возраст превысит значение параметра Test Value (значение теста), но вы также можете выбрать вариант Is Less Than Test Value (если меньше, чем значение теста). Например, если вы используйте тип возраста Absolute Age, установите параметр Test Value равным 60, а параметр Variation равным нулю, и выберете вариант Is Less Than Test Value, то частицы будут проходить в следующее событие только до 60-го кадра. После 60-го кадра все оставшиеся частицы останутся в текущем событии до тех пор, пока другой тест не возвратит истину.

Test Value (значение теста) – определенный номер кадра, возраст частицы (в кадрах) или продолжительность события (в кадрах) для тестирования. По умолчанию 30. Анимировать этот параметр нельзя.

Variation (отклонение) – количество кадров, на которое тестируемая величина может расходиться со значением Test Value. По умолчанию 5. Анимировать этот параметр нельзя. Чтобы получить реальное значение на тест для каждой частицы, система умножает значение Variation на случайное число из диапазона от -1.0 до 1.0 и складывает результат со значением Test Value. Например, если Test Value=300 и Variation=10, то тестируемая величина для каждой частицы будет случайным число из диапазона от 290 до 310.

Subframe Sampling (межкадровое разбиение) – включение этого параметра помогает избегать прерывистости системы частиц в моменты переходов от одних событий к другим путем тестирования времени в более высоком межкадровом разбиении (т.е. во времени каждого кадра) по сравнению с использованием относительно грубого разрешения по кадрам. Параметр включен по умолчанию. Прерывистость это эффект создания отдельных наборов частиц, двигающихся рывками по сравнению с постоянным потоком. Если параметр отключить, тест будет выполняться точно в моменты кадров.


СОБЫТИЕ ВТОРОЕ: Перемещение

Событие Перемещение двигает частицы из пункта старта в пункт назначения. Параметры операторов я подобрал таким образом, чтобы перед созданием целевого объекта частицы примерно равномерным облаком окружили то место, где он должен появиться. В событии используется только один новый оператор Keep Apart и новый тест Find Target, остальные операторы подробно не описываю, т.к. сделал это для предыдущего событии. В них я уменьшил влияние силы тяжести, увеличил скорость, расхождение и скорость вращения также увеличил по сравнению с предыдущим событием, чтобы получить нужную анимацию облака из осколков частиц. Все параметры подбираются вручную и это, кстати, довольно долгий процесс, который приходится повторять и шлифовать каждый раз для разных положений и объектов пока не понравится, ну впрочем как всегда.



Keep Apart (держать по отдельности)

Оператор из семейства Speed, позволяет применить к частицам силы притяжения или отталкивания, чтобы держать их на некотором расстоянии от соседних частиц, предотвращая или уменьшая проникновение частиц друг в друга. Также вы можете использовать отрицательную силу, чтобы частицы при этом не слишком “разбегались” в стороны. Для этих целей оператором Keep Apart контролируются скорость и ускорение частиц. Примечание: Keep Apart не использует геометрию частиц, вместо этого он создает сферическое силовое поле с центром в опорной точке для каждой частицы. Можно настроить размер силового поля изменив размер частицы. Во многих случаях настройки по умолчанию не устраняют пересечения частиц. Чтобы увеличить разделение следует использовать большие величины для параметров Force (сила) и Accel Limit (предел ускорения), выбрать вариант Relative To Particle Size (относительно размеров частицы) и увеличить значение параметра Core % (ядро).

Force – величина силы, применяемой к частицам. Положительные значения направляют частицы в стороны друг от друга, отрицательные сдвигают плотнее. По умолчанию 100.

Accel(eration) Limit – предел ускорения, во включенном состоянии позволяет вам установить максимальный предел ускорения, который может повлиять на движение частиц. Если параметр отключен, система использует любое подходящее ускорение. Параметр включен по умолчанию, величина 1000. Подсказка: для плавного движения нужно использовать низкие значения параметра, а для точности высокие, например, в случаях, когда частицы должны ударять цель малого размера. Вы можете анимировать этот параметр (используйте синхронизацию Sync By > Event Duration), чтобы задать различные подходящие величины в зависимости от требуемых результатов.

Speed Limit – предел скорости, во включенном состоянии позволяет установить максимальный предел скорости, который может повлиять на движение частиц. Если параметр отключен, система использует любую подходящую скорость. По умолчанию отключен, значение по умолчанию 600.

Группа Range – эти параметры устанавливают объем и спад объема в пределах которых работает сила оператора. Можно задать как абсолютные размеры, так и относительные, установив процентную зависимость от размера частиц. По умолчанию Absolute Size.

Absolute Size (абсолютный размер) – выберите этот вариант, чтобы установить фиксированные абсолютные величины для радиуса ядра и зоны спада в системных единицах.

Core Radius (радиус ядра) – расстояние от опорной точки каждой частицы, в системных единицах. Внутри сферы с этим радиусом и центром в опорной точке действие силы максимально. По умолчанию 10.

Falloff Zone (зона спада) – расстояние за радиусом в системных единицах, в пределах которого сила уменьшается от максимального значения до нуля. По умолчанию 10.

Relative To Particle Size (относительно размеров частицы) – выберите этот вариант, чтобы установить радиус ядра и зону спада как процентные отношения от радиуса каждой частицы. Этот радиус определяется измерением расстояния от опорной точки до самого дальнего угла габаритного контейнера (ха, также как и у меня в алгоритме).

Core % (ядро) – расстояние от центра каждой частицы как процент от ее радиуса, в пределах которого действие силы максимально. По умолчанию 200.

Falloff % (спад) – расстояние за ядром как процент от радиуса, в пределах которого сила уменьшается от максимального значения до нуля. По умолчанию 100.

Variation % (отклонение) – значение, на которое расстояния могут случайно изменяться, как процент от соответствующих величин. Системой используется одинаковое случайное отклонение для величин ядра и спада в независимости от того, абсолютные они или относительные. По умолчанию 0. Пример: если вы выберите Absolute Size и установите 40 для Core Radius, 20 для Falloff Zone и 50 % для Variation, то тогда реальный радиус каждой частицы будет являться случайным числом из диапазона от 20 до 60, а зона спада – случайным числом в диапазоне от 10 до 30.

Группа Scope – эти настройки позволяют вам держать частицы на дистанции от других частиц в различных событиях и даже системах, без влияния на поведение последних. По умолчанию Current Event (текущее событие).

Current Event (текущее событие) – оператор Keep Apart работает только в текущем событии. Если используется глобально, держит частицы в каждом событии текущего потока отдельно друг от друга, но не отдельно от частиц других событий в этом потоке.

Current Particle System (текущая система частиц) – держит частицы текущего события отдельно друг от друга и от всех остальных частиц в текущем потоке (для справки у меня в сцене один единственный поток, т.к. один объект PF Source). Частицы, не состоящие в текущем событии, не попадают под влияние оператора. Когда используется глобально то держит по отдельности все частицы текущего потока.

Selected Events (выделенные события) – держит частицы текущего события отдельно от частиц событий, подсвеченных в нижнем списке. Частицы, не вошедшие в текущее событие, не попадают под влияние оператора. Когда используется глобально, держит все частицы текущего потока отдельно от частиц подсвеченных в списке событий, но действует только на частицы из текущего потока. Не забудьте щелкнуть на событиях в списке, чтобы их подсветить.

Selected Particle Systems (выделенные системы частиц) – держит частицы текущего события отдельно от всех частиц подсвеченных в списке потоков. Частицы, не вошедшие в текущее событие, не попадают под влияние оператора. Когда используется глобально, держит все частицы текущего потока отдельно от частиц подсвеченных в списке потоков, но влияет только на частицы текущего потока. Не забудьте щелкнуть на потоках в списке, чтобы их подсветить.

Так-с, в событии Перемещение остался только желтый ромбик теста Find Target и надо показать, как выглядят частицы на этом этапе.


Кадры слева направо: 50-й, 100-й и 150-й соответственно


Переход к следующему событию через Find Target (тест поиска цели)

По умолчанию оператор Find Target направляет частицы в сторону одной или нескольких заданных целей. После достижения цели, частицы получают право на переход к другому событию. Вы можете задать специальную скорость или время движения частицы до цели, а также определить место на цели, в которое частица будет направлена. А вообще можно использовать тест Find Target как простую проверку на близость: после сближения частицы до определенной дистанции с целью, она становится способной перейти в следующее событие. Когда вы добавляете Find Target в систему частиц в окне Particle View, то в начале координат сцены появляется специальный значок теста Find Target. Этот значок можно использовать как цель (я делаю это в третьем событии – значок стоит на месте объекта собирания осколков) либо в качестве целей можно использовать один или несколько объектов-сеток mesh objects, это я использую в данном событии, мои частицы бегут за объектом $Destination. Чтобы отобразить параметры теста на панели Modify, нужно выделить значок теста. Если удалить значок из сцены, тест из Particle View также удалится.

Control By (способ управления) – список вверху панели позволяет выбрать один из способов управления частицами, а именно направлять частицы к цели, задавая для них скорость и ускорение, либо задавая время движения, которое им понадобится, чтобы достичь цели. И, в качестве альтернативы, выбирая вариант No Control можно анализировать расстояние от цели до частиц. Варианты:

  1. Control By Speed (управление по скорости) – этот тип управления позволяет настраивать параметры скорости и ускорения частиц во время движения до цели.
  2. Control By Time (управление по времени) - этот тип управления позволяет настраивать параметры времени движения частиц до цели.
  3. No Control (без управления) – при этом варианте Find Target функционирует только как тест близости. Как только частица приближается к цели на определенное расстояние, она получает право перейти в следующее событие. При выборе No Control тест Find Target не будет влиять на скорость и направление движения частиц.

Группа Test True If Distance To: (условие истинно, если расстояние до:) – параметры данной группы позволяют вам выбрать точку на цели, расстояние до которой тест будет анализировать и задать это расстояние. Можно выбрать Target Pivot (цель – точка опоры) или Target Point (цель - точка) и задать расстояние в параметре Is Less Than: (меньше чем:).

Target Pivot – измеряет расстояние между частицей и точкой опоры цели. Если частицы направлены в стороны от цели, а значение параметра Is Less Than мало, то такое состояние может продолжаться бесконечно, и тест не сработает.

Target Point – измеряет расстояние между частицей и специальной точкой на цели.

Is Less Than – когда расстояние от частиц до точки опоры или до специальной точки на цели меньше, чем это расстояние, то такие частицы проходят тест и получают право перейти к следующему событию. Параметр измеряется в системных единицах. Примечание: если вы присвоите ноль параметру Is Less Than, то никакие частицы не пройдут тест с истиной. Такое может понадобиться, например, для анимации пчел, которые кружатся вокруг цветка, но не садятся на него. В этом случае можно задать небольшую величину параметру Accel Limit, чтобы пчелы не кружились слишком близко от цветка.

Группа Control By Speed (управление по скорости) – параметры этой группы используются для настройки скорости и ускорения частиц при выборе способов управления Control By Speed или Speed Then Time (скорость потом время). Группа доступна только при выборе управления Control By Speed (управление по скорости). Я ее в уроке не использую, но расскажу все равно. Параметры группы:

Use Cruise Speed (использовать скорость хода) – когда флажок включен, система предоставляет вам явный контроль над скоростью частицы и отклонением скорости. Когда флажок выключен – система вычисляет скорость частицы автоматически, используя значение параметра Accel Limit. По умолчанию флажок включен.

Speed (скорость) – скорость частицы в системных единицах на секунду. По умолчанию 300.

Variation (отклонение) – отклонение скорости частицы от величины скорости. Работает аналогично описанному ранее параметру Variation для оператора Speed.

Accel Limit (предел ускорения) – устанавливает предел ускорения. Это значение задает инерцию и скорость частиц. По умолчанию 1000. Значение по умолчанию для предела ускорения соотносится со значением скорости по умолчанию, поэтому рекомендуется менять пропорционально значение Accel Limit при изменении значения Speed. Подсказка: для плавного движения нужно использовать низкие значения параметра, а для точности высокие, например, в случаях, когда частицы должны ударять цель малого размера. Вы можете анимировать этот параметр (используйте синхронизацию Sync By > Event Duration) чтобы задать различные подходящие величины в зависимости от требуемых результатов.

Ease In % (замедлять на) – параметр задает темп, с которым частица будет снижать свою скорость по мере приближения к точке цели. Система вычисляет конечную скорость по следующей формуле: (100% - Ease In) * Speed. Следовательно, если значение Ease In равно 100%, то частицы достигнут цели с нулевой скоростью, а если Ease In равно 0%, то они вообще не будут замедляться на протяжении всего пути до цели. При средних значениях между нулем и сотней скорость вычисляется в соответствии с расстоянием до точки цели, как линейная интерполяция между начальной скоростью хода и конечной скоростью. Когда частица поступает в событие, расстояние до точки цели вычисляется и используется в дальнейшем для этой интерполяции. По умолчанию 0%.

Sync By – синхронизация анимации, работает аналогично одноименному и описанному ранее параметру теста Age Test.

Группа Control By Time (управление по времени) – позволяет вам задать время, за которое частицы должны достичь цели. Группа недоступна, если выбрано управление Control By Speed. Параметры группы:

Timing (синхронизация по времени) – определяет вариант, который система будет использовать при вычислении времени, задаваемом параметрами Time и Variation. Возможные варианты:

  1. Absolute Time (абсолютное время) – кадр относительно общего времени системы. Каждая частица достигнет своей цели в кадре с номером, заданном параметром Time.
  2. Particle Age (возраст частицы) – кадр относительно времени, прошедшего с момента рождения частицы. Каждая частица достигнет своей цели, когда ее возраст достигнет значения, указанного в параметре Time.
  3. Event Duration (продолжительность события) – кадр относительно времени, прошедшего с момента вхождения частицы в текущее событие. Каждая частица достигнет своей цели, когда с этого момента пройдет столько кадров, сколько указано в параметре Time.

Time (время) – число кадров, которое потребуется частицам, чтобы достичь цели. По умолчанию 60.

Variation (отклонение) – число кадров, на которое реальное значение Time может отклониться от указанного значения. По умолчанию 5. Чтобы получить реальное время достижения цели для каждой частицы, система умножает значение Variation на случайное число из диапазона от -1.0 до 1.0, а результат прибавляет к значению Time. Например, если Time=60 и Variation=20, то время достижения цели для каждой частицы будет в интервале от 20 до 80 кадров.

Subframe Sampling (межкадровое разбиение) – включено по умолчанию, что это и зачем нужно я уже писал.

Use Docking Speed (использовать скорость стыковки) – флажок позволяет задать скорость частицы когда она достигает цели. Может потребоваться, чтобы частица “стыковалась” с целью из заданного положения и с определенной скоростью. Когда этот флажок выключен, система вычисляет путь стыковки как кратчайшее расстояние с минимальным ускорением вдоль пути. Когда флажок включен, система вычисляет конечную скорость частицы перед достижением цели (т.е. скорость стыковки) используя параметры Speed и Variation. Поэтому если нужно плавное приземление, установите параметр Speed равным нулю. Флажок по умолчанию выключен.

Speed (скорость) – стыковочная скорость в системных единицах на секунду. По умолчанию 100.

Variation (отклонение) – величина, на которую реальная скорость частицы может случайно изменяться. По умолчанию 0.

Группа Target (цель) – по умолчанию система использует в качестве цели значок Find Target, но вместо него можно задать и другую цель с помощью инструментов этой группы.

Icon (значок) – использовать в качестве цели значок Find Target. Каждый тест Find Target использует свой собственный значок. Даже если вы не используете его в качестве цели, он все равно влияет на поведение частиц, если установлен один из типов стыковки (docking type): Parallel, Spherical или Cylindrical.

Mesh Objects (объекты-сетки) – используйте в качестве целей один или несколько Mesh-объектов сцены. Если включить эту радиокнопку, становятся доступными список и управляющие кнопки. Если в качестве цели вы используйте более одного объекта, цель для каждой частицы определяется вариантом из выпадающего списка Object.

Add – нажмите кнопку и выберите объект в сцене, чтобы сделать его целью и занести в список.

By List – нажмите кнопку и в появившемся окне Select Target Objects выберите один или несколько объектов-сеток для добавления в список. В окне отображаются существующие в сцене объекты подходящего типа.

Remove – выделите объект в списке и нажмите на эту кнопку, чтобы его из списка удалить. Любой удаляемый объект остается в сцене.

Sync By (синхронизировать по) – выберите кадр анимации, когда частицы посылаются в направлении цели и включены флажки Animated Shape (анимированная форма) или Follow Target Animation (следовать анимации цели). Варианты:

  1. Absolute Time (абсолютное время) – анимация, наследуемая частицами от целевого объекта применяется к ним немедленно.
  2. Particle Age (возраст частицы) – наследуемая анимация применяется к частицам в соответствующих кадрах жизни каждой частицы.
  3. Event Duration (продолжительность события) – наследуемая анимация применяется к частицам в соответствующих кадрах, отсчитываемых с момента поступления в событие каждой частицы.

Animated Shape (анимированная форма) – включите флажок, если требуется, чтобы частицы следовали к поверхности объекта, форма которого меняется посредством масштабирования, морфинга или модификаторов. Для этого требуется больше вычислений, поскольку цель должна обновляться на каждом шаге проверки.

Follow Target Animation (следовать анимации цели) – включите флажок, чтобы частицы следовали за двигающейся целью, т.е. целью с анимированным положением. Для этого требуется больше вычислений, поскольку цель должна обновляться на каждом шаге проверки.

Point (точка) – позволяет вам определить место на объекте, в которое частица должна “приземлиться”. Варианты:

  1. Random (случайно) – каждая частица нацеливается в случайную точку на объекте.
  2. Closest Surface (ближайшая поверхность) – каждая частица нацеливается в ближайшую точку на поверхности объекта(ов) цели.
  3. By Script Vector (по вектору, задаваемому скриптом) – точки цели определяются скриптом, находящимся в Script Operator (оператор скрипта), который задает точные координаты в канале particleVector. Оператор скрипта может находиться где угодно в событии, но выше чем тест Find Target. Забегая вперед, скажу, что именно этот вариант я использую в третьем событии, когда из осколков первого объекта собирается второй. Примечание: если вы используете вариант By Script Vector для указания точных целевых позиций, например координат вершин, установите тип целевого объекта Icon. Если поставите Mesh, то координаты, хранящиеся в скрипте, будут считаться относительно позиции объекта-сетки. Последний вариант полезен для посадки частиц с помощью скрипта на поверхность двигающегося объекта.

Object (объект) – когда есть несколько объектов-целей, данный вариант позволяет вам указать, каким образом система должна выбирать из них конкретную. Вариант доступен только когда число целей больше одной. Выбирайте один из пунктов:

  1. Random (случайно) – для каждой частицы система случайным образом выбирает целевой объект.
  2. Closest Pivot (ближайшая опорная точка) – для каждой частицы система выбирает тот объект в качестве цели, опорная точка которого находится ближе к частице, чем опорные точки всех остальных потенциальных объектов-целей.
  3. Closest Surface (ближайшая поверхность) – для каждой частицы система выбирает тот объект в качестве цели, поверхность которого находится ближе к частице, чем поверхности всех остальных потенциальных объектов-целей.
  4. Least Deviation (минимальное отклонение) - для каждой частицы система выбирает тот объект в качестве цели, для достижения которого частице требуется минимальное изменение текущего направления движения по сравнению с другими объектами-целями.
  5. By Script Integer (по скрипту индексов) – для каждой частицы выбор целевого объекта определяет оператор скрипта, который устанавливает индексы объектов. Индекс соответствует номеру объекта в списке целей (сверху вниз начиная с нуля).

Lock On Target Object (прикрепить к целевому объекту) – когда флажок включен, система определяет целевой объект для каждой частицы один единственный раз: в момент поступления частицы в событие. После этого частица “прикрепляется” к своей цели. Когда флажок выключен, система может постоянно пересчитывать целевой объект для каждой частицы. Флажок доступен, когда задано несколько целевых объектов. Например, если вы указываете частицам нацеливаться на ближайшую поверхность (Closest Surface) в соответствии с анимацией этой поверхности и движением частицы, то определение ближайшей поверхности будет происходить постоянно. Это приведет к тому, что частица может сменить целевой объект в зависимости от ситуации. Примечание: каждый раз, когда тест Find Target устанавливает целевой объект, он “прикрепляется” к определенной точке на этом объекте. Точка может измениться, только если изменится объект. Поэтому, когда целевой объект один, точка закрепления всегда остается неизменной относительно объекта. Это происходит ввиду того, что если целевой объект или его поверхность анимированы и включен флажок Follow Target Animation либо флажок Animated Shape, абсолютные координаты целевой точки могут измениться. Когда флажок Lock On Target Object отключен, требуется больше вычислений, так как системе необходимо рассчитывать оптимальную целевую точку для каждой частицы в каждом кадре.

Группа Docking Direction (направление стыковки).
Параметры Docking Type (тип стыковки) позволяют вам определить направление, в котором частицы будут сближаться с целью. Варианты:

  1. None Specified (не определен) – нет ограничений стыковки. Частицы приближаются к своим целям по самому подходящему пути на основе назначенных им параметров и текущих свойств частицы.
  2. Along Icon Arrow (вдоль стрелки значка) – финальное направление движения совпадает с направлением стрелки значка Find Target.
  3. Icon Spherical (сферический значок) – финальное движение направлено к центру значка Target Icon
  4. Icon Cylindrical (цилиндрический значок) - финальное движение направлено к центральной оси цилиндрического значка Target Icon, формируя таким образом цилиндрическое поле с осью значка в качестве центральной линии.
  5. Surface Normals (нормали поверхности) – каждая частица достигает своей цели по направлению, перпендикулярному к поверхности в точке стыковки.

Примечание: выбор второго, третьего или четвертого варианта ведет к появлению стрелок на значке теста, указывающих, в каком направлении частицы последуют для стыковки. Вы можете это направление изменить, повернув значок. Работает всегда, даже если в качестве целей выбраны Mesh-объекты. При выборе пятого варианта на значке также появятся стрелки, сигнализирующие о том, что для направления стыковки будут использоваться нормали поверхностей. Реальные направления зависят от поверхности цели. Вариант работает всегда, даже если в качестве целей выбраны Mesh-объекты.

Distance (дистанция) – расстояние от частицы до цели, при достижении которого частица начинает процесс стыковки. В процесс включаются направление и скорость стыковки (в случае, когда используется контроль по времени – Control By Time).

Icon Size (размер значка) – устанавливает размер значка Find Target. Размер влияет на поведение частиц, когда значок используется в качестве цели. Обращаю ваше внимание – я в уроке это использую. Значок достаточно большой и расположен внутри объекта $TDestination01.

Color Coordinated (цветные координаты) – когда флажок включен, значок Find Target использует цвет события, содержащего этот тест. Цвет задается локальным оператором Display, если таковой существует. Это работает, даже когда оператор Display выключен. Когда флажок выключен, тест Find Target использует цвет гизмо-оболочек для тестов по умолчанию, как определено в меню Customize User Interface > Colors > Particle Flow. По умолчанию флажок включен. Включенный флажок позволяет легко найти значок Find Target, поскольку частицы в событии используют этот же цвет.


СОБЫТИЕ ТРЕТЬЕ: Собирание

Наконец-то я до него добрался. Из новых операторов там к счастью только один – Script Operator, который используется тестом Find Target, чтобы направить осколки в нужные позиции и заодно этот скрипт меняет форму осколков, делая из них осколки целевого объекта. В самом начале урока я говорил, что хочу сделать это преобразование как можно более незаметно, так вот, незаметность достигается за счет того, что осколки крутятся на протяжении всей жизни, а подмена происходит в момент стыковки, когда они быстро устремляются на свои конечные позиции. И глаз не замечает замену, все легко и просто. Вообщем быстрое движение и короткая траектория – залог успеха в моем морфинге.


О скриптах для Particle Flow. Все подобные скрипты, как правило, состоят из четырех небольших блоков, pCont это контейнер (particeContainer) - имя переменной, в которой сохраняется текущий поток частиц, с которым работает скрипт, имя может быть любым:

  1. on ChannelsUsed pCont do () – в этом блоке перечисляются каналы, которые будет использовать скрипт. Каналов очень много, тут требуется нужные.
  2. on Init pCont do () – в этом блоке происходят все предварительные и подготовительные действия, инициализация переменных, начальные расчеты и т.п.
  3. on Proceed pCont do () – блок выполняется каждый раз, когда выполняется действие. Т.е. каждый раз, когда проверяется условие теста например, или каждый раз, при рождении частицы, вообщем можно считать, что он постоянно выполняется. Поэтому осторожнее с тяжелыми расчетами в данном блоке. Всю предвариловку надо делать заранее, а в этом блоке происходят все действия и любые изменения непосредственно с частицами.
  4. on Release pCont do () – этот блок запускается после того, как отработал блок Proceed, можно использовать для удаления остатков работы, временных переменных и т.п., обычно он пустой.

Вот код скрипта, используемого в Script Operator третьего события. Меняю геометрию частиц на правильную, и ставлю их на места осколков объекта $TDestination. Все очень просто.

on ChannelsUsed pCont do --ВНУТРИ ЭТОГО БЛОКА ПЕРЕЧИСЛЯЮТСЯ КАНАЛЫ, КОТОРЫЕ БУДЕТ ИСПОЛЬЗОВАТЬ СКРИПТ
(
pCont.useVector = true --КАНАЛ ВЕКТОРОВ ДЛЯ СТЫКОВКИ
pCont.useTM = true --КАНАЛ ТРАНСФОРМАЦИЙ, ЧТОБЫ ПОСТАВИТЬ ОСКОЛКИ В ТОЧНОЕ ПОЛОЖЕНИЕ
pCont.useShape = true --КАНАЛ ГЕМЕТРИИ ЧАСТИЦ
)


on Init pCont do --ИНИЦИЛИЗАЦИЯ НАЧАЛЬНЫХ ПЕРЕМЕННЫХ. ПРОИСХОДИТ ОДИН РАЗ ПЕРЕД ЗАПУСКОМ
(
global gr = $Chunks_Destination* -- СОБИРАЮ В МАССИВ ОБЪЕКТЫ ОСКОЛКОВ
global NumChunks=gr.count --СКОЛЬКО ВСЕГО ОСКОЛКОВ
global fragments --МАССИВ ДЛЯ ХРАНЕНИЯ ОСКОЛКОВ
fragments=#()
fragments[NumChunks]=0
for i=1 to gr.count do fragments[i]=gr[i] -- ЗАПОЛНЯЮ МАССИВ
)

on Proceed pCont do --ПОШЕЛ ПРОСЧЕТ, ВЫПОЛНЯЕТСЯ ПРИ СТЫКОВКЕ
(
    count = pCont.NumParticles() --СКОЛЬКО ЧАСТИЦ ИМЕЕТСЯ В НАЛИЧИИ
    for i in 1 to count do ( --ДЛЯ КАЖДОЙ ЧАСТИЦЫ В ЦИКЛЕ ШУРШИМ
    pCont.particleIndex = i -- НОМЕР ЧАСТИЦЫ
    vpos = fragments[i].pos --БЕРЕМ ПОЗИЦИЮ ОСКОЛКА
    pCont.particleVector = vpos --ЗАПИСЫВАЕМ ПОЗИЦИЮ В ВЕКТОР
    pCont.particleShape = fragments[i].mesh --МЕНЯЕМ ТЕКУЩУЮ ГЕОМЕТРИЮ ЧАСТИЦЫ НА ГЕОМЕТРИЮ ОСКОЛКА ИЗ МАССИВА
    pCont.particleTM=fragments[i].transform --СТАВИМ ЧАСТИЦУ В НУЖНОЕ ПОЛОЖЕНИЕ
    )
)

on Release pCont do () --ПОСЛЕ ТОГО КАК ВСЕ ГОТОВО ДЕЛАТЬ БОЛЬШЕ НЕЧЕГО.

Обращаю ваше внимание на тесты Find Target в событиях. Синхронизация идет по абсолютному времени, а между вторым и третьим тестом проходит совсем небольшой интервал – в среднем чуть больше 10 кадров. Это сделано как раз для того, чтобы обмануть глаз и быстро собрать из частиц второй объект. Эффект сдвигания осколков достигается за счет настроек стыковки в Find Target третьего события.


Вот так они и собираются. Номера кадров слева направо: 220 225 230 235 и 240

Ну, вот и все. Надеюсь, урок поможет вам создать свой красивый эффект Particle Flow. Да, и последнее: просто так скрипты в Particle Flow работать не будут. Их нужно поместить в соответствующие операторы (у меня это Birth Script и Script Operator), нажав кнопку Edit Script и заменив текст по умолчанию на текст соответствующих скриптов, а потом запустить (CTRL+E). Комментарии по скриптам находятся у них внутри. Если вместо русских кириллических букв в редакторе максскрипта вы увидите "крякозябры" - надо в файле maxroot/MXS_Editor.properties заменить значение code.page на 0 вместо -1.


Файлы урока:
  1. Скрипт “сам в себе”: script_itself_voronoi_tesselation.ms
  2. Скрипт для оператора Birth: script_birth_explode.ms
  3. Скрипт для Find Target третьего события: Script_Operator_combine.ms