5
$\begingroup$

(as advised I deleted my previous question)

I would like to add a light streak effect to the finger tips of this animated dancer.

I am using a particle emitter that is assigned to a vertex group (finger tips) but I am unable to get them to give me a streak effect.

So far I can only get this: enter image description here and I would like something closer to this: (I'd be delighted with something far more simple) enter image description here

I've seen tutorials on line but they were in Blender Render and not Cycles. Is there a way to get the streak effect with a particle emitter in cycles?

$\endgroup$
17
$\begingroup$

You can achieve this by using particles to trace out the path of the motion and converting each particle system into a Curve object. The curves can then be used to draw the trails and animated using keyframes to match the motion.

Start by adding particle systems to the mesh. It's important to have a separate particle system for each 'trail'. The order the particles are emitted will control the order of the points in that trail.

There are two ways of achieving this.

  1. Create a mesh with a single vertex and add a particle system. This can then be duplicated, positioned, and parented to the moving mesh so that it follows the motion. You can create as many of these 'trace' objects as are required.

  2. Emit the particles directly from the mesh being traced. It's important that each particle system emits from a single vertex - so use Vertex Groups to limit the emission for each particle system, adding a particle system for each.

Ensure the particle systems are set to emit the particles with no initial velocity and that the relevant Field Weights are set to zero so they are not affected by any forces present. This should result in the particles 'hanging' at the point they are emitted.

initial velocity Field Weights

animated single trail

Once you have the emitted particles, open a Text Editor window and paste the following python code :

import bpy

def particles_to_path(objName, tracks=1, particleSystem=0, curveResolution=0, bevelDepth=0.1):

    object = bpy.context.scene.objects[objName]

    particles = object.particle_systems[particleSystem].particles

    trackNo = 0
    trackPoint = 0
    curves = []
    splines = []

    for p in particles:
        if trackPoint == 0:
            # Create new track
            curve = bpy.data.curves.new('particlePath', type='CURVE')
            curve.dimensions = '3D'
            curve.resolution_u = curveResolution
            curve.bevel_depth = bevelDepth
            spline = curve.splines.new('NURBS')
            curves.append(curve)
            splines.append(spline)

            #set first point
            print(spline.points[0])
            print(spline.points[0].co)
            spline.points[0].co = (p.location[0], p.location[1], p.location[2],1)
        else:
            # Add point
            splines[trackNo].points.add()
            splines[trackNo].points[trackPoint].co = (p.location[0], p.location[1], p.location[2],1)

        trackNo += 1

        if trackNo >= tracks:
            trackNo = 0
            trackPoint += 1

    # deselect all
    for curve in curves:
        curveObject = bpy.data.objects.new('particlePath', curve)
        bpy.context.scene.objects.link(curveObject)
        #...select it?

particles_to_path('Trace',particleSystem=0, curveResolution=8)

The code defines a function (particles_to_path) which will use the particles emitted by a particle system on an object to create a Curve object. Note the last line in the script which invokes the function, specifying the object 'Trace'. This will use the first particle system on the 'Trace' object and will create a Curve. To invoke the function on multiple particle systems, simply duplicate the last line and change each as appropriate.

For example, for multiple objects named 'Trace', 'Trace.001', 'Trace.002', 'Trace.003', etc. use the following lines :

particles_to_path('Trace',particleSystem=0, curveResolution=8)
particles_to_path('Trace.001',particleSystem=0, curveResolution=8)
particles_to_path('Trace.002',particleSystem=0, curveResolution=8)
particles_to_path('Trace.003',particleSystem=0, curveResolution=8)

If you have multiple particle systems on the same object, each emitting from a single vertex then you can use the 'particleSystem' parameter to indicate which particle system to use (0 = the first one, 1 = the second one, 2 = the third one, etc.) :

particles_to_path('myMesh',particleSystem=0, curveResolution=8)
particles_to_path('myMesh',particleSystem=1, curveResolution=8)
particles_to_path('myMesh',particleSystem=2, curveResolution=8)
particles_to_path('myMesh',particleSystem=3, curveResolution=8)
particles_to_path('myMesh',particleSystem=4, curveResolution=8)

Once you have generated the curves, simply adjust the curve properties to generate the required trails. For example, set Resolution and Fill in the Shape settings to generate a curved shape...

Curve Shape settings

...and use the Geometry Bevel settings to adjust the 'trail' - setting Start and End to position it along the curve :

Curve Geometry

Note that the Taper Object can be used to specify an additional curve object which defines the shape of the bevel along the curve (with Map Taper enabled to taper the bevel along the Start-End interval rather than the whole curve).

some trails

Carefully keyframing the Start and End will allow the trail to be animating. Ensure to set the Interpolation of the animation curves to Linear so it matches the motion of the particle emitting mesh.

For the material, you can use a combination of Emission and Scatter on the Volume. This will allow the trails to be partially opaque while still emitting light. Mixing in a Transparent shader based on the light path allows them to be hidden from all but the camera (so they don't reflect off of the surfaces). Including variation based on the Random object info provides automatic variation for each trail (you could assign individual materials manually in you prefer more control).

material

This can produce the following result :

animated cube

Blend file :

Applying this to the example 'dancer' mesh can produce the following effect :

robot dancer frame 72


EDIT: I have since packaged the above script as an add-on as part of this answer, available from here. This makes it much simpler to run and avoids the need to create and edit a custom script.

$\endgroup$
  • 1
    $\begingroup$ WOW! Thank you so much. I learnt a lot from your answer! :) $\endgroup$ – chris loughnane Nov 23 '17 at 1:17
  • $\begingroup$ Can we do the same thing with Animation Nodes? $\endgroup$ – unfa Mar 23 '18 at 10:09
  • $\begingroup$ @unfa From what I’ve see of Animation Nodes for other questions, I imagine so. However, I’ve never got very far with AN - I struggled to get it to install on my system due to python and library issues - so I don’t really have the experience to comment on that. Perhaps start a new question linking to this one and asking if anyone can do similar with AN. $\endgroup$ – Rich Sedman Mar 23 '18 at 10:29
  • $\begingroup$ How exactly do you set up the taper path object? $\endgroup$ – Legoman Jul 12 '18 at 18:40
  • 1
    $\begingroup$ @Legoman The taper object is essentially just a path that varies alog its length to define how your curve bevel tapers - the further from its own local Y axis, the thicker the bevel. See blender.stackexchange.com/a/8691/29586 - hope this makes sense. $\endgroup$ – Rich Sedman Jul 12 '18 at 19:09
3
$\begingroup$

Hi I took the liberty of updating Rich Sedman's script to work in 2.8

import bpy

def particles_to_path(objName, tracks=1, particleSystem=0, curveResolution=0, bevelDepth=0.1):

degp = bpy.context.evaluated_depsgraph_get()
object = bpy.context.scene.objects[objName]
particle_systems = object.evaluated_get(degp).particle_systems

particles = particle_systems[particleSystem].particles
trackNo = 0
trackPoint = 0
curves = []
splines = []


for p in particles:
    if trackPoint == 0:
        # Create new track
        curve = bpy.data.curves.new('particlePath', type='CURVE')
        curve.dimensions = '3D'
        curve.resolution_u = curveResolution
        curve.bevel_depth = bevelDepth
        spline = curve.splines.new('NURBS')
        curves.append(curve)
        splines.append(spline)

        #set first point
        print(spline.points[0])
        print(spline.points[0].co)
        spline.points[0].co = (p.location[0], p.location[1], p.location[2],1)

    else:
        # Add point
        splines[trackNo].points.add(1)
        splines[trackNo].points[trackPoint].co = (p.location[0], p.location[1], p.location[2],1)

    trackNo += 1

    if trackNo >= tracks:
        trackNo = 0
        trackPoint += 1

# deselect all

for curve in curves:
    curveObject = bpy.data.objects.new('particlePath', curve)
    bpy.context.scene.collection.objects.link(curveObject)
    #...select it?

particles_to_path('objectName',particleSystem=0, curveResolution=8)
$\endgroup$
  • $\begingroup$ Thanks Bent, always nice to see new approaches $\endgroup$ – chris loughnane Aug 3 at 1:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.