I'm working on a script to access and manipulate render passes directly in memory without saving them to disk. My goal is to obtain and process different render passes like the beauty pass and mist pass.
Here's a simplified version of my script:
import bpy
import numpy as np
import cv2
def convert_blender_pass(blender_image, dimension=1):
# Get the pixel data from the Blender image
pixels = np.array(blender_image.pixels) # This is a flattened array of float64 type
# Get image dimensions
width = blender_image.size[0]
height = blender_image.size[1]
# Reshape and convert
image = np.reshape(pixels, (height, width, 4))
if dimension == 1:
values = np.flipud(image[:, :, 0]) # The values of the three channels are the same
elif dimension == 3:
values = np.flipud(image[:, :, :3]) # The values of the three channels are distinct
alpha = np.flipud(image[:, :, 3]) # Alpha channel
return values, alpha
bpy.context.scene.use_nodes = True # Enable the compositor
tree = bpy.context.scene.node_tree
# Clear existing nodes
for node in tree.nodes:
tree.nodes.remove(node)
# Create Render Layers node
rl_node = tree.nodes.new('CompositorNodeRLayers')
# Create Viewer nodes
# Create Beauty pass
image = tree.nodes.new('CompositorNodeViewer')
image.name = 'Beauty Pass'
image.label = 'Viewer Image'
tree.links.new(rl_node.outputs['Image'], image.inputs[0])
# Create Mist pass
bpy.context.scene.view_layers['ViewLayer'].use_pass_mist = True # Enable the Mist pass
mist = tree.nodes.new('CompositorNodeViewer')
mist.name = 'Mist Pass'
mist.label = 'Viewer Mist'
tree.links.new(rl_node.outputs['Mist'], mist.inputs[0])
# Render the scene
bpy.ops.render.render()
# Display the rendered images
# Beauty pass
image_viewer = bpy.data.images['Viewer Node'] # 'Beauty Pass' is active by default.
values, _ = convert_blender_pass(image_viewer, dimension=3)
values = values * 255
values = values.astype(np.uint8)
opencv_image = cv2.cvtColor(values, cv2.COLOR_BGR2RGB)
cv2.imshow('Converted Image', opencv_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Mist pass
tree.nodes.active = mist # Set the node as the active node
bpy.ops.render.render() # Render again
mist_viewer = bpy.data.images['Viewer Node'] # Get the rendered image
values, _ = convert_blender_pass(mist_viewer, dimension=1)
normalized = cv2.normalize(values, None, 0, 255, cv2.NORM_MINMAX)
opencv_image = normalized.astype(np.uint8)
cv2.imshow('Converted Image', opencv_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
I successfully retrieved the beauty pass by adding and connecting a viewer node to the render layer node. However, I encountered difficulties when trying to access other passes, such as the mist pass. To work around this, I set the active node to the mist viewer node and rendered again to get the mist pass. This approach is inefficient as it requires multiple renders to access different passes, despite them all being generated in a single render.
Note: You can toggle commenting out the second bpy.ops.render.render()
call to see how it gives a wrong result if the scene is not re-rendered. (Only the second OpenCV window matters here. The first window will probably plot a black screen. )
Question: Is there a way to access the different render passes (e.g., mist pass) directly from the viewer nodes without performing multiple renders? Ideally, I would like to retrieve all passes in one render operation without saving to disk.
Any advice or alternative methods to achieve this would be greatly appreciated. Thank you!