1
$\begingroup$

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.

$\endgroup$
2
$\begingroup$

This is a known limitation of the render border as described in bug report T46166 (reported by me):

Rendering only takes into account the render border of the current scene when compositing.

Sergey Sharybin (a Blender developer) says:

This is workign as designed and expected and in fact it's the only wayt you can have usable border render for scenarios when your scene is split into several render layers/scenes for the performance reasons.. For example, you might want ot render volumes in BI, rest of geometry in Cycles and combine them together. With your suggestion you'll need to change render border in every scene every time you want ot preview different area of your frame.

Soe thanks for the report, but it's not considered a bug.

So, to summarise, as you have already seen, only the render border of the current scene is used.

To quote myself from the bug report, a partial solution, which may be applicable to you:

This can be fixed, but only by pressing the render button on the render layer node for the scene that rendered incorrectly. When I do that, it uses the correct render border for that scene.

Here I am referencing the render icon on the render layer node:

enter image description here

This re-renders the render layer and should use the correct render border. You would have to press this on each render layer node to get it to render.

Another potential way around this would be to do the rendering in your code by doing separate renders for each scene. Looping through all the scenes you want to render, you would first set the current scene to the scene you want to render, set the render border and then render it. You may want/need to save out the renders to the hard drive and then read all the images into the compositor at the end.

(It's a little hard to read your code, so you may already be doing some of this.)

$\endgroup$

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.