6
$\begingroup$

Related to this: Baking textures on headless machine (batch baking)

Is there a way to batch the baking of textures over all frames? I have an animation and since the light is changing at each frame, would like to bake out the texture of each frame into an image sequence.

  1. I've set my render mode to cycles
  2. I've added a Image Texture Node and have it set to image sequence
  3. My UV is unwrapped to the image sequence

If I put this in console, I can bake one frame to the sequence:

bpy.data.objects["Cube"].select = True
bpy.context.scene.frame_current = 1
bpy.ops.object.bake(type='COMBINED')

Similarly, I can bake another frame:

bpy.data.objects["Cube"].select = True
bpy.context.scene.frame_current = 2
bpy.ops.object.bake(type='COMBINED')

However, when I try to put this in a for loop to bake multiple frames like this:

for i in range(1,30):
     bpy.context.scene.frame_current = i
     bpy.data.objects["Cube"].select = True
     bpy.ops.object.bake(type='COMBINED')

It will only bake the texture of the first frame in the loop. It appears like it is looping through all the frames and it is baking them, but the images in those frames do not get updated once the script is finished. Any ideas on what is happening?

$\endgroup$
5
+100
$\begingroup$

Since 'Animation Baking' is now officially supported by this Add-on (shipped with Blender by default), you can simply call bpy.ops.object.anim_bake_image() for a sequence:

import bpy

scn = bpy.context.scene
scn.animrenderbake_start = 3
scn.animrenderbake_end = 30
bpy.ops.object.anim_bake_image('INVOKE_DEFAULT')

The following script is a slightly modified version of the Add-on in order to bake specific frames (for demonstration purposes). To bake all frames (or a custom range) use the range() function.

import bpy
import os
import shutil

# set the frames to bake
bake_frames = [0, 230, 99]

# format frame number
def framefile(filepath, frame):
    fn, ext = os.path.splitext(filepath)
    return "%s_%04d%s" % (fn, frame, ext)

# scene attributes
context = bpy.context
is_cycles = (context.scene.render.engine == 'CYCLES')
selected = context.selected_objects
img = None

# inspect the materials
if is_cycles:
    inactive = None
    selected = None
    for mat_slot in context.active_object.material_slots:
        mat = mat_slot.material
        if not mat or not mat.node_tree:
            continue
        trees = [mat.node_tree]
        while trees and not img:
            tree = trees.pop()
            node = tree.nodes.active
            if node.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'}:
                img = node.image
                break
            for node in tree.nodes:
                if node.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'} and node.image:
                    if node.select:
                        if not selected:
                            selected = node
                    else:
                        if not inactive:
                            inactive = node
                elif node.type == 'GROUP':
                    trees.add(node.node_tree)
        if img:
            break
    if not img:
        if selected:
            img = selected.image
        elif inactive:
            img = inactive.image
else:
    for uvtex in context.active_object.data.uv_textures:
        if uvtex.active_render == True:
            for uvdata in uvtex.data:
                if uvdata.image is not None:
                    img = uvdata.image
                    break

# convert the path to absolute path
img_filepath_abs = bpy.path.abspath(img.filepath, library=img.library)

# iterate through the frame list
for f in bake_frames:
    print("Baking frame %d" % f)

    # set the frame
    context.scene.frame_set(f)

    # cycles bake 
    if is_cycles:
        bpy.ops.object.bake()

    # internal bake
    else:
        bpy.ops.object.bake_image()

    # save the image
    img.save()
    img_filepath_new = framefile(img_filepath_abs, f)

    # copy the image
    shutil.copyfile(img_filepath_abs, img_filepath_new)
    print("Saved %r" % img_filepath_new)
$\endgroup$
  • $\begingroup$ How to make it work with image selection? If I use images without sequence it just bakes the first frame. $\endgroup$ – Denis Jan 3 '18 at 16:37
  • $\begingroup$ Don't get it, sorry @Denis. The addon basically bakes an image for every frame in the timeline (per object), so what do you mean by images without a sequence? Can you elaborate on what are you trying to do? $\endgroup$ – p2or Jan 3 '18 at 17:24
  • $\begingroup$ I have images that are named differently without suffixes 001 002 etc.., so if I load them as a sequence only the first image is baked since the next image has a different name. $\endgroup$ – Denis Jan 3 '18 at 17:27
  • $\begingroup$ And those images you'd like to plug into a shader via Image Texture node to get another image every frame, right? If yes, I think that's not supported, the node only accepts a sequence. Why you can't just rename the images into a real sequence with any batch-rename tool like MuCommander or even TotalCommander? Otherwise you need to tell blender which image to load at frame X or frame Y into the node via scripting and some handler or something. I think that isn't really worth it @Denis $\endgroup$ – p2or Jan 3 '18 at 17:55
  • $\begingroup$ I have a lot of images and many of them look very similar and I need the names so they dont get confused, ideally I would need the image file name to be added as a suffix or prefix of baked texture file name, but at least I need the baked textures to have the same order as the images. $\endgroup$ – Denis Jan 3 '18 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.