Равномерные трейлы частиц
Когда возникает задача получить партиклами какие-то гладкие линии, трейлы или струи, обычно надежды возлагаются на Blobby Surface или новый Output Mesh для nParticles. У обоих есть свои достоинства и недостатки, общий из которых – как добиться ровного трейла – при минимуме использованых частиц получить максимально гладкий результат.
Первым решением наверняка будет – генерить трейлы из основных частиц обычным emitter. Как раз при таком варианте трейлы будут получаться неравномерными – в зависимости от скорости движения основной частицы-генератора участки трейла будут состоять из разного количества частиц, где-то скучкованы, где-то значительные промежутки, для устранения которых придется увеличивать радиус blobby, что в свою очередь приведет к общему утолщению трейла, лишним вычислениям и т.п. Гораздо более эффективный способ, о котором и пойдет речь в данном посте – генерить частицы трейла партикловым экспрешеном в зависимости от расстояния, которое базовая частица-генератор проходит за фрейм, и располагать их равномерно между этими двумя позициями.
Собственно, этот партикловый экспрешен для базовой частицы-генератора вполне прост – берем позицию до динамики/перемещения в этом фрейме, позицию после, и в зависимости от заданного пользователем промежутка между двумя партиклами трейла (я создал scalar float атрибут separ на шейпе частиц) создаем командой emit необходимое количество равномерно расположенных частиц между этими позициями. Более подробное объяснение получения указанных значений позиции есть в посте Before/After Runtime Expressions (упомяну, что тут я воспользовался созданием дополнительного атрибута beforePosition потому что в сцене нет коллиженов и lastPosition/worldPosition будут выдавать не те значения что нам нужны):
runtimeBeforeDynamics:
beforePosition = position;
runtimeAfterDynamics:
string $trail_pt = "trail_pt";
float $separ = separ;
vector $lastPos = beforePosition;
vector $pos = position;
vector $move = <<(($pos.x)-($lastPos.x)), (($pos.y)-($lastPos.y)),
(($pos.z)-($lastPos.z))>>;
int $num = ceil( mag( $move ) / $separ );
if( $num != 0 ) {
vector $step = $move / $num;
for( $i = 1; $i <= $num; $i++ ) {
vector $newPos = $lastPos + $step*$i;
emit -o $trail_pt -pos ($newPos.x) ($newPos.y) ($newPos.z);
}
}
Основная частица-генератор (красная) создает за собой равномерный трейл из зеленых частиц:
Сцена примера: 05_uniPtTrail_01.mb
Разумеется, если частица быстро двигается по гладкой траектории, в результате линейной интерполяции между позициями трейл будет выглядеть изломанным:
Это решается увеличением oversampling динамики (в данном случае с 1 до 2):
Сцена примера: 05_uniPtTrail_02.mb
Теперь казалось бы все как нужно и можно запускать частицы и генерить ими трейлы… Еще одна проблема становится заметна, если к самим трейлам применить какую-то динамику или рампы от возраста age. В этой сцене на trail_pt действует гравитация – некогда при создании гладкие арки склоняются к земле с какими-то разрывами (верхняя часть следующего изображения). Откуда же они берутся и как от них избавиться (нижняя часть):
Если рассмотреть более детально чем же отличаются эти разорванные группы частиц, становится ясно, что все части в каждой созданы разом в один и тот же момент времени – birthTime и как результат age у них одинаковы:
Таким образом, когда просчитывается действие динамики, созданные за один и тот же фрейм частицы получают одни и те же модификации и в данном случае распадаются жесткими группами. Решение проблемы – заодно с генерацией в экспрешене задавать значение birthTime, чтобы у каждой частицы трейла был плавный прирост, сливающий всю группу с соседними:
[...]
for( $i = 1; $i <= $num; $i++ ) {
vector $newPos = $lastPos + $step*$i;
float $life = time - (1.0/24/$num * ($num-$i));
emit -o $trail_pt -pos ($newPos.x) ($newPos.y) ($newPos.z)
-at "birthTime" -fv $life;
}
[...]
Подправив birthTime, подправляется и age. Как результат – и силы и различные рампы, управляемые возрастом частиц, гладко влияют при переходе с группы на группу:
Арки и их цвета становятся гладкими:
Сцена примера: 05_uniPtTrail_03.mb
In: FX · Tagged with: age, birthTime, blobby, emit, expression, maya, oversampling, particle attribute, particle trail, particles
on 4 September 2009 at 13:34
Permalink
Simply great and very imformative!! thanks a tons
on 24 September 2009 at 5:36
Permalink
This is great. Exactly what I was looking for too. The problem I’ve been having is creating a smooth contrail like emission from the wing tips on a plane moving at a high rate. I’ll check this out and link back if this works for my scene.
Thanks for posting this!
on 19 February 2010 at 12:31
Permalink
Hey man thanks a lot for the tut, its great information. i was wondering if you could help explain a little more what these two lines of code do and why you are using the equations that you are? I kind of understand, but I’m not completely sure yet. Thanks.
int $num = ceil( mag( $move ) / $separ );
and
float $life = time – (1.0/24/$num * ($num-$i));
on 20 February 2010 at 0:33
Permalink
The first one calculates the number of particles that needs to be generated between two positions of a master one. I find the distance between these two positions – magnitude of the vector of transformation – and divide it by user defined $separ (that’s like density of particles in the trail – a distance we want between a pair). Since this division almost never leaves a whole number, I’ve decided that one more particle is better than one less 😉 so it’s ceil here. Then in the $step variable the final distance between these particular two positions is calculated – it’s like $separ adapted for the distance in current step. As a result that makes sections of the trail a little bit uneven (some have denser particles), but I believe in the end that’s better than gaps around each master-particle position.
Second one is easier to write than to explain 🙂 Since I’ve got a bunch of particles born at the exactly same time, I want to fool them that they were born consistently in time from last frame to this frame to make forces affect them accordingly. So, I take the number of particles I generate in current step and find the time delta needed for each one depending on the current fps to evenly stretch in time – 1/24/$num (that’s actually should be 1/$fps/$num to make it more generalized). Then since I generate particles “from the past towards now”, I subtract this delta multiplied by the inverse number of the particle from current time. That creates them with birthTime gradually from previous time to current.
Hope that was understandable 🙂
on 8 April 2010 at 16:06
Permalink
Thank you very much for this high level tutorial Sagroth! Its a great contributrion and very appreciated.
About your last post, yes, it´s clearly understandable what the script does in terms of birthTime and for lifespan related ramps, this works fine. I´m using Maya2009 right now and i got the problem, that with fields (tested gravity and uniform), i still get the “chunk” drop. I guess we need to add the trick to active the field per particle a short amount of time after its creation.
on 9 April 2010 at 22:14
Permalink
You can access the magnitude of a specific field via .inputForce[#] attribute. More details in another thread here about the checking particle presence in a volume. Hence you can set the magnitude precisely for each particle when you create it, just like birthTime. That all is becoming pretty tricky though. That’s strange birthTime doesn’t work for you ’cause it does just that – modifies the magnitude of forces. Trails in my example are falling with gravity also.
on 15 October 2013 at 16:08
Permalink
Thank you! I’m going to use your script to visualize a few things. Very cool!
on 11 October 2014 at 19:44
Permalink
Any chance this can somehow work with Maya Fluids correctly?
I was hoping emission would work from these ‘substepped’ particles without having to actually sim the fluids at .2, etc…. but alas Fluids does not recognize the extra particles…
on 25 June 2015 at 8:44
Permalink
Hi there, thanks so much for this – very useful. I’m trying to make this work using a softbody to drive the emitting particles – but having no luck. the emitted particles are offset all over the place. essentially im trying to direct the movement of the trails explicitly (without fields or expressions). ideally i’d do this just by animating an emitter, but this approach doesn’t seem to work that way.
do you have any thoughts about why this wouldn’t work? I may just be missing something obvious. many thanks
on 12 January 2016 at 17:37
Permalink
Thanks helps alot,!!!!!
on 25 March 2016 at 18:51
Permalink
I can confirm that the correction for the forces being evaluated in chunks doesn’t work in Maya 2015 and 2016, legacy Particles or nParticles.
Even if you use a normal emitter moving fast, which has the birthTime set properly by default, the forces will be applied per frame, resulting in chunks :/
This is slowly driving me crazy.
on 26 March 2016 at 22:21
Permalink
Increasing old ‘Over Samples’ seems to help (nCache>Edit Oversampling or Cache Settings…), but definitely might slow things down.
on 27 March 2016 at 11:48
Permalink
You might have forgotten to explain the gradient > arrayMapper > gravity_magnitude, or perhaps thought it was irrelevant, but THAT’s the magic that gets rid of the chunks in your scene, not the birthTime alone 🙂
Please give this a read: http://unaimb.blogspot.co.uk/2016/03/fixing-maya-particle-dynamics.html
Thanks!