3
$\begingroup$

Is it possible to have same (single) material object applied to different objects (mesh or curves) but have different key frames for each (mesh or curve)?

I want to fade-in my text (which is curve) like a window so the text appear fade-In gradually character wise. I have created a material slot, added TransparentBSDF and MixShader Node, but separate Material slot for each Character (curve) because each character fade-In start time index is slightly greater than the previous.

Currently I am doing this using Python Code (Blender 2.83)

fade_objects = bpy.context.selected_objects

frame_dif = 2
frame_length = 40
frame_start = 0

fade_frame_start = frame_start
fade_frame_end = fade_frame_start + frame_length

for fade_object in fade_objects:

    # Create Material Object and Apply to character curve
    mat = bpy.data.materials.new('Fade')  
    fade_object.data.materials.pop()
    fade_object.data.materials.append(mat)
    mat.use_nodes = True
    mat.blend_method = 'BLEND'

    nodes = mat.node_tree.nodes
    links = mat.node_tree.links

    #Creating New Nodes
    node_transparent = nodes.new('ShaderNodeBsdfTransparent')
    node_mixshader = mat.node_tree.nodes.new('ShaderNodeMixShader')
    node_bsdf = nodes['Principled BSDF']
    node_matout = nodes['Material Output']

    #Linking Nodes
    links.new(node_transparent.outputs['BSDF'], node_mixshader.inputs[1])
    links.new(node_bsdf.outputs['BSDF'], node_mixshader.inputs[2])
    links.new(node_mixshader.outputs['Shader'], node_matout.inputs['Surface'])

    #Adding Key Frame for MisShader Node (Fade In)
    node_mixshader.inputs['Fac'].default_value = 0
    node_mixshader.inputs['Fac'].keyframe_insert('default_value', frame = fade_frame_start)
    node_mixshader.inputs['Fac'].default_value = 1
    node_mixshader.inputs['Fac'].keyframe_insert('default_value', frame = fade_frame_end)
    
    #Add frame difference for next character for sliding effect
    fade_frame_start += frame_dif
    fade_frame_end += frame_dif

This code create n number of material slots as my n number of curves. Can I do this with single Material Object with different values for MixShader node for each curve?

$\endgroup$
2
  • 1
    $\begingroup$ You can't directly have different keyframe sets for a material but you can use for instance the object pass index attribute, plug it into your factor and then keyframe the object pass. More info : blender.stackexchange.com/a/183384/86891 $\endgroup$
    – Gorgious
    Jul 5 '20 at 10:41
  • $\begingroup$ Use drivers for this. $\endgroup$ Jul 5 '20 at 11:02
4
$\begingroup$

Object Info Node

enter image description here

As mentioned by @Gorgeous can use an object info node.

Control object emission strength (not material)

Using Object.color

Object color, same as Color in the
Properties Editor ‣ Object ‣ Viewport Display

enter image description here

For the example below am creating two new nodes, an object info node, separating the RGB via a separate RGB node and feeding this into the mix node factor.

Unfortunately I am not aware of how to use the Alpha channel, so am keyframing and using the Red channel.

This way the action is created on and belongs to the object using the material.

Minor adjustment to your code to add only one material with the new nodes and links, and keyframe the object's red color in the loop.

import bpy

fade_objects = bpy.context.selected_objects

frame_dif = 10
frame_length = 40
frame_start = 0

fade_frame_start = frame_start
fade_frame_end = fade_frame_start + frame_length



# Create Material Object and Apply to character curve
mat = bpy.data.materials.new('Fade')  

mat.use_nodes = True
mat.blend_method = 'BLEND'

nodes = mat.node_tree.nodes
links = mat.node_tree.links

#Creating New Nodes
node_transparent = nodes.new('ShaderNodeBsdfTransparent')
node_mixshader = mat.node_tree.nodes.new('ShaderNodeMixShader')
node_bsdf = nodes['Principled BSDF']
node_matout = nodes['Material Output']


objinput = nodes.new("ShaderNodeObjectInfo")
seprgb = nodes.new("ShaderNodeSeparateRGB")


#Linking Nodes
links.new(node_transparent.outputs['BSDF'], node_mixshader.inputs[1])
links.new(node_bsdf.outputs['BSDF'], node_mixshader.inputs[2])
links.new(node_mixshader.outputs['Shader'], node_matout.inputs['Surface'])
links.new(objinput.outputs['Color'], seprgb.inputs['Image'])
links.new(seprgb.outputs[0], node_mixshader.inputs['Fac'])

for fade_object in fade_objects:
    fade_object.animation_data_create()
    fade_object.animation_data_clear()
    fade_object.data.materials.clear()
    fade_object.data.materials.append(mat)
    fade_object.color[0] = 0
    fade_object.keyframe_insert("color", index=0, frame=fade_frame_start)
    fade_object.color[0] = 1
    fade_object.keyframe_insert("color", index=0, frame=fade_frame_end)    
    #Add frame difference for next character for sliding effect
    fade_frame_start += frame_dif
    fade_frame_end += frame_dif

Other things to consider would be using drivers on the color field based on the current frame, or by using a single action that is attached to each object and offset via the NLA

$\endgroup$
2
$\begingroup$

With V2.92 and the new Attribute node, you can achieve it with a single driver in the material.

First, you need to add a custom property to all your objects. Then, compare it with the current frame. Choose a different offset for each object to make them appear in sequence.

Here's the code :

import bpy
new_mat = bpy.data.materials.get('Fade In Material')
if not new_mat:
    new_mat = bpy.data.materials.new('Fade In Material')
    
frame_offset = 50

new_mat.use_nodes = True
node_tree = new_mat.node_tree
nodes = node_tree.nodes
nodes.clear()
    
links = node_tree.links
links.clear()
    
# Nodes :

new_node = nodes.new(type='ShaderNodeBsdfPrincipled')
new_node.location = (10.0, 300.0)
new_node.name = 'Principled BSDF'

new_node = nodes.new(type='ShaderNodeOutputMaterial')
new_node.location = (300.0, 300.0)
new_node.name = 'Material Output'

new_node = nodes.new(type='ShaderNodeAttribute')
new_node.attribute_name = 'frame_start'
new_node.attribute_type = 'OBJECT'
new_node.location = (-665.818115234375, -101.93711853027344)
new_node.name = 'Attribute'

new_node = nodes.new(type='ShaderNodeMath')
new_node.location = (-482.0205383300781, -93.33195495605469)
new_node.name = 'Math'
new_node.operation = 'SUBTRACT'
driver = new_node.inputs[0].driver_add("default_value").driver
driver.expression = "frame"

new_node = nodes.new(type='ShaderNodeMath')
new_node.location = (-282.9521484375, -71.30279541015625)
new_node.name = 'Math.001'
new_node.operation = 'DIVIDE'
new_node.use_clamp = True
new_node.inputs[1].default_value = frame_offset

new_mat.blend_method = 'BLEND'
new_mat.show_transparent_back = False

# Links :

links.new(nodes["Principled BSDF"].outputs[0], nodes["Material Output"].inputs[0])    
links.new(nodes["Attribute"].outputs[2], nodes["Math"].inputs[1])    
links.new(nodes["Math"].outputs[0], nodes["Math.001"].inputs[0])    
links.new(nodes["Math.001"].outputs[0], nodes["Principled BSDF"].inputs[19])    

# Link Material to objects :

for i, obj in enumerate(so for so in bpy.context.selected_objects if so.data):
    obj["frame_start"] = i * frame_offset
    if obj.data.materials:
        obj.data.materials[0] = new_mat
    else:
        obj.data.materials.append(new_mat)

Resulting shader (There is a driver to the current frame in the first Math node) :

enter image description here

Result with a few objects :

enter image description here

$\endgroup$

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.