Flowing Particles

This is a slightly more involved example of what can be achieved with the ParticleBlinkScript node, the result is making particles flow around an object.

The resulting effect of the following example flowing particles kernel

Geometry cannot be accessed from the ParticleBlinkScript node, so expressions can be used within the translate, rotate and scale knobs of the ParticleBlinkScript node to access the translate, rotate and scale values from a Cylinder geometry node. The radius can be linked using an expression as well.

Example node graph for the following kernel

The basic principle of this kernel is to generate a vector field which gives the direction of flow of the particles at every point in space. The velocity vector of each particle is changed to point in the direction of flow.

Tip:  Avoiding objects is just one of the ways this example can be used, many different effects can be achieved by using different flow field functions.

Here is the relevant part of the kernel which implements this. The fieldAt function returns the direction of flow at a point in space and the process function redirects the velocity vector of each particle to point along the flow vector at its position.

float3 fieldAt( float3 pos )

{

return pos; // A field which just points away from the origin

}

void process()

{

float3 p = p_position();

float3 v = p_velocity();

float3 field = fieldAt( p );

field.normalize();

p_velocity() = field*v.length();

}

The following example kernel implements a function causing particles to flow around an imaginary cylinder.

kernel ParticleCylinderFlowKernel : ImageComputationKernel<ePixelWise>

{

Image<eReadWrite> p_position;

Image<eReadWrite> p_velocity;

Image<eReadWrite> p_orientation;

param:

float3 _origin; // Origin of the cylinder

float3 _axis; // Axis of the cylinder

float _radius; // Radius of the cylinder

float3 _flow; // Direction of flow

float _strength; // The strength of the interaction with the flow

float _falloff; // The speed at which the force falls off with distance from the surface

local:

float3 _normalizedAxis;

void define() {

defineParam(_origin, "pa_origin", float3(0.0f, 0.0f, 0.0f));

defineParam(_axis, "pa_axis", float3(0.0f, 1.0f, 0.0f));

defineParam(_flow, "pa_flow", float3(0.0f, 0.0f, -1.0f));

defineParam(_radius, "pa_radius", 1.0f);

defineParam(_strength, "pa_strength", 1.0f);

defineParam(_falloff, "pa_falloff", 1.0f);

}

void init() {

_normalizedAxis = normalize(_axis);

}

float3 mix(float3 a, float3 b, float t ) {

return a+t*(b-a);

}

void process() {

float3 p = p_position()-_origin;

float l = length(p);

if ( l != 0.0f ) {

float3 axis = _axis/l;

p -= _origin+_normalizedAxis*dot(p, _normalizedAxis);

float r = length(p);

float3 normal = p;

float3 d = cross(normal, _flow);

float3 tangent = cross(d, normal);

float fall;

if ( r >= _radius )

fall = exp(-_falloff*(r-_radius));

else

fall = 1.0f;

float3 force = tangent*_strength;

force = mix( _flow, force, fall );

l = length(force);

if ( l != 0.0f ) {

force = force/l;

float speed = length(p_velocity());

p_velocity() = speed*force;

p_orientation() = float4(0.0, force.x, force.y, force.z);

}

}

}

};

Tip:  There are many ways to experiment with this kernel, try using a fluid simulation to generate the flow field for simulating smoke.