output
only.Expand description
Output driver callbacks.
This module declares several closure types. These can be passed via
Callback
s to an
OutputDriver
node to stream
pixels during and/or after a render, in-memory.
There are three types of closure:
-
FnOpen
is called once when theOutputDriver
is opened by the renderer. -
FnWrite
is called for each bucket of pixel data the renderer sends to theOutputDriver
. -
FnFinish
is called once when theOutputDriver
is closed by the renderer.
As a user you can choose how to use this API.
-
To get a single buffer of pixel data when rendering is finished it is enough to implement an
FnFinish
closure. -
To get the pixel buffer updated while the renderer is working implement an
FnWrite
closure.
The format of the Vec<f32>
buffer is described by the PixelFormat
parameter which is passed to both of these closures.
Example
// Setup a screen.
ctx.create("screen", nsi::SCREEN, None);
// We pretend we defined a camera node earlier.
ctx.connect("screen", None, "camera", "screens", None);
ctx.set_attribute(
"screen",
&[
// The resolution becomes the width & height passed to
// FnOpen, FnWrite and FnFinish type callbacks.
nsi::integers!("resolution", &[1920, 1080]).array_len(2),
],
);
// Setup an RGBA output layer.
ctx.create("beauty", nsi::OUTPUT_LAYER, None);
ctx.set_attribute(
"beauty",
&[
// The Ci variable comes from Open Shading Language.
nsi::string!("variablename", "Ci"),
// We want the data as raw, 32 bit float.
nsi::string!("scalarformat", "float"),
nsi::integer!("withalpha", 1),
],
);
ctx.connect("beauty", None, "screen", "outputlayers", None);
// Setup an output driver.
ctx.create("driver", nsi::OUTPUT_DRIVER, None);
ctx.connect("driver", None, "beauty", "outputdrivers", None);
// Our FnFinish callback. We will be called once.
let finish = nsi::output::FinishCallback::new(
|// Passed from the output driver node, below.
image_filename: String,
// Passed from the screen node, above.
width: usize,
// Passed from the screen node, above.
height: usize,
pixel_format: nsi::output::PixelFormat,
pixel_data: Vec<f32>| {
// Call some function to write our image to an OpenEXR file.
write_exr(
(String::from(image_filename) + ".exr").as_str(),
width,
height,
pixel_format.len(),
&pixel_data,
);
nsi::output::Error::None
},
);
ctx.set_attribute(
"driver",
&[
// Important: FERRIS is the built-in output driver
// that understands the closure parameters.
nsi::string!("drivername", nsi::output::FERRIS),
// This will end up in the `name` parameter passed
// to finish().
nsi::string!("imagefilename", "render"),
nsi::callback!("callback.finish", finish),
],
);
ctx.render_control(nsi::Action::Start, None);
// The finish() closure will be called once, before the next call returns.
ctx.render_control(nsi::Action::Wait, None);
Color Profiles
The pixel color data that the renderer generates is linear and scene-referred. I.e. relative to whatever units you used to describe illuminants in your scene.
Using the
"colorprofile"
attribute
of an OutputLayer
you can ask the
renderer to apply an Open Color IO (OCIO)
profile/LUT
before quantizing (see below).
Once OCIO has a Rust wrapper you can easily choose to
do these color conversions yourself. In the meantime there is the
colorspace
crate which has some useful profiles built
in, e.g. ACEScg.
ctx.create("beauty", nsi::OUTPUT_LAYER, None);
ctx.set_attribute(
"beauty",
&[
// The Ci variable comes from Open Shading Language.
nsi::string!("variablename", "Ci"),
// We want the pixel data 'display-referred' in sRGB and quantized down to 0.0..255.0.
nsi::string!("colorprofile", "/home/luts/linear_to_sRGB.spi1d"),
nsi::string!("scalarformat", "uint8"),
],
);
Quantization
Using the "scalarformat"
attribute
of an OutputLayer
you can ask the
renderer to quantize data down to a suitable range. For example, setting
this to "uint16"
will get you valid u16
values from 0.0..65535.0
, but
stored in the f32
s of the pixel_data
buffer. The value of 1.0
will map
to 65535.0
and everything above will be clipped. You can convert
such a value straight via f32 as u16
.
Unless you asked the renderer to also apply some color profile (see above) the data is linear. To look good on a screen it needs to be made display-referred.
See the output
example on how to do this with a simple, display-referred
sRGB
curve.
Modules
Structs
- Wrapper to pass an
FnFinish
closure to anOutputDriver
node. - Description of an
OutputLayer
node inside a flat, raw pixel. - Wrapper to pass an
FnOpen
closure to anOutputDriver
node. - Wrapper to pass an
FnWrite
closure to anOutputDriver
node.
Enums
- An error type the callbacks return to communicate with the renderer.
- The depth (number and type of channels) a pixel in a
Layer
is composed of.
Statics
- This is the name of the crate’s built-in output driver that understands the “closure.*” attributes.
Traits
- A closure which is called once per
OutputDriver
instance. - A closure which is called once per
OutputDriver
instance. - A closure which is called for each bucket of pixels the
OutputDriver
instance sends during rendering.