3
$\begingroup$

I have a scene where I have a lot of particles (> 5000) which are essentially spheres, that I would like to animate in a particular fashion. Basically, the thousands of particles can be categorized into a few types, with each type having its own material.

I am working on animating the behavior of a "periodic" box where, as particles cross a plane, they appear out the other side. Similar to a game of snake where the snake goes through one wall and appears out the other. However, I have come across the following limitation(s):

  • When I keyframe the animation, unless the frames are literally 1 apart, the particles will travel backwards then start moving forwards again. If the frames are 1 apart, the particle will disappear and reappear at the back. This is undesirable as I would like to have a smooth animation.

As a solution to this, I thought of fading out the object, move it, then fade it back in at the start. However this brings up another issue:

  • I am using cycles. Short of having 5000+ materials, one for each objects, I don't know how to control the opacity/transparency of an object independently. Also, if I have that many materials, I lose the ability to change the attributes (color/texture/etc...) of an entire group collectively.

If anyone has any thoughts on how to deal with this issue, whether in terms of fading out an object independently of its material, or any neat approaches to what I am trying to do, it would be greatly appreciated. Just for sake of completeness, I am writing a blender python module to do this, since all the position data for the particles are in a file generated by a simulation, and this is the remaining issue I have.

Thanks!

$\endgroup$
  • 1
    $\begingroup$ Maybe I've not well understood the situation, but what would the benefit be in terms of simplicity by not using materials? This "vanishing" property must be controlled per object too,..or not? Changing the attributes of an entire group can be done by using materials node groups. Do you have an example video to show? It would be helpful to better understand the situation $\endgroup$ – Carlo Aug 13 '15 at 21:29
  • $\begingroup$ So you are saying I create independent materials for each particle, but have all the similar particles in one node group? I did not think of that. If I do that, how can I change the materials of all of the nodes collectively? Will it override the individual material properties of each one? This may be what I'm looking for, but if you could clarify that would be great. $\endgroup$ – pragmatist1 Aug 13 '15 at 21:39
  • $\begingroup$ It would be great if you could add a couple of images to your question describing what you are after. It might be something that's easier fine on the compositor. But I'm not 100% sure I understood the question correctly $\endgroup$ – cegaton Aug 13 '15 at 22:04
  • $\begingroup$ No, I was just asking for clarifications. What I was suggesting that with material nodes group you can give a base shader, one per type, and than add a node setup to controle the transparency, that would work per object, so you'll still have 5000 material but based on few groups. The challenge is to avoid to control 5000 transparency keyframes $\endgroup$ – Carlo Aug 13 '15 at 22:06
2
$\begingroup$

Ok so I figured out a solution thanks to @Carlo's comment. Hopefully this will help anyone else who's trying to do something similar.

I create one node group per object type that has a default color, and accepts one input: transparency. The node group itself is basically a mixer between Diffuse bsdf and transparent bsdf shaders.

Each object of that type has it's own material which basically links the node group with the material output. I can then keyframe the opacity on each material independently, but still maintain the ability to change the color collectively for the type by changing the node group color.

Since I am scripting this, I am not worried about having to keyframe 5000+ objects. I came up with the following functions:

# create_node_group - Creates a node group
# for a particle type with a default color.
def create_node_group(name, color):
    group = bpy.data.node_groups.new(name, 'ShaderNodeTree')

    # Create node group input.
    inputs = group.nodes.new('NodeGroupInput')
    inputs.location = (-300, 0)

    # Create diffuse shader.
    diffuse = group.nodes.new('ShaderNodeBsdfDiffuse')
    diffuse.location = (0, -200)
    diffuse.inputs[0].default_value = (color[0], color[1], color[2], 1)

    # Create transparent shader.
    transparent = group.nodes.new('ShaderNodeBsdfTransparent')
    transparent.location = (0, -400)

    # Create shader mixer.
    mixer = group.nodes.new('ShaderNodeMixShader')
    mixer.location = (200, 0)

    # Create node group output.
    outputs = group.nodes.new('NodeGroupOutput')
    outputs.location = (350, 0)

    # Create input and output sockets.
    group.inputs.new('NodeSocketFloat', 'Transparency')
    group.outputs.new('NodeSocketShader', 'Shader')

    # Link everything together.
    group.links.new(inputs.outputs[0], mixer.inputs[0])
    group.links.new(diffuse.outputs[0], mixer.inputs[1])
    group.links.new(transparent.outputs[0], mixer.inputs[2])
    group.links.new(mixer.outputs[0], outputs.inputs[0])

    return group

The above function creates the node group and returns a reference to it. I then use the function below to create a material for each object in my scene, which calls the function above.

# create_material - Creates a material with a color
# for the group (if not created already). Name and
# group name should typically be the same.
def create_material(name, group_name, color):
    group = bpy.data.node_groups.get(group_name)
    if group is None:
        group = create_node_group(group_name, color)

    mat = bpy.data.materials.new(name)
    mat.use_nodes = True

    for node in mat.node_tree.nodes:
        mat.node_tree.nodes.remove(node)

    mgroup = mat.node_tree.nodes.new('ShaderNodeGroup')
    mgroup.node_tree = group

    outmat = mat.node_tree.nodes.new('ShaderNodeOutputMaterial')
    outmat.location = (200, 0)

    mat.node_tree.links.new(mgroup.outputs[0], outmat.inputs[0])
    return mat

Finally, if I want to change the opacity of an object, I simply call this small function:

# set_transparency - Set the transparency of an object.
def set_transparency(ob, val):
    mat = ob.data.materials[0]
    mat.node_tree.nodes["Group"].inputs[0].default_value = val

This may not be the best solution, but it works for me, and hopefully it will help others in a similar situation.

Thanks!

$\endgroup$
  • $\begingroup$ Just to clarify. Do you call the create_material() method 5k times once for each sphere? Or just a few times, once for each sphere type? $\endgroup$ – Justas Feb 15 '18 at 21:06

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.