Skip to main content
added 342 characters in body
Source Link
brockmann
  • 12.8k
  • 4
  • 51
  • 94

TLDR: You can create one material per polygon and assign all the materials using(strictly speaking: the respective index of each material slot) to MeshPolygon.material_index.

I'd suggest create a dictionarycreate a dictionary based on the actual face id and the image path beforehand (// prefix is a Blender specific identifier for the current blend file):

images_per_polygon = {
    0: "city"//city.exr",
    1: "courtyard"//courtyard.exr",
    2: "forest.exr",
    3: "interior.exr",
    4: "night"//forest.exr",
    5: "studio.exr"
    ...
}

Then iterate through all Mesh.polygons, create a material based on the current index, create the relevantadd all nodes, load and assign the image to the Image Texture node using BlendDataImages.load() and assign each material to the polygons properlyrespective face/polygon id to MeshPolygon.material_index.

Demo on how to setup one image per polygon using the default cube and the built-in hdris based on How to load an image from disc and assign it to a newly created image texture node? and How can I get the filepaths of all default/built-in hdris?How to assign a new material to an object in the scene from Python? assuming the following folder structure (6 faces hence 6 images).(6 faces hence 6 images):

main_folder/
├── file.blend/
├── textures
    ├── city.exr
    ├── courtyard.exr
    ├── forest.exr
    ...
import bpy

# Get a reference to the object in context
# -> https://blender.stackexchange.com/a/239102/
obj = bpy.context.object

# Create the dictdictionary
# polygon id: "/path/to/current/blend/textures/image.exr"
images_per_polygon = {
    0: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/worldtextures/city.exr",
    1: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/worldtextures/courtyard.exr",
    2: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/worldtextures/forest.exr",
    3: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/worldtextures/interior.exr",
    4: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/worldtextures/night.exr",
    5: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/worldtextures/studio.exr"
}

# Remove all material slots from the object
# -> https://blender.stackexchange.com/a/146719/
obj.active_material_index = 0
for i in range(len(obj.material_slots)):
    bpy.ops.object.material_slot_remove({'object': obj})

# Create (if not present) the materials and assign them per polygon
for c, p in enumerate(obj.data.polygons):
    image = images_per_polygon.get(c)
    if image:
        # Create the material based on the name if not present 
        # -> https://blender.stackexchange.com/a/23434
        mat_name = "Mat{}".format(c)
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(mat_name)
        
        # Append the materials to the slots and assign each per polygon
        obj.data.materials.append(mat)
        p.material_index = c
        
        # Material Properties
        # -> https://blender.stackexchange.com/q/23436/31447
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        nodes.clear()
        
        # Create Principled Shader node
        node_principled = nodes.new(type='ShaderNodeBsdfPrincipled')
        node_principled.location = 0,0

        # Create Image Texture node
        # -> https://blender.stackexchange.com/a/201414
        node_tex = nodes.new('ShaderNodeTexImage')
        # Assign the image
        node_tex.image = bpy.data.images.load(image)
        node_tex.location = -400,0

        # Create Output node
        node_output = nodes.new(type='ShaderNodeOutputMaterial')   
        node_output.location = 400,0

        # Link all nodes
        links = mat.node_tree.links
        link = links.new(node_tex.outputs["Color"], node_principled.inputs["Base Color"])
        link = links.new(node_principled.outputs["BSDF"], node_output.inputs["Surface"])

        

You can create one material per polygon and assign all the materials using MeshPolygon.material_index.

I'd suggest create a dictionary based on the actual face id and the image path beforehand:

images_per_polygon = {
    0: "city.exr",
    1: "courtyard.exr",
    2: "forest.exr",
    3: "interior.exr",
    4: "night.exr",
    5: "studio.exr"
    ...
}

Then iterate through all Mesh.polygons, create a material based on the current index, create the relevant nodes, load and assign the image using BlendDataImages.load() and assign each material to the polygons properly.

Demo on how to setup one image per polygon using the default cube and the built-in hdris based on How to load an image from disc and assign it to a newly created image texture node? and How can I get the filepaths of all default/built-in hdris? (6 faces hence 6 images).

import bpy

# Get a reference to the object in context
# -> https://blender.stackexchange.com/a/239102/
obj = bpy.context.object

# Create the dict
images_per_polygon = {
    0: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/city.exr",
    1: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/courtyard.exr",
    2: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/forest.exr",
    3: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/interior.exr",
    4: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/night.exr",
    5: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/studio.exr"
}

# Remove all material slots from the object
# -> https://blender.stackexchange.com/a/146719/
obj.active_material_index = 0
for i in range(len(obj.material_slots)):
    bpy.ops.object.material_slot_remove({'object': obj})

# Create (if not present) the materials and assign them per polygon
for c, p in enumerate(obj.data.polygons):
    image = images_per_polygon.get(c)
    if image:
        # Create the material based on the name if not present 
        # -> https://blender.stackexchange.com/a/23434
        mat_name = "Mat{}".format(c)
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(mat_name)
        
        # Append the materials to the slots and assign each per polygon
        obj.data.materials.append(mat)
        p.material_index = c
        
        # Material Properties
        # -> https://blender.stackexchange.com/q/23436/31447
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        nodes.clear()
        
        # Create Principled Shader node
        node_principled = nodes.new(type='ShaderNodeBsdfPrincipled')
        node_principled.location = 0,0

        # Create Image Texture node
        # -> https://blender.stackexchange.com/a/201414
        node_tex = nodes.new('ShaderNodeTexImage')
        # Assign the image
        node_tex.image = bpy.data.images.load(image)
        node_tex.location = -400,0

        # Create Output node
        node_output = nodes.new(type='ShaderNodeOutputMaterial')   
        node_output.location = 400,0

        # Link all nodes
        links = mat.node_tree.links
        link = links.new(node_tex.outputs["Color"], node_principled.inputs["Base Color"])
        link = links.new(node_principled.outputs["BSDF"], node_output.inputs["Surface"])

        

TLDR: You can create one material per polygon and assign all the materials (strictly speaking: the respective index of each material slot) to MeshPolygon.material_index.

I'd suggest create a dictionary based on the actual face id and the image path beforehand (// prefix is a Blender specific identifier for the current blend file):

images_per_polygon = {
    0: "//city.exr",
    1: "//courtyard.exr",
    2: "//forest.exr",
    ...
}

Then iterate through all Mesh.polygons, create a material based on the current index, add all nodes, load and assign the image to the Image Texture node using BlendDataImages.load() and assign the respective face/polygon id to MeshPolygon.material_index.

Demo on how to setup one image per polygon using the default cube and the built-in hdris based on How to load an image from disc and assign it to a newly created image texture node? and How to assign a new material to an object in the scene from Python? assuming the following folder structure (6 faces hence 6 images):

main_folder/
├── file.blend/
├── textures
    ├── city.exr
    ├── courtyard.exr
    ├── forest.exr
    ...
import bpy

# Get a reference to the object in context
# -> https://blender.stackexchange.com/a/239102/
obj = bpy.context.object

# Create the dictionary
# polygon id: "/path/to/current/blend/textures/image.exr"
images_per_polygon = {
    0: "//textures/city.exr",
    1: "//textures/courtyard.exr",
    2: "//textures/forest.exr",
    3: "//textures/interior.exr",
    4: "//textures/night.exr",
    5: "//textures/studio.exr"
}

# Remove all material slots from the object
# -> https://blender.stackexchange.com/a/146719/
obj.active_material_index = 0
for i in range(len(obj.material_slots)):
    bpy.ops.object.material_slot_remove({'object': obj})

# Create (if not present) the materials and assign them per polygon
for c, p in enumerate(obj.data.polygons):
    image = images_per_polygon.get(c)
    if image:
        # Create the material based on the name if not present 
        # -> https://blender.stackexchange.com/a/23434
        mat_name = "Mat{}".format(c)
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(mat_name)
        
        # Append the materials to the slots and assign each per polygon
        obj.data.materials.append(mat)
        p.material_index = c
        
        # Material Properties
        # -> https://blender.stackexchange.com/q/23436/31447
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        nodes.clear()
        
        # Create Principled Shader node
        node_principled = nodes.new(type='ShaderNodeBsdfPrincipled')
        node_principled.location = 0,0

        # Create Image Texture node
        # -> https://blender.stackexchange.com/a/201414
        node_tex = nodes.new('ShaderNodeTexImage')
        # Assign the image
        node_tex.image = bpy.data.images.load(image)
        node_tex.location = -400,0

        # Create Output node
        node_output = nodes.new(type='ShaderNodeOutputMaterial')   
        node_output.location = 400,0

        # Link all nodes
        links = mat.node_tree.links
        link = links.new(node_tex.outputs["Color"], node_principled.inputs["Base Color"])
        link = links.new(node_principled.outputs["BSDF"], node_output.inputs["Surface"])

        
link to "How to assign a new material to an object in the scene from Python?", formatting
Source Link
brockmann
  • 12.8k
  • 4
  • 51
  • 94

Then iterate through all Mesh.polygons, create a material based on the current index, create the relevant nodes, load and assign the image using BlendDataImages.load() and assign each material to the polygons properly.

Demo on how to setup one image per polygon using the default cube (6 faces) and the built-in hdris based on How to load an image from disc and assign it to a newly created image texture node? and How can I get the filepaths of all default/built-in hdris? (6 faces hence 6 images). 

Make sure the filepaths are correct, the object is selected and run the script:

import bpy

C# =Get bpy.a reference to the object in context
# -> https://blender.stackexchange.com/a/239102/
obj = Cbpy.context.object 

# Create the dict
images_per_polygon = {
    0: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/city.exr",
    1: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/courtyard.exr",
    2: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/forest.exr",
    3: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/interior.exr",
    4: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/night.exr",
    5: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/studio.exr"
}

# Remove all material slots from the object
# -> https://blender.stackexchange.com/a/146719/
obj.active_material_index = 0
for i in range(len(obj.material_slots)):
    bpy.ops.object.material_slot_remove({'object': obj})

# Create (if not present) and assign the materials and assign them per polygon
for c, p in enumerate(obj.data.polygons):
    image = images_per_polygon.get(c)
    if image:
        # Create the material based on the name if not present 
        # -> https://blender.stackexchange.com/a/23434
        mat_name = "Mat{}".format(c)
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(mat_name)
        
        # Append the materialmaterials to the slots and assign each per polygon
        obj.data.materials.append(mat)
        p.material_index = c
        
        # Material Properties
        # -> https://blender.stackexchange.com/q/23436/31447
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        nodes.clear()
        
        # Create Principled Shader node
        node_principled = nodes.new(type='ShaderNodeBsdfPrincipled')
        node_principled.location = 0,0

        # Create Image Texture node
        # -> https://blender.stackexchange.com/a/201414
        node_tex = nodes.new('ShaderNodeTexImage')
        # Assign the image
        node_tex.image = bpy.data.images.load(image)
        node_tex.location = -400,0

        # Create Output node
        node_output = nodes.new(type='ShaderNodeOutputMaterial')   
        node_output.location = 400,0

        # Link all nodes
        links = mat.node_tree.links
        link = links.new(node_tex.outputs["Color"], node_principled.inputs["Base Color"])
        link = links.new(node_principled.outputs["BSDF"], node_output.inputs["Surface"])
 
        
        
     

Further reading:

Then iterate through all Mesh.polygons, create a material based on the current index, create the relevant nodes, load and assign the image using BlendDataImages.load() and assign each material properly.

Demo on how to setup one image per polygon using the default cube (6 faces) and the built-in hdris based on How to load an image from disc and assign it to a newly created image texture node? and How can I get the filepaths of all default/built-in hdris?. Make sure the filepaths are correct, the object is selected and run the script:

import bpy

C = bpy.context
obj = C.object

images_per_polygon = {
    0: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/city.exr",
    1: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/courtyard.exr",
    2: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/forest.exr",
    3: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/interior.exr",
    4: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/night.exr",
    5: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/studio.exr"
}

# Remove all material slots from the object
obj.active_material_index = 0
for i in range(len(obj.material_slots)):
    bpy.ops.object.material_slot_remove({'object': obj})

# Create (if not present) and assign the materials them per polygon
for c, p in enumerate(obj.data.polygons):
    image = images_per_polygon.get(c)
    if image:
        # Create the material based on the name if not present
        mat_name = "Mat{}".format(c)
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(mat_name)
        
        # Append the material to the slots and assign each per polygon
        obj.data.materials.append(mat)
        p.material_index = c
        
        # Material Properties
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        nodes.clear()
        
        # Create Principled Shader node
        node_principled = nodes.new(type='ShaderNodeBsdfPrincipled')
        node_principled.location = 0,0

        # Create Image Texture node
        node_tex = nodes.new('ShaderNodeTexImage')
        # Assign the image
        node_tex.image = bpy.data.images.load(image)
        node_tex.location = -400,0

        # Create Output node
        node_output = nodes.new(type='ShaderNodeOutputMaterial')   
        node_output.location = 400,0

        # Link all nodes
        links = mat.node_tree.links
        link = links.new(node_tex.outputs["Color"], node_principled.inputs["Base Color"])
        link = links.new(node_principled.outputs["BSDF"], node_output.inputs["Surface"])
 
        
        
     

Further reading:

Then iterate through all Mesh.polygons, create a material based on the current index, create the relevant nodes, load and assign the image using BlendDataImages.load() and assign each material to the polygons properly.

Demo on how to setup one image per polygon using the default cube and the built-in hdris based on How to load an image from disc and assign it to a newly created image texture node? and How can I get the filepaths of all default/built-in hdris? (6 faces hence 6 images). 

Make sure the filepaths are correct, the object is selected and run the script:

import bpy

# Get a reference to the object in context
# -> https://blender.stackexchange.com/a/239102/
obj = bpy.context.object 

# Create the dict
images_per_polygon = {
    0: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/city.exr",
    1: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/courtyard.exr",
    2: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/forest.exr",
    3: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/interior.exr",
    4: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/night.exr",
    5: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/studio.exr"
}

# Remove all material slots from the object
# -> https://blender.stackexchange.com/a/146719/
obj.active_material_index = 0
for i in range(len(obj.material_slots)):
    bpy.ops.object.material_slot_remove({'object': obj})

# Create (if not present) the materials and assign them per polygon
for c, p in enumerate(obj.data.polygons):
    image = images_per_polygon.get(c)
    if image:
        # Create the material based on the name if not present 
        # -> https://blender.stackexchange.com/a/23434
        mat_name = "Mat{}".format(c)
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(mat_name)
        
        # Append the materials to the slots and assign each per polygon
        obj.data.materials.append(mat)
        p.material_index = c
        
        # Material Properties
        # -> https://blender.stackexchange.com/q/23436/31447
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        nodes.clear()
        
        # Create Principled Shader node
        node_principled = nodes.new(type='ShaderNodeBsdfPrincipled')
        node_principled.location = 0,0

        # Create Image Texture node
        # -> https://blender.stackexchange.com/a/201414
        node_tex = nodes.new('ShaderNodeTexImage')
        # Assign the image
        node_tex.image = bpy.data.images.load(image)
        node_tex.location = -400,0

        # Create Output node
        node_output = nodes.new(type='ShaderNodeOutputMaterial')   
        node_output.location = 400,0

        # Link all nodes
        links = mat.node_tree.links
        link = links.new(node_tex.outputs["Color"], node_principled.inputs["Base Color"])
        link = links.new(node_principled.outputs["BSDF"], node_output.inputs["Surface"])

        

Further reading

Source Link
brockmann
  • 12.8k
  • 4
  • 51
  • 94

You can create one material per polygon and assign all the materials using MeshPolygon.material_index.


I'd suggest create a dictionary based on the actual face id and the image path beforehand:

images_per_polygon = {
    0: "city.exr",
    1: "courtyard.exr",
    2: "forest.exr",
    3: "interior.exr",
    4: "night.exr",
    5: "studio.exr"
    ...
}

Then iterate through all Mesh.polygons, create a material based on the current index, create the relevant nodes, load and assign the image using BlendDataImages.load() and assign each material properly.

enter image description here

Demo on how to setup one image per polygon using the default cube (6 faces) and the built-in hdris based on How to load an image from disc and assign it to a newly created image texture node? and How can I get the filepaths of all default/built-in hdris?. Make sure the filepaths are correct, the object is selected and run the script:

import bpy

C = bpy.context
obj = C.object

images_per_polygon = {
    0: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/city.exr",
    1: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/courtyard.exr",
    2: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/forest.exr",
    3: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/interior.exr",
    4: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/night.exr",
    5: "/Applications/Blender.app/Contents/Resources/2.93/datafiles/studiolights/world/studio.exr"
}

# Remove all material slots from the object
obj.active_material_index = 0
for i in range(len(obj.material_slots)):
    bpy.ops.object.material_slot_remove({'object': obj})

# Create (if not present) and assign the materials them per polygon
for c, p in enumerate(obj.data.polygons):
    image = images_per_polygon.get(c)
    if image:
        # Create the material based on the name if not present
        mat_name = "Mat{}".format(c)
        mat = bpy.data.materials.get(mat_name)
        if not mat:
            mat = bpy.data.materials.new(mat_name)
        
        # Append the material to the slots and assign each per polygon
        obj.data.materials.append(mat)
        p.material_index = c
        
        # Material Properties
        mat.use_nodes = True
        nodes = mat.node_tree.nodes
        nodes.clear()
        
        # Create Principled Shader node
        node_principled = nodes.new(type='ShaderNodeBsdfPrincipled')
        node_principled.location = 0,0

        # Create Image Texture node
        node_tex = nodes.new('ShaderNodeTexImage')
        # Assign the image
        node_tex.image = bpy.data.images.load(image)
        node_tex.location = -400,0

        # Create Output node
        node_output = nodes.new(type='ShaderNodeOutputMaterial')   
        node_output.location = 400,0

        # Link all nodes
        links = mat.node_tree.links
        link = links.new(node_tex.outputs["Color"], node_principled.inputs["Base Color"])
        link = links.new(node_principled.outputs["BSDF"], node_output.inputs["Surface"])

        
        
    

Further reading: