# How to get random color variation on a single mesh?

I am trying to get color variation and to bake it in a single mesh but the Object Info Random only works if i separate the meshes. Is there an alternative way to get the same result with the meshes combined, they aren't attached between them.

• I don't know of any way to currently do this in Cycles, though I would also like to know if anyone has figured out any way to achieve this effect. Commented Mar 27, 2017 at 16:38

Following the several comments, I've thought an addon can be useful to do that and may have a general interest.

What does it do:

• Identify loose parts of the object (or all selected objects)
• Generate a random color for each loose part
• Follow the mesh loops to assign the vertex colors

(can be installed as an addon)

The code is commented below (please comment if needed):

bl_info = {
"name": "Assign random vertex color to each loose part of a mesh",
"author": "lemon",
"version": (1, 0),
"blender": (2, 78, 0),
"location": "Properties Editor > Object data",
"description": "",
"warning": "",
"wiki_url": "",
"category": "Mesh"}

import bpy
import time
import random
from mathutils import Color

def LoosePartsMakeVertPaths( verts, edges ):
#Initialize the path with all vertices indexes
result = {v.index: set() for v in verts}
#Add the possible paths via edges
for e in edges:
return result

def LoosePartsFollowEdges( startingIndex, paths ):
current = [startingIndex]
result = set( current )

while follow:
#Get indexes that are still in the paths
eligible = set( [ind for ind in current if ind in paths] )
if len( eligible ) == 0:
follow = False #Stops is no more
else:
next = [paths.pop( i ) for i in eligible]
#Get the new links as new inputs
current = set( [ind for sub in next for ind in sub] )
result = result.union( current )

return result

def LoosePartsCountIslands( obj ):
#Prepare the paths/links from each vertex to others
paths = LoosePartsMakeVertPaths( obj.data.vertices, obj.data.edges )
found = True
n = 0
while found:
try:
#Get one input as long there is one
startingIndex = next( iter( paths.keys() ) )
n = n + 1
#Deplete the paths dictionary following this starting index
LoosePartsFollowEdges( startingIndex, paths )
except:
found = False
return n

def LoosePartGetIslands( obj ):
paths = LoosePartsMakeVertPaths( obj.data.vertices, obj.data.edges )

result = []

found = True
while found:
try:
#Get one input as long there is one
startingIndex = next( iter( paths.keys() ) )
#Deplete the paths dictionary following this starting index and append the result
result.append( LoosePartsFollowEdges( startingIndex, paths ) )
except:
found = False

return result

def RandomColor( rnd ):
return Color( (rnd.random(), rnd.random(), rnd.random()) )

def RandomColors( rnd, count ):
return [RandomColor( rnd ) for i in range( count )]

def ColorFromVertexIndex( vertexIndex, looseParts, colors ):
for c, lp in zip( colors, looseParts ):
if vertexIndex in lp:
return c
return Color()

def RandomVertexColorsByLooseParts( obj, vcolName, useSeed, seed ):

rnd = random.Random()
if useSeed:
rnd.seed( seed )

mesh = obj.data

#Create vertex colors if it does not already exist
if vcolName not in mesh.vertex_colors:
mesh.vertex_colors.new( vcolName )
#Update it so that it is available below
mesh.vertex_colors.update()
#Get the vertex colors
vertexColors = mesh.vertex_colors[vcolName]

#Get loose parts
looseParts = LoosePartGetIslands( obj )
#Generate random colors
colors = RandomColors( rnd, len(looseParts) )

mesh.loops.update()

for i, vertexIndex in enumerate( [loop.vertex_index for loop in mesh.loops] ):
#Assign vertex colors from loop index and vertex index
vertexColors.data[i].color = ColorFromVertexIndex( vertexIndex, looseParts, colors )

def InitProperties():
bpy.types.Scene.VertexColorsName = bpy.props.StringProperty( name = "Vertex colors", description = "Vertex colors to use or create", default = "Col" )
bpy.types.Scene.VertexColorsUseSeed = bpy.props.BoolProperty( name = "Use seed", description = "Use fixed seed to generate random values" )
bpy.types.Scene.VertexColorsSeed = bpy.props.IntProperty( name = "Seed", description = "Seed for random values" )
bpy.types.Scene.VertexColorsAllSelected = bpy.props.BoolProperty( name = "All selected objects", description = "Apply to all selected object or only active one" )

class RndVColToLoosePartsPanel(bpy.types.Panel):
bl_label = "Random vcol to loose parts"
bl_space_type = "PROPERTIES";
bl_region_type = "WINDOW";
bl_context = ""

@classmethod
def poll(self, context):
return context.object and context.scene

def draw(self,context):
layout = self.layout
scn = context.scene

layout.prop( scn, 'VertexColorsName', icon='GROUP_VCOL' )
layout.prop( scn, 'VertexColorsUseSeed' )
layout.operator( "rndvcoltolooseparts.ope")
layout.prop( scn, 'VertexColorsAllSelected' )

class RndVColToLooseParts(bpy.types.Operator):
bl_idname="rndvcoltolooseparts.ope"
bl_label="Generate random vcol to each loose parts"

def execute(self, context):

scene = context.scene
obj = context.object

if scene and obj and obj.type == 'MESH':

prevMode = obj.mode
bpy.ops.object.mode_set( mode='OBJECT', toggle=False )

if scene.VertexColorsAllSelected:
for obj in [obj for obj in context.selected_objects if obj.type == 'MESH']:
RandomVertexColorsByLooseParts( obj, scene.VertexColorsName, scene.VertexColorsUseSeed, scene.VertexColorsSeed )
else:
RandomVertexColorsByLooseParts( obj, scene.VertexColorsName, scene.VertexColorsUseSeed, scene.VertexColorsSeed )

bpy.ops.object.mode_set( mode=prevMode, toggle=False )

return {'FINISHED'}

def register():
InitProperties()
bpy.utils.register_class(RndVColToLooseParts)
bpy.utils.register_class(RndVColToLoosePartsPanel)

def unregister():
bpy.utils.unregister_class(RndVColToLooseParts)
bpy.utils.unregister_class(RndVColToLoosePartsPanel)

if __name__ == "__main__":
register()


2.80 Version.

• Creates a random grey-scale color and assigns to vertex color for each mesh loose part. Each component of the rgba vector is the same. Can be plugged into factor sockets individually.

• Sets the face material color of each part to the active material index. Example material used in image above, for the greyscale plug the color attribute node directly into shader.

bl_info = {
"name": "Mesh Islands Random Vertex Color",
"author": "batFINGER",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "F3 Search",
"description": "Assign random greyscale vertex color to mesh islands",
"warning": "",
"wiki_url": "",
"category": "Mesh"}

import bpy
import bmesh
from random import random
from bpy.props import StringProperty, BoolProperty
from mathutils import Vector

def walk_island(vert):
''' walk all un-tagged linked verts '''
vert.tag = True
yield (vert)
if not e.other_vert(vert).tag
]
if v.tag:
continue
yield from walk_island(v)

def get_islands(bm, verts=[]):
def tag(verts, switch):
for v in verts:
v.tag = switch

tag(bm.verts, True)
tag(verts, False)
ret = {"islands": []}
verts = set(verts)
while verts:
v = verts.pop()
island = set(walk_island(v))
faces = set(
f for x in island for f in x.link_faces
if all(v.tag for v in f.verts))
edges = set(
e for x in island for e in x.link_edges
if all(v.tag for v in e.verts))
ret["islands"].append(island.union(edges).union(faces))
tag(island, False)  # remove tag = True
verts -= island
return ret

class BMIslands(list):
def __init__(self, bm):
self.extend(BMeshIsland(i, island) for i, island in enumerate(get_islands(bm, verts=bm.verts)["islands"]))
self.sort(key=lambda i: (i.co.x, i.co.y))
for i, island in enumerate(self):
island.index = i

class BMeshIsland:
def __init__(self, index, geom):
self.index = index
self.verts = [e for e in geom if isinstance(e, bmesh.types.BMVert)]
self.co = sum([v.co for v in self.verts], Vector()) / len(self.verts)
self.faces = [e for e in geom if isinstance(e, bmesh.types.BMFace)]

class MESH_OT_random_island_vertex_color(bpy.types.Operator):
"""Tooltip"""
bl_idname = "mesh.random_island_vert_color"
bl_label = "Random Island Vert Color"
bl_options = {'REGISTER', 'UNDO'}
islands = None
bm = None

refresh: BoolProperty(name="Refresh")
color_map: StringProperty(
name="Color Map", default="vertcol")

@classmethod
def poll(cls, context):
return(context.object
and context.object.mode == 'OBJECT'
and context.object.type == 'MESH')

def main(self, ob):
me = ob.data

bm = bmesh.new()
bm.from_mesh(me)
self.bm = bm

self.islands = BMIslands(bm)

def randomize(self, ob):
me = ob.data
clayers = self.bm.loops.layers.color
color_layer = clayers.get(self.color_map) or clayers.new(self.color_map)

ami = ob.active_material_index
for island in self.islands:
r = random()
for f in island.faces:
f.material_index = ami
for l in f.loops:
#l[color_layer] = (r, r, r, 1)
l[color_layer] = (r, ) * 4

self.bm.to_mesh(me)
me.update()

def invoke(self, context, event):
ob = context.object
self.main(ob)
return self.execute(context)

def execute(self, context):
self.randomize(context.object)
return {'FINISHED'}

def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, "color_map")

row.prop(self, "refresh", text="", icon='FILE_REFRESH')
layout.operator(self.__class__.bl_idname)

def register():
bpy.utils.register_class(MESH_OT_random_island_vertex_color)

def unregister():
bpy.utils.unregister_class(MESH_OT_random_island_vertex_color)

if __name__ == "__main__":
register()

• Great! Many thanks for your bmesh magic. Had to find an automatic workflow to bake id maps based on random vertex colors per island in painter. Created a hsv version based on your code which allows to set the colors in vertex paint mode as well: gist.github.com/p2or/ab314836285579cdfdb5d7848af8ee5c Also fixed a bug when assigning a new name if there is more than one layer present. Anyway, this script is super quick and pretty useful. Can we have a repo for this?
– p2or
Commented May 2, 2021 at 19:34
• @p2or Cheers. Update looks good. Thanks for the support. Not too many too quickly, lol (based on time matching comment above) Commented May 3, 2021 at 14:57

In Blender 2.8, there is an attribute "Random Per Island" in the geometry input. It only works with Cycles though (not Eevee nor the material preview). It does the job!

• nasty..this was the easiest and worked like a charm..THANK YOU Commented Mar 25, 2021 at 15:48

You may use a Color Ramp node to do it. Add a Separate XYZ node (Add-->Converter-->SeparateXYZ) and plug its Z output to the Fac input of the ColorRamp node. Set its interpolation type to Constant. Use '+' sign to add colors and set the sliders to fit the mesh's segments.

• The problem is that it wont give a good result if i combine multiple assets with different shapes. Commented Mar 27, 2017 at 17:16
• You could use vertex colors to flag different regions, instead of using locations within the generated coords box. Commented Mar 27, 2017 at 18:50
• @JtheNinja Sounds good! Please write an answer. Commented Mar 28, 2017 at 0:06

Here is the pure Blender GUI way:

1. Add a new shape key (basis) and then another, call it 'merged'. Set merged to value 1.
2. Change pivot point to individual origins. Select all verts.
3. S, 0 Enter. All loose parts are merged into a single point in space.
4. Unwrap the mesh, sphere projection. each of these loose parts has all vertices in a tiny tiny area in uv space.
5. disable the shape key merged, or set to 0, or delete.
6. I can post a node setup, but you probably get it. By using a procedural texture in uv space, you get that loose part randomness you wanted.