7
$\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$
1

2 Answers 2

8
+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$
16
  • $\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, 2018 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, 2018 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, 2018 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, 2018 at 17:55
  • 2
    $\begingroup$ Had a bit time today and converted that into a real addon, which you can find here. Once it's enabled, you'll find a new panel in the misc area of the node editor's tool shelf, if you select any Image Texture node: i.stack.imgur.com/QiuDN.jpg In case you want to get the same output file names after baking, we need to modify the other addon. Hope that helps, any feedback is appreciated @Denis $\endgroup$
    – p2or
    Jan 4, 2018 at 19:47
0
$\begingroup$

Didn't embark on using solution above so if someone might find use in this, for me at least simpler approach, tho above one seems detailed and generalized I assume but I don't know, so here you go

As I would standardly do, after selecting object and Image Texture in material (not connected texture or it really doesn't matter) instead of clicking Bake in Cycles option

I run script now for EMIT option, you can switch to COMBINED or whichever one you'd want I assume ("Untitled.004" is image I created before that is in Image Texture) and ofc u can switch the path from the mentioned one:

import bpy

for i in range(1, 151):
    for object in bpy.context.selected_objects:
        bpy.context.scene.frame_set(i)
        bpy.ops.object.bake(type='EMIT')
        name='C:\\path'+'textureemission'+str(i)+'.png'
        bpy.data.images["Untitled.004"].save(filepath=name)
$\endgroup$

You must log in to answer this question.

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