WIP: Single-frame job compiler #104194

Draft
k8ie wants to merge 30 commits from k8ie/flamenco:single-frame into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
Showing only changes of commit af1f308d6c - Show all commits

View File

@ -46,7 +46,6 @@ function compileJob(job) {
const renderDir = path.dirname(renderOutput);
const renderTasks = authorRenderTasks(settings, renderDir, renderOutput);
const mergeTask = authorCreateMergeTask(settings, renderOutput);
const denoiseTask = authorCreateDenoiseTask(settings, renderOutput);
const compositeTask = authorCreateCompositeTask(settings, renderOutput);
for (const rt of renderTasks) {
@ -57,17 +56,8 @@ function compileJob(job) {
mergeTask.addDependency(rt);
}
job.addTask(mergeTask);
// Add a denoise task if denoising is enabled and
// set the merge task as its dependency
if (denoiseTask) {
denoiseTask.addDependency(mergeTask);
job.addTask(denoiseTask);
compositeTask.addDependency(denoiseTask);
}
else {
compositeTask.addDependency(mergeTask);
}
job.addTask(compositeTask);
compositeTask.addDependency(mergeTask);
job.addTask(compositeTask);
}
// Do field replacement on the render output path.
@ -164,6 +154,7 @@ bpy.context.scene.render.use_compositing = True
bpy.context.scene.use_nodes = True
bpy.context.scene.node_tree.nodes.clear()
bpy.context.scene.render.image_settings.file_format = 'OPEN_EXR_MULTILAYER'
bpy.context.scene.render.filepath = basepath + "MERGED"
image_nodes = []
for index, image in enumerate(filenames):
@ -172,25 +163,53 @@ for index, image in enumerate(filenames):
image_nodes.append(bpy.context.scene.node_tree.nodes.new(type='CompositorNodeImage'))
image_nodes[index].image = image
output_node = bpy.context.scene.node_tree.nodes.new(type='CompositorNodeComposite')
denoising = "${settings.use_denoising}" == "true"
alpha_over_nodes = []
albedo_mix_nodes = []
normal_mix_nodes = []
for index, node in enumerate(image_nodes):
if index == 0:
# Take the first two image nodes and combine them
alpha_over_nodes.append(bpy.context.scene.node_tree.nodes.new(type='CompositorNodeAlphaOver'))
bpy.context.scene.node_tree.links.new(image_nodes[0].outputs['Combined'], alpha_over_nodes[0].inputs[1])
bpy.context.scene.node_tree.links.new(image_nodes[1].outputs['Combined'], alpha_over_nodes[0].inputs[2])
bpy.context.scene.node_tree.links.new(image_nodes[0].outputs['Combined'], alpha_over_nodes[index].inputs[1])
bpy.context.scene.node_tree.links.new(image_nodes[1].outputs['Combined'], alpha_over_nodes[index].inputs[2])
if denoising:
albedo_mix_nodes.append(bpy.context.scene.node_tree.nodes.new(type='CompositorNodeMixRGB'))
albedo_mix_nodes[index].blend_type = 'ADD'
bpy.context.scene.node_tree.links.new(image_nodes[0].outputs['Denoising Albedo'], albedo_mix_nodes[index].inputs[1])
bpy.context.scene.node_tree.links.new(image_nodes[1].outputs['Denoising Albedo'], albedo_mix_nodes[index].inputs[2])
normal_mix_nodes.append(bpy.context.scene.node_tree.nodes.new(type='CompositorNodeMixRGB'))
normal_mix_nodes[index].blend_type = 'ADD'
bpy.context.scene.node_tree.links.new(image_nodes[0].outputs['Denoising Normal'], normal_mix_nodes[index].inputs[1])
bpy.context.scene.node_tree.links.new(image_nodes[1].outputs['Denoising Normal'], normal_mix_nodes[index].inputs[2])
else:
# Take one image node and the previous alpha over node
alpha_over_nodes.append(bpy.context.scene.node_tree.nodes.new(type='CompositorNodeAlphaOver'))
bpy.context.scene.node_tree.links.new(alpha_over_nodes[index-1].outputs['Image'], alpha_over_nodes[index].inputs[1])
bpy.context.scene.node_tree.links.new(image_nodes[index+1].outputs['Combined'], alpha_over_nodes[index].inputs[2])
if denoising:
albedo_mix_nodes.append(bpy.context.scene.node_tree.nodes.new(type='CompositorNodeMixRGB'))
albedo_mix_nodes[index].blend_type = 'ADD'
bpy.context.scene.node_tree.links.new(albedo_mix_nodes[index-1].outputs['Image'], albedo_mix_nodes[index].inputs[1])
bpy.context.scene.node_tree.links.new(image_nodes[index+1].outputs['Denoising Albedo'], albedo_mix_nodes[index].inputs[2])
normal_mix_nodes.append(bpy.context.scene.node_tree.nodes.new(type='CompositorNodeMixRGB'))
normal_mix_nodes[index].blend_type = 'ADD'
bpy.context.scene.node_tree.links.new(normal_mix_nodes[index-1].outputs['Image'], normal_mix_nodes[index].inputs[1])
bpy.context.scene.node_tree.links.new(image_nodes[index+1].outputs['Denoising Normal'], normal_mix_nodes[index].inputs[2])
if index + 1 == len(image_nodes) - 1:
# Link the last image node and feed the output into the composite node
output_node = bpy.context.scene.node_tree.nodes.new(type='CompositorNodeOutputFile')
output_node.base_path = basepath + "MERGED"
bpy.context.scene.node_tree.links.new(alpha_over_nodes[index].outputs['Image'], output_node.inputs['Image'])
if denoising:
denoise_node = bpy.context.scene.node_tree.nodes.new(type='CompositorNodeDenoise')
bpy.context.scene.node_tree.links.new(alpha_over_nodes[index].outputs['Image'], denoise_node.inputs['Image'])
bpy.context.scene.node_tree.links.new(albedo_mix_nodes[index].outputs['Image'], denoise_node.inputs['Albedo'])
bpy.context.scene.node_tree.links.new(normal_mix_nodes[index].outputs['Image'], denoise_node.inputs['Normal'])
bpy.context.scene.node_tree.links.new(denoise_node.outputs['Image'], output_node.inputs['Image'])
else:
# Link the last image node and feed the output into the composite node
bpy.context.scene.node_tree.links.new(alpha_over_nodes[index].outputs['Image'], output_node.inputs['Image'])
break
bpy.ops.render.render()`;
bpy.ops.render.render(write_still=True)`;
const command = author.Command("blender-render", {
exe: "{blender}",
exeArgs: "{blenderArgs}",
@ -205,42 +224,14 @@ bpy.ops.render.render()`;
return task;
}
function authorCreateDenoiseTask(settings, renderOutput) {
if (! settings.use_denoising) {
return;
}
const task = author.Task(`denoise`, "blender");
let pythonExpression = `
import bpy
print("Running the denoiser")
bpy.ops.cycles.denoise_animation(input_filepath="${renderOutput}/MERGED.exr", output_filepath="${renderOutput}/DENOISED.exr")`;
const command = author.Command("blender-render", {
exe: "{blender}",
exeArgs: "{blenderArgs}",
argsBefore: [],
blendfile: settings.blendfile,
args: [
"--python-exit-code", 1,
"--python-expr", pythonExpression
]
});
task.addCommand(command);
return task;
}
function authorCreateCompositeTask(settings, renderOutput) {
let filename;
let pythonExpression = `
import pathlib
import os
import bpy
C = bpy.context
basepath = "${renderOutput}/"
filenames = [f for f in os.listdir(basepath) if os.path.isfile(basepath + f)]
if "DENOISED.exr" in filenames:
filename = "DENOISED.exr"
else:
filename = next((s for s in filenames if "MERGED" in s), None)
filename = "MERGED.exr"
`;
if (settings.use_compositing) {
// Do the full composite+export pipeline