enter image description hereenter image description hereenter image description hereenter image description hereenter image description here> ### AI Icon Warning, Potential AI generated content⚠️

This post is suspected to have been generated with the help of an artificial intelligence, chat bot, or other language model tools without proper attribution.

There is reason to believe this post was not written by a human due to its structure, and grammatical construction, and has been marked by our community.
Current AI chat bots are a language models, that means they are like a parrot, they can mimic human language well, but they are built to write seemingly logical, seemingly factual grammatically valid text, rather than actually be correct, like a human would.
The veracity of the following content cannot be ascertained. See the Concerns regarding AI generated content.

Ive been trying to learn Blender for a few weeks and Ive put lots of late nights and early starts in. The more I learn about Blender the more I realise how little know!

I am trying to create a scene that I can use as input to a batch render process. I am trying to get the batch process to work before I refine the scene to a finished look. When I start rendering I want the colour of the fire surround to cycle through say 10 different colours/textures.

For each colour fire surround, I want to render with a different set of three flame jpgs, its important to know that the flames change/animate as the camera pans around the fireplace. Potentially I might have 12 sets of three jpg flames. Therefore I would produce 120 mpeg files of around 10 seconds long each.

I also have a small problem in that shadows from the fire surround are not cast on the images of flames which I will need for when there is no fire or small flames. From reading, I think this may be because the picture is "emissive"? How can I fix this, please?

The Scene is attached ( I think with all supported files embedded)

  1. How can I make a batch script? (I have attached a ChatGPT attempt below )
  2. How can shadows be cast inside the fire surround?

I am happy to share the scene.

# Define your colors (RGB tuples from 0 to 1)
colors = [
    (1, 0, 0), # Red
    (0, 1, 0), # Green
    (0, 0, 1), # Blue
    # Add more colors as needed

# Path to the JPEG files, assuming they're named sequentially
jpeg_paths = [f"\flames{i}.jpg" for i in range(1, 3)]
print (jpeg_paths)

# Reference to the object you want to color
color_object = bpy.data.objects['Cube.001']
# Reference to the plane object to which you want to apply JPEG textures
texture_object = bpy.data.objects['Picture']

for color in colors:
    # Apply color to the color_object
    if color_object.data.materials:
        material = color_object.data.materials[0]
        material = bpy.data.materials.new(name="ObjectMaterial")
    material.diffuse_color = color + (1,)  # RGB + Alpha

    for jpeg_path in jpeg_paths:
        # Load the JPEG as a texture
        bpy.ops.image.open(filepath=jpeg_path, directory="/", files=[{"name":jpeg_path}], relative_path=True, show_multiview=False)
        # Assuming the plane uses a material that has an Image Texture node
        # Set the image of the texture node to the loaded image
        texture_material = texture_object.data.materials[0]  # Assuming it has a material
        for node in texture_material.node_tree.nodes:
            if node.type == 'TEX_IMAGE':
                node.image = bpy.data.images[jpeg_path.split('/')[-1]]
        # Render settings
        bpy.context.scene.render.filepath = f'/path/to/output/{color}_{jpeg_path.split("/")[-1].split(".")[0]}'
        bpy.ops.render.render(write_still=True)  # Render the scene


enter image description here

enter image description here

  • $\begingroup$ Chat GPT is pretty much useless for coding with Blender's Python API beyond most basic stuff. You can forget about it. $\endgroup$ Commented Feb 4 at 23:26
  • $\begingroup$ OK thanks for the heads up on ChatGPT. I will forget about it for now. There are 3 jpgs that different when you look at them straight on, from the left, from the right. when I work out how to post the .blend file here I will. do you upload or have to post a link to the file hosted somewhere else? $\endgroup$
    – Mikeyhhh
    Commented Feb 4 at 23:28
  • $\begingroup$ So you have scene with animation and you have an object that uses some sort of material with a texture and you want to render that animation with the texture replaced, right? $\endgroup$ Commented Feb 4 at 23:29
  • $\begingroup$ Not quite, I have a scene, it changes the images according to where the camera is, i.e at which angle you look at it from. Please see attached screen shots... $\endgroup$
    – Mikeyhhh
    Commented Feb 4 at 23:42
  • $\begingroup$ I have no idea what those shader nodes are supposed to do to be honest. Maybe let's start from there - what is the shader supposed to do exactly and why? Why do you need such a shader? $\endgroup$ Commented Feb 4 at 23:55

1 Answer 1


OK, so the weird part about rendering in Blender from a script is that rendering is a complex multithreaded process possibly happening on multiple devices, so it's sort of difficult to know when the rendering job is done inside a script and to make it wait before executing anything after render process is started. So this does have to happen inside a modal operator with a bunch of application handlers. Confusing right? I know... Well, it is what it is. Thanks to this amazing answer(let's upvote it!) it's possible to figure out how to do that.

So in order for my script to work unmodified, you have to prepare the .blend file and your folder structure in a certain way:

  1. You have to place the .blend file in a folder where you also have another folder called Variations:

enter image description here

  1. You have to make sure that the images you want to replace with variations are called exactly the same as folders in that Variations folder and put your variation images in them.


You can also see the image names in the Outliner, Blender File mode:

enter image description here

Image data-block name can be anything regardless of the image path.

So your Variations folder should contain folders named same as your image data-blocks and there(in those folders) you should have images for those data-blocks.

For example: I have Variations folder containing folders named Sphere and Suzanne and some image textures for them(2 in one, 3 in another). I also named them in a reasonable manner, because the script will use those names for render output. They are images filled with single color only for the sake of simplicity.

enter image description here

This is how my folder structure looks:

enter image description here

And here is the script that you can copy to the Text Editor of your file and run from it:

import bpy
import os
import itertools

class Multi_Render(bpy.types.Operator):
    """Renders multiple times"""
    bl_idname = "render.multi"
    bl_label = "Render multiple times"
    _timer = None
    shots = None
    stop = None
    rendering = None
    path = bpy.path.abspath("//Render//")
    v_dir = bpy.path.abspath("//Variations")
    def pre(self, scene, context=None):
        self.rendering = True 
    def post(self, scene, context=None):
        self.shots.pop(0) #this removes last variation from the list
        self.rendering = False

    def cancelled(self, scene, context=None):
        self.stop = True

    def execute(self, context):
        self.stop = False
        self.rendering = False
        # Prepare all variations for rendering 
        # let's make a list of image variation lists(so a list of all lists)
        variations = []
        for folder in os.listdir(self.v_dir):
            image_variations = []
            for file in os.listdir(os.path.join(self.v_dir,folder)):
                image_variations.append(os.path.join(self.v_dir,folder, file)) 
        # Let's convert it to a list of all variations
        self.shots = list(itertools.product(*variations))
        # Carry on with other stuff making this work
        context.scene.render.filepath = self.path
        self._timer = context.window_manager.event_timer_add(1, window=context.window)
        return {"RUNNING_MODAL"}
    def modal(self, context, event):
        if event.type == 'TIMER':
            if True in (not self.shots, self.stop is True): 
                return {"FINISHED"}
            elif self.rendering is False: 
                # Changes happen here: 
                paths = self.shots[0]
                sc = context.scene
                names = [os.path.splitext(os.path.basename(f))[0] for f in paths]
                sc.render.filepath = self.path + '-'.join(names) + " "
                for path in paths:
                    image = os.path.basename(os.path.dirname(path)) # folder name
                    bpy.data.images[image].filepath = path
                # now just render
                bpy.ops.render.render("INVOKE_DEFAULT", animation=True,write_still=True)

        return {"PASS_THROUGH"}


Like I mentioned it is weird, because it is wrapped in a modal operator, but there are 2 main parts: The one starting at line 29 where you prepare a list of variations and the one starting at line 59 where all the changes happen per iteration. It is using a list and every time an item is rendered in that list it is removed by render_complete application handler at line 19. This is the only way(at least I know of) to have the render visible in the UI, which from my experience is sort of important when you render stuff and want to see if it's going OK(so pretty much every single time)

Once you hit run in the Text Editor, it will start rendering and output your render to the same folder the .blend file is in a Render subfolder:

enter image description here

I have an animation of only 2 frames and output set to .png so it outputs frames separately. It names them according the texture names.

This should work with more variations as you can add more folders matching image data-blocks.

Note that I did not write any checks whatsoever for errors. So you should only have folders and valid images in those folders and names must match existing image data-blocks, other way it will fail in ways I have not tested. The idea is if you like it, you should probably write your own checks (I spent way too much time on this already, but don't get me wrong, it was fun and educational) ...and of course I assume you would modify it further according your needs on your own.

If you have sets of images, I think it might be easier to just combine every set into one image for this, although, it's also possible to load a folder of images as one variation, but I would just combine them to be honest and split them with UV map or any other way in the shader.

  • $\begingroup$ Ive not had chance to read or test this yet... but totally awesome effort (: thank you so much for having a look at this. I think I would have been working on this for several months... will read, test and get back to you. $\endgroup$
    – Mikeyhhh
    Commented Feb 6 at 10:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .