I have a sphere with various materials assigned to different faces. I want to animate it, so the materials get assigned to different faces while the animation is running. Of course, I could always create a different material for each face manually and animate each one separately, but that is boring and repetitive. Is there any easy way to achieve this, maybe built-in or using AnimAll?
-
$\begingroup$ What is the desired end result? Because if you just want to change the color you can have one multicolored texture and animate it using the Hue/Saturation/Value Node's Hue slider to cycle through the color wheel. $\endgroup$– MentalistCommented May 22, 2016 at 18:17
-
$\begingroup$ How many faces? How many colors? How many frames before a color change. How much randomness? Cycles Render. Blender Internal Render. $\endgroup$– atomicbezierslingerCommented May 22, 2016 at 18:25
-
$\begingroup$ Needs more info. Probably need to rethink the material itself rather than change materials. $\endgroup$– kheetorCommented May 23, 2016 at 7:19
2 Answers
Two preliminary animations. Top with constant color. Lower with linear interpolated color. 2 Samples [only] per frame with Cycles, thus graininess. The OP can deliver more exact visual requirements.
import bpy
import datetime
print(datetime.datetime.today())
def object_id_mark():
m = 0
for s in bpy.context.selected_objects:
m = m + 1
s.pass_index = m
object_id_mark()
In the Python above, the object index is assigned values to coordinate with the materials nodes. Those objects are the faces of a sphere with a parent for grouping. Extrude Individual Faces and Separate Spare Parts. Those objects have the same parent. They can be easily selected and the script above is executed.
Material Nodes, note the group. Image above.
Detail of Group. Image above.
Material Nodes. The color ramp interpolation can be set to constant or linear. Click to see larger image.
Correct RNA path. Image above.
Be especially careful on the node named [Datablock Careful]. This value is the current frame in a material node which requires Datablock Driver. The 3 Panels are intended to stress the necessity of navigating a deep heirarchy in the Data-Blocks Mode of the Outlner Window.
ObjectName/MaterialSlots/[MaterialName]/Material/[MaterialName]/NodeTree/ShaderNodes/[NodeName]
then
Outputs/Value/Node/[NodeName]/DefaultValue
This list is quite long and my path may not meet exactly your reading of the path. Changing the value manually will verify you have correctly navigated the hierarchy. The Outliner Data-Blocks window interface may help you reduce clicks by automatically opening a single choice when present.
Image Above.
Just to look at a different way of doing this, I thought I would use bpy.app.handlers
to do the material changes.
If we were starting from an empty scene we can create the sphere and a material with a random colour for each face.
import bpy
import random
bpy.ops.mesh.primitive_uv_sphere_add(segments=12, ring_count=12,
size=1, location=(0, 0, 0))
obj = bpy.context.active_object
# clear any existing material slots
while len(obj.material_slots):
bpy.ops.object.material_slot_remove()
# create a random coloured material for each face
# adjust this number to have less materials if desired
# eg for f in range(10): will only create 10 materials
for f in range(len(obj.data.polygons)):
mat = bpy.data.materials.new('randomColour')
bpy.ops.object.material_slot_add()
obj.material_slots[-1].material = mat
r = random.random()
g = random.random()
b = random.random()
# mat.diffuse_color is also used by cycles for viewport colour
# and as a fallback if nodes are not active
mat.diffuse_color.r = r
mat.diffuse_color.g = g
mat.diffuse_color.b = b
Now that we have a material for each face, lets make a way to randomise what material each face uses.
import bpy
import random
def randomise_materials(scene):
obj = scene.objects['Sphere']
polys = obj.data.polygons
if scene.frame_current == 1:
# by setting a seed on frame 1 we can get
# a repeatable sequence during playback
random.seed(5)
m_idx = []
i = 0
for x in range(len(polys)):
# we want a list matching the number of faces
# it can only contain indexes matching available materials
if i >= len(obj.material_slots):
i = 0
m_idx.append(i)
i += 1
# after creating a list of indexes we can shuffle them
# and then copy to the matching poly.material_index
random.shuffle(m_idx)
for i,p in enumerate(polys):
p.material_index = m_idx[i]
# set materials on first run
randomise_materials(bpy.context.scene)
Then to use this for an animation we append this function to the handlers list.
bpy.app.handlers.frame_change_pre.append(randomise_materials)
And if you want to disable it
bpy.app.handlers.frame_change_pre.remove(randomise_materials)