WIP: Single-frame job compiler #104194
@ -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" },
|
||||
@ -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" },
|
||||
{ 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