1. The document describes how to build a customizable particle system by breaking it down into emitters, particle groups, and individual particle instances.
2. Key properties that can be customized include position, scale, rotation, opacity, anchor points, life span, birth rate, wind, gravity, bouncing, and time controls.
3. The properties of individual particles are calculated based on inputs to the parent emitter and particle group, combined with randomization and animation over the particle's lifetime.
6. POSITION
POSITION
OVER LIFE
POSITION
AT BIRTH
SCALE
SCALE
OVER LIFE
SCALE
AT BIRTH
ROTATION
ROTATION
OVER LIFE
ROTATION
AT BIRTH
OPACITY
OPACITY
OVER LIFE
OPACITY
AT BIRTH
ANCHOR
ANCHOR
OVER LIFE
ANCHOR
AT BIRTHPOSITION
POSITION
OVER LIFE
POSITION
AT BIRTH
SCALE
SCALE
OVER LIFE
SCALE
AT BIRTH
ROTATION
ROTATION
OVER LIFE
ROTATION
AT BIRTH
OPACITY
OPACITY
OVER LIFE
OPACITY
AT BIRTH
ANCHOR
ANCHOR
OVER LIFE
ANCHOR
AT BIRTH
POSITION
POSITION
OVER LIFE
POSITION
AT BIRTH
SCALE
SCALE
OVER LIFE
SCALE
AT BIRTH
ROTATION
ROTATION
OVER LIFE
ROTATION
AT BIRTH
OPACITY
OPACITY
OVER LIFE
OPACITY
AT BIRTH
ANCHOR
ANCHOR
OVER LIFE
ANCHOR
AT BIRTH
POSITION
POSITION
OVER LIFE
POSITION
AT BIRTH
SCALE
SCALE
OVER LIFE
SCALE
AT BIRTH
ROTATION
ROTATION
OVER LIFE
ROTATION
AT BIRTH
OPACITY
OPACITY
OVER LIFE
OPACITY
AT BIRTH
ANCHOR
ANCHOR
OVER LIFE
ANCHOR
AT BIRTH
6
7. P POSITION
P POSITION
OVER LIFE
P POSITION
AT BIRTH
RANDOM
RANDOM
P SCALE
P SCALE
OVER LIFE
P SCALE
AT BIRTH
RANDOM
RANDOM
P ROTATION
P ROTATION
OVER LIFE
P ROTATION
AT BIRTH
RANDOM
RANDOM
P OPACITY
P OPACITY
OVER LIFE
P OPACITY
AT BIRTH
RANDOM
RANDOM
P ANCHOR
P ANCHOR
OVER LIFE
P ANCHOR
AT BIRTH
RANDOM
RANDOM
7
39. 1. EMITTER OBJECT
contains all centralized inputs for controlling all of its child particle groups
int shape
float[3] shapeSize
int distribution
float[3] noise
float[3] anchor, position, scale, rotation
float opacity
39
40. 2. PARTICLE GROUP OBJECT
contains all centralized inputs for controlling all of its child particle groups
Image look
int randomSeed
float startTime
float quantity
float density
float life, lifeRandom
float speed, speedRandom
boolean speedOverdriveFromEmitterMotion
40
41. contains all centralized inputs for controlling all of its child particle groups
float[3] spreadAngle, spreadRange, spreadRandom
boolean spreadFollowEmitterMotion
float[3] anchorOffset, anchorOffsetRandom,
anchorWiggleFrequency, anchorWiggleAmplitude
float[3] positionWiggleFrequency,
positionWiggleAmplitude
2. PARTICLE GROUP OBJECT
41
42. contains all centralized inputs for controlling all of its child particle groups
float[2] scale, scaleRandom,
scaleWiggleFrequency, scaleWiggleAmplitude
float[3] rotationAtBirth, rotationAtBirthRandom,
rotationSpeed, rotationSpeedRandom,
rotationWiggleFrequency,
rotationWiggleAmplitude
boolean[3] rotationInBothDirection
float opacity, opacityRandom,
opacityWiggleFrequency, opacityWiggleAmplitude
2. PARTICLE GROUP OBJECT
42
43. 2. PARTICLE GROUP OBJECT
contains all centralized inputs for controlling all of its child particle groups
float[3] wind
float[3] gravity
boolean bounce
float bounceFloorDistance,
bounceElasticity, bounceFriction
float drag
float timeStretch
float timeLoop
43
44. contains generated properties to draw on screen
int index
float birth, life, age
float[3] anchor
float[3] position
float[2] scale
float[3] rotation
float opacity
3. PARTICLE INSTANCE OBJECT
44
46. Particle Life is the longevity of the particle. It tells when the particle dies:
life = pg.life + random(-pg.lifeRandom, pg.lifeRandom);
If we have PG Life Random, Particle Life will differ from PG Life.
Particle Birth is the moment the particle gets born:
birth = pg.startTime + index/pg.density;
PG Start Time is the time this Particle Group starts showing up.
Particle Index is the order of the particle, starting from 0.
PG Density is how many particles get born in a second.
Particle Age is how old the particle is at a certain time:
age = time β birth;
Time is the global value. It keeps increasing over time.
1. PARTICLE BIRTH + LIFE + AGE
46
48. Every particle has its anchor at the center of it. If we need PG Anchor Offset
feature, we need to implement it like this:
anchor = pg.anchorOffset +
random(-pg.anchorOffsetRandom,
pg.anchorOffsetRandom);
Anchor alters the result when rotating or scaling an object:
2. PARTICLE ANCHOR
48
50. Particle follows Emitting Direction. When Emitting Direction changes,
Particle Direction changes,
affecting Particle Position.
3. PARTICLE POSITION
50
51. Step 2: Calculate Particle Direction affected by
Emitter Rotation and PG Spread:
direction =
emitter.rotation +
pg.spreadAngle +
random(-pg.spreadRandom,
pg.spreadRandom);
Step 1: Define vector Particle Velocity pointing straight up (-Y):
velocity = [0, -pg.speed, 0];
PG Speed defines how fast particles travel
when they are born.
3. PARTICLE POSITION
51
53. particlePositionAtBirth
emitterPosition
vNew * age
Step 4: We now have new vector Particle Velocity. Multiply it with
Particle Age to get its local Particle Position over time:
position = vNew * age;
Finally we get global Particle Position:
position += emitter.Position;
3. PARTICLE POSITION
53
54. Emitter Size
Random point picking reference
for other shapes:
http://mathworld.wolfram.
com/topics/
RandomPointPicking.html
If Emitter Shape Size is not zero. Position At Birth is
a random point within Emitter Shape. So it is
affected by Emitter Shape and Emitter Shape Size:
positionAtBirth =
random(-emitter.shapeSize/2,
emitter.shapeSize/2);
particlePositionAtBirth
emitterPosition
vNew * time
3. PARTICLE POSITION
So we have to count in Particle Position At Birth:
position += positionAtBirth;
54
56. Particle Scale defines the size of the particle. And it is affected by PG Scale
and PG Scale Random:
scale = pg.scale +
random(-pg.scaleRandom, pg.scaleRandom);
In order to change Particle Scale Over Life we need to enable key frames for
PG Scale so its value varies from time to time, and then get its value at its
proper age, we call it PG Age:
pgAge = age * pg.life/life;
So we use this code instead of the one above:
scale = pg.scale.valueAtTime(pgAge) +
random(-pg.scaleRandom, pg.scaleRandom);
4. PARTICLE SCALE
56
58. Particle Rotation is a little more complicated. We need to apply both
PG Particle Rotation At Birth and PG Rotation Speed:
rotation = pg.rotationAtBirth +
random(-pg.rotationAtBirthRandom,
pg.rotationAtBirthRandom) +
age *
(pg.rotationSpeed.valueAtTime(age * pg.life/life) +
random(-pg.rotationSpeedRandom,
pg.rotationSpeedRandom));
5. PARTICLE ROTATION
58
60. Also like Particle Scale, Particle Opacity is simple, supporting Particle Opacity
Over Life:
opacity = pg.opacity.valueAtTime(pgAge) +
random(-pg.opacityRandom, pg.opacityRandom);
6. PARTICLE OPACITY
60
61. To achieve uniform spread, we need to add to the Particle Direction
code that we made before:
direction = emitter.rotation + pg.spreadAngle +
random(-pg.spreadRandom, pg.spreadRandom);
β¦with this:
direction += pg.spreadAngle β pg.spreadRange/2 +
pg.spreadRange/pg.quantity * index;
Quantity is the total number of particles of this Particle Group.
PARTICLE UNIFORM SPREAD
61
62. To implement Wiggle Effect for Position, Scale, Opacity and even Rotation,
we need to replicate the wiggle expression in After Effects.
Here is an example expression of Particle Scale Wiggle in After Effects:
scale = pg.scale +
wiggle(pg.scaleWiggleFrequency,
pg.scaleWiggleAmplitude);
PG Scale Wiggle Frequency is how many times a second the value changes.
PG Scale Wiggle Amplitude is how large the value changes each time.
Go to this link for reference:
http://codepen.io/jamiejefferson/pen/hHegc
WIGGLE
62
63. PG Wind is a vector that helps push particles at a constant speed:
position += age * pg.wind;
PG Gravity makes particles fall down or float up at an accelerated speed. We
can use this equation:
position[1] = 0.5 * pg.gravity * age * age +
vNew[1] * age;
PG Drag makes particles slow down over time to zero speed:
if (age == 0) age = 0.000001;
dragModifier = (1 β exp(-pg.drag * age)) /
(pg.drag * age);
position *= age * dragModifier;
WIND + GRAVITY + DRAG
63
64. PG Bounce is another story. We need to pass launch velocity, gravity, bounce
floor height, elasticity and friction to get the vertical height of the particle.
Step 1: We have velocity and gravity, we can find the max height:
π =
π π
ππ
=
π π π π
ππ
=
ππ π
π
(π = ππ)
maxHeight = pg.bounceFloorDistance +
(vNew[1] * vNew[1]) / (2 * pg.gravity);
Step 2: Find the time the particle reaches the floor. It consists of the time the
particle travels from its position at birth to the max height and the time from
the max height to the floor. Time at bounce:
π = π π + π π
BOUNCE
64
65. At max height velocity reaches zero, so we have the time from position at
birth to the max height:
π = π π β ππ π β π = π π β ππ π β π π =
π π
π
We have the max height and the gravity, we can find the time from the max
height to the floor:
π π =
ππ
π
(π =
ππ π
π
π
)
Finally we have:
timeAtBounce = vNew[1]/pg.gravity +
sqrt(2 * maxHeight/pg.gravity);
BOUNCE
65
66. Step 3: If the particle has not reached the floor (Particle Age is less than
Particle Time At Floor), it just moves under the effect of the gravity:
π = ππ β
ππ π
π
position[1] = pg.bounceFloorDistance +
vNew[1] * age β
(pg.gravity * age * age)/2;
If the particle has reached the floor (Particle Age is equal or more than
Particle Time At Floor), itβs time to calculate the bounce.
First we invert the velocity at bounce:
π = π π β ππ
v[1] = -(vNew[1] β pg.gravity * timeAtBounce);
BOUNCE
66
67. When the particle bounces on the floor, it will loose some velocity basing on
PG Bounce Elasticity:
v[1] *= pg.bounceElasticity;
If PG Bounce Elasticity is less than 1: the particles will lose some vertical velocity
after each bounce; is more than 1: they gain more vertical velocity.
The same for PG Bounce Friction but applied for horizontal velocity instead.
Then we add the time to the next bounce. It is twice the time to reach the
max height (if we consider there is no drag):
π = π Γ
π
π
timeAtBounce += 2 * v[1]/pg.gravity;
Step 4: We repreat it again and againfrom bounce to bounce until the time
between any two bounces is less than one frame duration.
Reference bounce code:
http://www.motion-graphics-exchange.com/
after-effects/Object-bouncing-on-a-floor/
473c0e7522331BOUNCE
67
68. Time Loop enables us to make seamless transition between the end and the
beginning of our animation, so that the animation can repeat forever.
Simple, we implement Time Loop by controlling Particle Age:
if (pg.timeLoop > 0 && age < 0)
age += pg.timeLoop;
This code will make unfinished particles at the end of the loop continue their
existence at the beginning of the loop.
TIME LOOP
68
69. Time Stretch defines the time and speed of the whole Particle Group.
For example if Time Stretch = 50%, the Particle Group only has half the time
to finish its animation, thus it will play faster at double speed. We can cheat
it without changing the speed by speeding up Particle Age alone:
timeStretch = abs(pg.timeStretch)/100;
age /= timeStretch;
if (pg.timeStretch < 0)
age = pg.life + pg.lifeRandom β
age β birth/timeStretch * 2;
if (pg.timeLoop > 0 && age < 0)
age += pg.timeLoop/timeStretch;
However Time Stretch also affects Particle Birth as they will get born sooner
or later:
birth = pg.startTime + timeStretch * index/pg.density;
TIME STRETCH
69