# How to merge different coloured materials into single material?

I have imported a .wrl model into 2.8 (via 2.79) that is made up of lots of individual objects, each with an assigned coloured material.

All objects that are the same colour share a single material. If I join all these separate objects into one, the materials all transfer and I retain the correct colours, however I now want to modify these materials in order to add different normal/roughness etc. maps.

I cannot add these maps individually to each coloured material as there are potentially hundreds of different materials. So I need a method of merging all the colour data into one material which I can then change the other aspects of without the individual vertices losing their colour information.

The model was originally exported from UCSF Chimera. I can use either Blender 2.79 or 2.8. I would prefer to have the whole model as one object but would be ok with it remaining as separate objects. Colours must remain exactly the same and in the exact same place (very important, no manual recolouring).

• Just to make sure, you want ALL the parts to have the SAME material, right? – Ben Mar 20 at 15:23
• Basically I want to be able to easily play around with different material settings while retaining the colour data. My thought would be that having everything be the same material would be the easiest way to do that but if you have another idea then please let me know. – Thallium Mar 20 at 15:28

This might serve as a kick-off. If the multiple materials are simple enough, you could bake the diffuse color component of those materials into a vertex-color layer of your meshes, and then access those colors through an Attribute node in your universal material.

This script writes the various materials' diffuse colors into a vertex-color layer named 'dif_color' of all selected mesh-objects, as far as I've tested it, in 2.79:

import bpy

def lin2srgb(rgb_color):
rgba=[0,0,0,1]
a = 0.055
for c, x in enumerate(rgb_color):
if x <= 0.0031308:
rgba[c] =  x * 12.92
else:
rgba[c] = (1 + a) * pow(x, 1 / 2.4) - a
return rgba

context = bpy.context
layer_name = 'dif_color'

for obj in context.selected_objects:
if obj.type == 'MESH':
me = obj.data
if not layer_name in me.vertex_colors:
me.vertex_colors.new(name=layer_name)
dif_layer =  me.vertex_colors[layer_name]

for poly in me.polygons:
slot = obj.material_slots[poly.material_index]
mat = slot.material
if mat is not None:
# convert from  RGB to sRGB,A..
sRGB_col = lin2srgb(mat.diffuse_color)
for loop_idx in poly.loop_indices:
dif_layer.data[loop_idx].color = sRGB_col


Cut and paste this into a text-block, select the objects with multiple materials, and run the script.

Then you could extend this node tree, (which picks up the diffuse colors from the vertex color layer through the Attribute node) to do anything else you like with other attributes of your merged material.

If you've got a ton of stuff to deal with, it would be more economical to make a dictionary of materials > sRGB colors first. to avoid the calculation in the loop.. but this is just a one-shot, so that shouldn't matter too much.

I fully expect corrections to come from people who know better than me..

• Thanks for the code, had to make a slight adjustment to the last line so that it read dif_layer.data[loop_idx].color = sRGB_col[0:3] as it was returning an error saying Traceback (most recent call last): File "\mergematerials.py", line 31, in <module> ValueError: bpy_struct: item.attr = val: sequences of dimension 0 should contain 3 items, not 4 Error: Python script fail, look in the console for now... without the [0:3]. I guess it didn't like you trying to include the alpha channel. Anyway, thanks very much, works as intended. – Thallium Mar 21 at 15:07
• @Thallium Thanks for the heads-up .. In my build of 279 vertex colours insist on the alpha, so, when writing, I got caught out the other way round! Which build are you running? Maybe I should edit for more recent builds.. hard to say. You could just edit lin2srgb() .. rgb = [0,0,0] for rgba = [0,0,0,1] – Robin Betts Mar 21 at 15:19
• It was a pretty old build of 2.79 to be fair, from 2017. I've just tried your code in 2.8 and it works un-altered, so I assume it will work fine in newer versions of 2.79 as well. – Thallium Mar 21 at 15:46