7

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?

uclatommy
  • 95
  • 2
  • 4

2 Answers2

8

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)
p2or
  • 15,860
  • 10
  • 83
  • 143
  • How to make it work with image selection? If I use images without sequence it just bakes the first frame. – Denis Jan 03 '18 at 16:37
  • 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? – p2or Jan 03 '18 at 17:24
  • 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. – Denis Jan 03 '18 at 17:27
  • 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 – p2or Jan 03 '18 at 17:55
  • 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. – Denis Jan 03 '18 at 18:06
  • With the tools I've mentioned, you can sort by date, name or whatever and just append the digits at the end of the filename, see: https://i.stack.imgur.com/SbN25.jpg (TotalCommander, sorted by date, 4 digits). Does this help @Denis? – p2or Jan 03 '18 at 18:17
  • I have tried it, adding a suffix to the names doesnt make the images recognizable as a sequence. – Denis Jan 03 '18 at 18:37
  • Appears to be not supported as well. Here you can find a quick'n dirty frame handler, which allows to exchange the filepath of an image texture node based on a given folder per frame. For now, you need to set the name of your material, the name of your Image texture node and the path to your image folder to make it work. I can't test the baking, since I've got no bake setup at the moment. However it's working fine for me in cycles preview so can you test it in combination with the Anim-Bake-Addon? @Denis – p2or Jan 03 '18 at 20:50
  • I've tested the script in combination with the Addon and it works as expected. Make sure you've got the second (empty) Image Texture node selected before running "Animated Bake" otherwise it's gonna overwrite the image (sure you know that already) @Denis – p2or Jan 03 '18 at 22:18
  • 2
    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: https://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 – p2or Jan 04 '18 at 19:47
  • I think I'm done, updated the addon here. It's a full replacement of Animated Render Baker now. Just disable the old one and install our new 'Sequence Bakery' addon. The addon now allows to bake a sequence like before as well as overriding the filepath of any Image Texture node. If "Override Image Path" is enabled when baking, the filename will be assembled by the original path and the current frame number. You can also find some instructions at the top of the py file to set it up properly. Anyway, does that help @Denis ? – p2or Jan 07 '18 at 16:58
  • Used it for a project today and I think it works quite ok. I'd still appreciate any feedback...? @Denis – p2or Mar 18 '18 at 17:20
  • Sorry for the late reply, I to cancel this job because I couldn't make it on time, the problem is that with cycles the baking is too slow, I couldn't bake just the textures and it didn't work with internal render engine. – Denis Mar 21 '18 at 18:04
  • No problem, just curious @Denis – p2or Mar 21 '18 at 19:02
  • how did this project go? I want to do something similar but basicly batch process the baked images for a specific style. – Sebastian Nebaeus Feb 06 '21 at 16:33
  • Apparently the animation baking add-on is no longer in blender (at least in version 3.5). Is there a third party add-on that does this? – Alexandre Marcati Jul 03 '23 at 18:09
  • Have you already tried: https://gist.github.com/p2or/43194fcab3f42bab9b70ff1cc2b7637a ? @AlexandreMarcati – p2or Jan 02 '24 at 07:58
  • No, but I actually resolved my issue with a different workflow: I use geometry nodes to unwrap and flatten the actual geometry of the object according to the UV map, then I just render it out from a perpendicular camera. It doesn't solve for every situation but for what I needed it did the trick. – Alexandre Marcati Jan 02 '24 at 15:02
0

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)