WIP: Initial version of a single-frame job compiler #104189
@ -6,7 +6,7 @@ const JOB_TYPE = {
|
||||
// Settings for artists to determine:
|
||||
{ key: "frame", type: "int32", required: true, eval: "C.scene.frame_current",
|
||||
description: "Frame to render"},
|
||||
{ key: "chunk_size", type: "int32", default: 128, description: "Number of samples to render in one Blender render task",
|
||||
{ key: "chunk_size", type: "int32", default: 128, propargs: {min: 1}, description: "Number of samples to render in one Blender render task",
|
||||
visible: "submission" },
|
||||
{ key: "denoising", type: "bool", required: true, default: false,
|
||||
description: "Toggles OpenImageDenoise" },
|
||||
k8ie marked this conversation as resolved
Outdated
|
||||
@ -23,6 +23,10 @@ const JOB_TYPE = {
|
||||
|
||||
// Automatically evaluated settings:
|
||||
{ key: "blendfile", type: "string", required: true, description: "Path of the Blend file to render", visible: "web" },
|
||||
{ key: "format", type: "string", required: true, eval: "C.scene.render.image_settings.file_format", visible: "web" },
|
||||
{ key: "uses_compositing", type: "bool", required: true, eval: "C.scene.use_nodes and C.scene.render.use_compositing", visible: "web" },
|
||||
k8ie marked this conversation as resolved
Outdated
Sybren A. Stüvel
commented
The properties are typically named as 'imperative' ("do X", "use Y") and not 'descriptive' ("does X", "uses Y"). So this could be The properties are typically named as 'imperative' ("do X", "use Y") and not 'descriptive' ("does X", "uses Y"). So this could be `use_compositing`.
k8ie
commented
Should I change Should I change `denoising` to be `use_denoising` or `use_compositing` to be just `compositing`? Just to keep things consistent.
Sybren A. Stüvel
commented
Use Use `use_denoising` and `use_compositing`.
|
||||
{ key: "image_file_extension", type: "string", required: true, eval: "C.scene.render.file_extension", visible: "hidden",
|
||||
description: "File extension used for the final export" },
|
||||
{ key: "samples", type: "string", required: true, eval: "f'1-{C.scene.cycles.samples}'", visible: "web",
|
||||
description: "Total number of samples in the job" },
|
||||
]
|
||||
@ -43,6 +47,7 @@ function compileJob(job) {
|
||||
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) {
|
||||
job.addTask(rt);
|
||||
@ -57,7 +62,12 @@ function compileJob(job) {
|
||||
if (denoiseTask) {
|
||||
denoiseTask.addDependency(mergeTask);
|
||||
job.addTask(denoiseTask);
|
||||
compositeTask.addDependency(denoiseTask);
|
||||
}
|
||||
else {
|
||||
compositeTask.addDependency(mergeTask);
|
||||
}
|
||||
job.addTask(compositeTask);
|
||||
}
|
||||
|
||||
// Do field replacement on the render output path.
|
||||
@ -82,18 +92,18 @@ function authorRenderTasks(settings, renderDir, renderOutput) {
|
||||
let chunks = frameChunker(settings.samples, settings.chunk_size);
|
||||
for (let chunk of chunks) {
|
||||
const task = author.Task(`render-${chunk}`, "blender");
|
||||
let chunk_arr = chunk.split("-", 2)
|
||||
let chunk_end
|
||||
let chunk_start
|
||||
let chunk_arr = chunk.split("-", 2);
|
||||
let chunk_end;
|
||||
let chunk_start;
|
||||
if (chunk_arr.length < 2) {
|
||||
chunk_end = chunk_arr[0]
|
||||
chunk_start = chunk_arr[0] - 1
|
||||
chunk_end = chunk_arr[0];
|
||||
chunk_start = chunk_arr[0] - 1;
|
||||
}
|
||||
else {
|
||||
chunk_end = chunk_arr[1]
|
||||
chunk_start = chunk_arr[0] - 1
|
||||
chunk_end = chunk_arr[1];
|
||||
chunk_start = chunk_arr[0] - 1;
|
||||
}
|
||||
let chunk_size = chunk_end - chunk_start
|
||||
let chunk_size = chunk_end - chunk_start;
|
||||
let pythonExpression = `
|
||||
import bpy
|
||||
bpy.context.scene.render.engine = 'CYCLES'
|
||||
@ -115,6 +125,7 @@ for layer in bpy.context.scene.view_layers:
|
||||
argsBefore: [],
|
||||
blendfile: settings.blendfile,
|
||||
args: [
|
||||
"--python-exit-code", 1,
|
||||
"--python-expr", pythonExpression,
|
||||
"--render-output", path.join(renderDir, path.basename(renderOutput), chunk),
|
||||
"--render-frame", settings.frame
|
||||
@ -170,6 +181,7 @@ pathlib.Path(tmp + str(index-1)+'.exr').rename(basepath + 'MERGED.exr')`;
|
||||
argsBefore: [],
|
||||
blendfile: settings.blendfile,
|
||||
args: [
|
||||
"--python-exit-code", 1,
|
||||
"--python-expr", pythonExpression
|
||||
]
|
||||
});
|
||||
@ -192,6 +204,97 @@ bpy.ops.cycles.denoise_animation(input_filepath="${renderOutput}/MERGED.exr", ou
|
||||
argsBefore: [],
|
||||
blendfile: settings.blendfile,
|
||||
args: [
|
||||
"--python-exit-code", 1,
|
||||
"--python-expr", pythonExpression
|
||||
]
|
||||
});
|
||||
task.addCommand(command);
|
||||
return task;
|
||||
}
|
||||
|
||||
function authorCreateCompositeTask(settings, renderOutput) {
|
||||
let filename;
|
||||
if (settings.denoising) {
|
||||
filename = "DENOISED.exr";
|
||||
}
|
||||
else {
|
||||
filename = "MERGED.exr";
|
||||
}
|
||||
let pythonExpression = `
|
||||
import pathlib
|
||||
import bpy
|
||||
C = bpy.context
|
||||
basepath = "${renderOutput}/"
|
||||
filename = "${filename}"`;
|
||||
if (settings.uses_compositing) {
|
||||
// Do the full composite+export pipeline
|
||||
// uses snippets from
|
||||
// https://github.com/state-of-the-art/BlendNet/blob/master/BlendNet/script-compose.py#L94
|
||||
pythonExpression += `
|
||||
bpy.ops.image.open(filepath=basepath + filename, use_sequence_detection=False)
|
||||
image = bpy.data.images[bpy.path.basename(filename)]
|
||||
image_node = C.scene.node_tree.nodes.new(type='CompositorNodeImage')
|
||||
image_node.image = image
|
||||
nodes_to_remove = []
|
||||
links_to_create = []
|
||||
for node in C.scene.node_tree.nodes:
|
||||
print('DEBUG: Checking node %s' % (node,))
|
||||
if not isinstance(node, bpy.types.CompositorNodeRLayers) or node.scene != C.scene:
|
||||
continue
|
||||
nodes_to_remove.append(node)
|
||||
print('INFO: Reconnecting %s links to render image' % (node,))
|
||||
for link in C.scene.node_tree.links:
|
||||
print('DEBUG: Checking link %s - %s' % (link.from_node, link.to_node))
|
||||
if link.from_node != node:
|
||||
continue
|
||||
print('DEBUG: Found link %s - %s' % (link.from_socket, link.to_socket))
|
||||
link_name = "Combined"
|
||||
for output in image_node.outputs:
|
||||
print('DEBUG: Checking output:', output.name, link_name)
|
||||
if output.name != link_name:
|
||||
continue
|
||||
links_to_create.append((output, link))
|
||||
break
|
||||
|
||||
for output, link in links_to_create:
|
||||
print('INFO: Connecting "%s" output to %s.%s input' % (
|
||||
output, link.to_node, link.to_socket
|
||||
))
|
||||
C.scene.node_tree.links.new(output, link.to_socket)
|
||||
|
||||
print("Removing the nodes could potentially break the pipeline")
|
||||
for node in nodes_to_remove:
|
||||
print('INFO: Removing %s' % (node,))
|
||||
C.scene.node_tree.nodes.remove(node)
|
||||
C.scene.render.filepath = basepath + "FINAL"
|
||||
bpy.ops.render.render(write_still=True)`;
|
||||
}
|
||||
else {
|
||||
pythonExpression += `
|
||||
print("Compositing is disabled")`;
|
||||
if (settings.format == "OPEN_EXR_MULTILAYER") {
|
||||
// Only rename
|
||||
pythonExpression += `
|
||||
print('Renaming ' + basepath + filename + ' to ' + basepath + 'FINAL.exr')
|
||||
pathlib.Path(basepath + filename).rename(basepath + 'FINAL.exr')`;
|
||||
}
|
||||
else {
|
||||
// Only export
|
||||
pythonExpression += `
|
||||
bpy.ops.image.open(filepath=basepath + filename, use_sequence_detection=False)
|
||||
image = bpy.data.images[bpy.path.basename(filename)]
|
||||
print("Saving final image to " + basepath + "FINAL" + "${settings.image_file_extension}")
|
||||
image.save_render(basepath + "FINAL" + "${settings.image_file_extension}")`;
|
||||
}
|
||||
}
|
||||
const task = author.Task(`composite`, "blender");
|
||||
const command = author.Command("blender-render", {
|
||||
exe: "{blender}",
|
||||
exeArgs: "{blenderArgs}",
|
||||
argsBefore: [],
|
||||
blendfile: settings.blendfile,
|
||||
args: [
|
||||
"--python-exit-code", 1,
|
||||
"--python-expr", pythonExpression
|
||||
]
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user
I think this can be moved into the hidden settings, with
expr: "bpy.context.scene.cycles.use_denoising"
. That way the denoising is managed via the normal setting, and not yet again by Flamenco.