0
$\begingroup$

I have some automated code below that I have written to set up render nodes for full output, depth, object index (segmentation) and surface normal maps.

I notice the output of the normal map is in World Space. Is there a trivial way of changing the render node of the normal map to camera space?

def PrepareRender(context, resolution, output_dir):
    scene = context.scene

    scene.render.engine = 'CYCLES'
    scene.render.filepath = output_dir


     #Output open exr .exr files
     context.scene.render.image_settings.file_format = 'OPEN_EXR'
     context.scene.render.image_settings.use_zbuffer = True
     context.scene.render.image_settings.color_depth = '16'
     context.scene.render.image_settings.use_preview = False
     context.scene.render.image_settings.exr_codec = 'ZIP'
     scene.cycles.samples = 1

    # Configure renderer to record object index
    scene.view_layers["ViewLayer"].use_pass_object_index = True
    scene.view_layers["ViewLayer"].use_pass_z = True
    scene.view_layers["ViewLayer"].use_pass_normal = True

    # Switch on nodes and get reference
    scene.use_nodes = True
    tree = scene.node_tree
    links = tree.links

    ## Clear default nodes
    for node in tree.nodes:
        tree.nodes.remove(node)



    # Create a node for outputting the rendered image
    image_output_node = tree.nodes.new(type="CompositorNodeOutputFile")
    image_output_node.label = "Image_Output"
    image_output_node.base_path =  os.path.join(output_dir, "image")
    image_output_node.location = 400,0

    # Create a node for outputting the depth of each pixel from the camera
    depth_output_node = tree.nodes.new(type="CompositorNodeOutputFile")
    depth_output_node.label = "Depth_Output"
    depth_output_node.base_path =  os.path.join(output_dir, "depth")
    depth_output_node.location = 400,-100

    # Create a node for outputting the index of each object
    index_output_node = tree.nodes.new(type="CompositorNodeOutputFile")
    index_output_node.label = "Index_Output"
    index_output_node.base_path = os.path.join(output_dir, "mask")
    index_output_node.location = 400,-200
  
    # Create a node for outputting the surface normals of each pixel
    normal_output_node = tree.nodes.new(type="CompositorNodeOutputFile")
    normal_output_node.label = "Normal_Output"
    normal_output_node.base_path = os.path.join(output_dir, "normal")
    normal_output_node.location = 400, -300

    # Create a node for the output from the renderer
    render_layers_node = tree.nodes.new(type="CompositorNodeRLayers")
    render_layers_node.location = 0,0

    # Link all the nodes together
    links.new(render_layers_node.outputs['Image'], image_output_node.inputs['Image'])
    links.new(render_layers_node.outputs['Depth'], depth_output_node.inputs['Image'])
    links.new(render_layers_node.outputs['IndexOB'], index_output_node.inputs['Image'])
    links.new(render_layers_node.outputs['Normal'], normal_output_node.inputs['Image'])



    camera = scene.camera
    scene.render.resolution_x = resolution
    scene.render.resolution_y = resolution

    return image_output_node, depth_output_node, index_output_node, normal_output_node

Output of render for each node, left to right (image, index, depth, normal)

enter image description here

$\endgroup$

1 Answer 1

0
$\begingroup$

There are two things you need to convert from world space normals to tangent space normals. First you will need the forward vector of your rendering camera and then you have to normalize the generated Normals output.

What you are looking for is a setup like this. Please note that this setup assumes that your rendering camera is looking down the Z axis. If your camera should change location or look down another axis, you need to adjust SeparateXYZ and CombineXYZ setup. Also, you may want to flip the X or Y channel depending on your target application.

enter image description here

To generate this setup via code, you have to do this:

    # Create a node for outputting the surface normals of each pixel
    normal_output_node = tree.nodes.new(type="CompositorNodeOutputFile")
    normal_output_node.label = "Normal_Output"
    normal_output_node.base_path = os.path.join(output_dir, "normal")
    normal_output_node.location = 1200, -300

    loc_x = 400
    loc_y = -300
    sep_xyz_node = tree.nodes.new("CompositorNodeSeparateXYZ")
    sep_xyz_node.location = (loc_x, loc_y)

    loc_x += 200
    normalize_x_node = tree.nodes.new("CompositorNodeNormalize")
    normalize_x_node.location = (loc_x, loc_y + 40)

    normalize_y_node = tree.nodes.new("CompositorNodeNormalize")
    normalize_y_node.location = (loc_x, loc_y - 40)

    loc_x += 200
    combine_xyz_node = tree.nodes.new("CompositorNodeCombineXYZ")
    combine_xyz_node.inputs[2].default_value = 1.0
    combine_xyz_node.location = (loc_x, loc_y)

    loc_x += 200
    set_alpha_node = tree.nodes.new("CompositorNodeSetAlpha")
    set_alpha_node.location = (loc_x, loc_y)

The linking code would look like this:

    links.new(render_layers_node.outputs['Normal'], sep_xyz_node.inputs[0])
    links.new(sep_xyz_node.outputs[0], normalize_x_node.inputs[0])
    links.new(sep_xyz_node.outputs[1], normalize_y_node.inputs[0])
    links.new(normalize_x_node.outputs[0], combine_xyz_node.inputs[0])
    links.new(normalize_y_node.outputs[0], combine_xyz_node.inputs[1])
    links.new(combine_xyz_node.outputs[0], set_alpha_node.inputs[0])
    links.new(render_layers_node.outputs['Alpha'], set_alpha_node.inputs[1])
    links.new(set_alpha_node.outputs[0], normal_output_node.inputs[0])
$\endgroup$
2
  • $\begingroup$ Thank you for your answer. I really do need to have the vector from any angle. Can a Vector Transform to camera space? If so, what is the Python code for it? $\endgroup$
    – Jack Rolph
    Commented Apr 22, 2023 at 3:22
  • $\begingroup$ I'm certain that this is possible using python but I'm afraid that is beyond my skillset, sorry. I'm no export at vector math and matrix transformations. I can build a material that does what you want but you would need to change to this material before rendering. $\endgroup$
    – Robert L.
    Commented Apr 25, 2023 at 19:10

You must log in to answer this question.

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