I have written a script that splits up a render into small blocks, each in their own scene, and then composites those blocks into the final image at full resolution. I originally wrote this script so that it makes a separate .blend file for each block. This, however, was proving to be very time and space consuming, so I wanted to find a way to make it work with a single .blend file.
I have it almost completely but am now running into an issue.
If you render a scene with a render border, and this scene is being used by a RenderLayer compositing node, the render border gets overridden by the border of the scene the compositor is in.
import bpy
import os
factor = 2
blocks = factor**2
blockno = 0
unit = 1/factor
blist = list(range(blocks))
startFrame = bpy.context.scene.frame_start
endFrame = bpy.context.scene.frame_end
xres = bpy.context.scene.render.resolution_x
yres = bpy.context.scene.render.resolution_y
pres = bpy.context.scene.render.resolution_percentage
currentSceneName = bpy.context.scene.name
csn = currentSceneName
blendlocation = bpy.data.filepath
blendlocation = os.path.splitext(blendlocation)[0]
filename = bpy.path.basename(bpy.data.filepath)
filename = os.path.splitext(filename)[0]
newSceneName = csn + " Block 001"
for block in range(blocks):
if block == blockno:
blockstr = str(block +1)
bpy.ops.scene.new(type='LINK_OBJECTS')
newSceneName = csn + " Block " + blockstr.zfill(3)
bpy.context.scene.name = newSceneName
bpy.context.scene.render.filepath = blendlocation + "_BLOCKED\\" + filename + "_BLOCK_" + blockstr.zfill(3) +"_"
min_x = block/factor%1
max_x = block/factor%1 + unit
min_y = int(block/factor)*unit
max_y = int(block/factor)*unit + unit
bpy.data.scenes[newSceneName].render.use_border = True
bpy.data.scenes[newSceneName].render.border_min_x = min_x
bpy.data.scenes[newSceneName].render.border_max_x = max_x
bpy.data.scenes[newSceneName].render.border_min_y = min_y
bpy.data.scenes[newSceneName].render.border_max_y = max_y
blockno = blockno + 1
blockno=1
bpy.ops.scene.new(type='NEW')
bpy.context.scene.render.filepath = blendlocation + "_BLOCKED\\" + filename + "_BLOCK_COMP_"
bpy.context.scene.frame_start = startFrame
bpy.context.scene.frame_end = endFrame
bpy.context.scene.render.resolution_x = xres
bpy.context.scene.render.resolution_y = yres
bpy.context.scene.render.resolution_percentage = pres
bpy.context.scene.name = csn +" Compositor"
#bpy.context.scene.render.layers["RenderLayer"].use = False
bpy.data.scenes[csn +" Compositor"].render.use_border = False
bpy.data.scenes[csn +" Compositor"].render.use_compositing = True
bpy.context.scene.view_settings.view_transform = 'Default'
bpy.context.scene.view_settings.exposure = 0.0
bpy.context.scene.view_settings.gamma = 1.0
bpy.context.scene.view_settings.look = 'None'
bpy.context.scene.view_settings.view_transform = 'Default'
bpy.context.scene.use_nodes = True
tree = bpy.context.scene.node_tree
# clear default nodes
for node in tree.nodes:
tree.nodes.remove(node)
#tree.nodes.new(type='CompositorNodeRLayers')
#bpy.data.scenes['Test Compositor'].node_tree.nodes['Render Layers'].scene.name = "Test"
blocno= 0
for scene in bpy.data.scenes:
if "Block" in scene.name:
if "001" in scene.name:
blockstr = str(blockno)
blockstr = blockstr.zfill(3)
ref= blockno-1
rlNodes=['rlNodes_'+str(i) for i in blist]
rlNodes[ref] = tree.nodes.new(type='CompositorNodeRLayers')
rlNodes[ref].scene = bpy.data.scenes[scene.name]
rlNodes[ref].location = 0,(-400)
rlNodes[ref].width = 250
aoNodes=['aoNodes_'+str(i) for i in blist]
aoNodes[ref] = tree.nodes.new(type='CompositorNodeAlphaOver')
aoNodes[ref].location = 400,(-400)
aoNodes[ref].inputs[1].default_value = (0,0,0,0)
opNodes = tree.nodes.new(type='CompositorNodeOutputFile')
opNodes.file_slots.remove(opNodes.inputs[0])
opNodes.file_slots.new(filename + "_BLOCK_" + blockstr.zfill(3) +"_")
opNodes.base_path = blendlocation + "_BLOCKED\\"
opNodes.label = filename + "_BLOCK_" + blockstr.zfill(3) +"_"
opNodes.location = 800,(-400*blockno)
compNodes = tree.nodes.new('CompositorNodeComposite')
compNodes.location = 800,-200
viewerNodes = tree.nodes.new('CompositorNodeViewer')
viewerNodes.location = 800, 0
links = tree.links
links.new(rlNodes[ref].outputs[0], opNodes.inputs[0])
links.new(rlNodes[ref].outputs[0], aoNodes[ref].inputs[2])
links.new(aoNodes[ref].outputs[0], compNodes.inputs[0])
links.new(aoNodes[ref].outputs[0], viewerNodes.inputs[0])
blockno = blockno + 1
else:
blockstr = str(blockno)
blockstr = blockstr.zfill(3)
ref= blockno-1
old = ref-1
rlNodes[ref] = tree.nodes.new(type='CompositorNodeRLayers')
rlNodes[ref].scene = bpy.data.scenes[scene.name]
rlNodes[ref].location = 0,(-400*blockno)
rlNodes[ref].width = 250
opNodes = tree.nodes.new(type='CompositorNodeOutputFile')
opNodes.file_slots.remove(opNodes.inputs[0])
opNodes.file_slots.new(filename + "_BLOCK_" + blockstr.zfill(3) +"_")
opNodes.base_path = blendlocation + "_BLOCKED\\"
opNodes.label = filename + "_BLOCK_" + blockstr.zfill(3) +"_"
opNodes.location = 800,(-400*blockno)
aoNodes[ref] = tree.nodes.new(type='CompositorNodeAlphaOver')
aoNodes[ref].location = 400,(-400*blockno)
aoNodes[ref].inputs[1].default_value = (0,0,0,0)
links.new(rlNodes[ref].outputs[0], opNodes.inputs[0])
links.new(rlNodes[ref].outputs[0], aoNodes[ref].inputs[2])
links.new(aoNodes[ref].outputs[0], aoNodes[old].inputs[1])
blockno = blockno + 1
Try it out for youself. Run this script and then press render in the "Compositor" Scene. You will see that the render ignores the borders of those individual scenes themselves. If you open up the individual "Block" scenes, you can clearly see the render border. Likewise, if you press "Render active scene" on any of the RenderLayer nodes in the Compositor scene, they render with the border. However if you press Render or Animation on the Compositor scene itself, the borders become overridden.
Is this a bug in blender or is there something I am missing?
Full disclosure: I have minimal programming experience and I know that my code isn't the most elegant and can be more efficiently optimized. Everything I know is from playing around with the blender API for my own applications. Any help would be greatly appreciated.