OSL Shaders - Arnold Developer Guide
OSL is a shading language designed for modern physically based rendering, supported starting from Arnold 5.0. It can be used to write shaders that work in Arnold and other renderers. It is an alternative to C++ shaders, with a higher-level API that makes writing shaders simpler while at the same time providing advanced optimizations.
Getting Started
OSL shaders are written in .osl files, with each file corresponding to a single Arnold shader node. Here is a simple example:
gamma.osl
shader gamma(
color Cin = color(0, 0, 0),
float exponent = 1,
output color Cout = color(0, 0, 0))
{
Cout = pow(Cin, 1/exponent);
}
The shader name gamma
must match the gamma.osl
filename. The shader defines input and output parameters much like Arnold C++ shader nodes but using a more compact syntax. Here Cin
and gamma
are input parameters, while Cout
is the output parameter. All parameters must be initialized with default values.
Compilation
Shaders are compiled automatically when they are found by Arnold in the shader searchpath, from .osl files to .oso files in the same directory. This happens if no corresponding .oso file exists, or if the .osl file was modified since the last compilation.
Alternatively, shaders can be manually compiled using the oslc
program included with Arnold.
$ oslc gamma.osl
Compiled gamma.osl -> gamma.oso
.oso files contain intermediate code that is operating system and CPU agnostic. At render time, OSL translates shader networks into machine code on the fly, and in the process heavily optimizes shaders and networks with full knowledge of the shader parameters and other runtime values that could not have been known when the shaders were compiled from .osl source code.
Installation
.osl and .oso files are installed the same way as .so and .dll shader files: place them anywhere in the shader searchpath, and Arnold will find them.
Using kick
we can verify that Arnold detects the shader and its parameters.
$ kick -info gamma
node: gamma
type: shader
output: RGB
parameters: 3
filename: gamma.oso
version: 4.2.12.0
Type Name Default
------------ -------------------------------- --------------------------------
RGB Cin 0, 0, 0
FLOAT exponent 1
STRING name
Manually Loading Shaders
Besides the automatically registered OSL shaders in the search path, it is possible to load shaders manually through an osl
node. This can be useful to insert expressions into a shader network or to get more control over which shaders are loaded and how their parameters should be interpreted.
osl
accepts a shadername
parameter to specify the shader, without the .osl or .oso file extension. As soon as the shadername
parameter is set, the OSL shader parameters are added to the node, with a param_
prefix.
osl
{
name myshader
shadername somefolder/test
param_value 0.5
}
For inserting expressions into the network, the .OSL or .OSO code can be embedded as well:
osl
{
name myshader
code "shader test(float value = 0, output float result = 0)
{
result = value * 10;
}"
param_value 0.5
}
Supported Features
Parameters
OSL shader parameters are converted to the corresponding Arnold parameters where possible. Arnold only supports one output parameter per node, so if there are multiple output parameters the first parameter will be used.
OSL Type | Arnold Type |
---|---|
int |
INT |
int (with metadata) |
BOOLEAN |
int (with metadata) |
ENUM |
float |
FLOAT |
color |
RGB |
color |
RGBA |
point |
VECTOR |
vector |
VECTOR |
normal |
VECTOR |
point |
POINT2 |
matrix |
MATRIX |
array of any type |
ARRAY |
closure color |
CLOSURE |
struct |
POINTER |
Boolean and enum parameter types are created with OSL metadata on integers.
#define OPTION_A 0
#define OPTION_B 1
#define OPTION_C 2
shader example(
int booleanvalue = 0 [[ string widget = "boolean" ]],
int enumvalue = 0 [[ string widget = "popup", string options = "OptionA|OptionB|OptionC" ]])
{
if (booleanvalue)
...
if (enumvalue == OPTION_B)
...
}
Attributes
Node parameters and user data are available through getattribute().
// object parameter
int id;
getattribute("id", id);
// object user data
color Cs;
getattribute("Cs", Cs);
// parameter of another node
int AA_samples;
getattribute("options", "AA_samples", AA_samples);
Standard attributes
Using the following attributes, if the getattribute() function specifies an objectname parameter, the value specific to that object is retrieved. If no specific object is named, the current object is implied.
Name | Type | Description |
---|---|---|
geom:type |
string | Object type |
geom:name |
string | Object name |
geom:bounds |
point[2] | Object bounding box in world space (min, max) |
geom:objbounds |
point[2] | Object bounding box in object space (min, max) |
Standard camera attributes
Using the following attributes, if the getattribute() function specifies an objectname parameter and it is the name of a valid camera, the value specific to that camera is retrieved. If no specific camera is named, the global or default camera is implied.
Name | Type | Description |
---|---|---|
camera:screen_window |
int[2] | Image resolution |
camera:pixelaspect |
float | Pixel aspect ratio |
camera:projection |
string | Camera type |
camera:fov |
float | Field of view |
camera:clip_near |
float | Near clip distance |
camera:clip_far |
float | Far clip distance |
camera:clip |
float[2] | Near and far clip distances |
camera:shutter_open |
float | Shutter open time |
camera:shutter_close |
float | Shutter close time |
camera:shutter |
float[2] | Shutter open and close times |
camera:screen_window |
float[4] | Screen window (xmin, ymin, xmax, ymax) |
Shader Globals
Arnold supports most OSL shader globals, like P
, u
, v
, N
, Ng
and time
. Their meaning is the same as in the C++ shading API.
Ps
(for light filters), surfacearea()
, dtime
and dPdtime
shader globals are not supported currently.
All shader globals are considered to be read-only, we do not support writing to Ci
and P
to output closures or displacement. Output parameters should be used instead.
Textures
Textures are accessed through the built-in texture()
and gettextureinfo(
) functions. The texture()
function accepts an optional colorspace
argument to indicate the texture's color space to convert from.
Note that unlike the Arnold C++ shading API, the texture origin is assumed to be in the top left corner rather than the bottom left corner, for consistency with the OSL standard. To match, the v
coordinate can be flipped to 1 - v
, or floor(v) + 1 - mod(v, 1)
in case of UDIM textures.
// look up texture color
color tex = texture(filename, u, v);
// query texture resolution
int resolution;
gettextureinfo(filename, "resolution", resolution);
Volume Channels
Volume channels are available through texture3d()
using object space coordinates.
point Po = transform("object", P);
float density = texture3d("density", Po, "interp", "bicubic");
Closures
The following closures are supported, matching the closures in the C++ shader API.
Supported Closures
closure color emission();
closure color background();
closure color diffuse(normal N);
closure color oren_nayar (normal N, float sigma);
closure color oren_nayar(normal N);
closure color translucent(normal N, float sigma);
closure color translucent(normal N);
closure color sheen(normal N, float roughness);
closure color metal(string distribution, normal N, vector U,
color n, color k,
float xalpha, float yalpha);
closure color microfacet(string distribution, normal N, vector U,
float xalpha, float yalpha, float eta, int refract);
closure color microfacet(string distribution, normal N,
float alpha, float eta, int refr);
closure color reflection(normal N, float eta);
closure color reflection(normal N);
closure color refraction(normal N, float eta);
closure color transparent();
closure color holdout();
closure color empirical_bssrdf(vector mfp, color albedo);
closure color randomwalk_bssrdf(vector mfp, color albedo, float g);
closure color volume_absorption();
closure color volume_emission();
closure color volume_henyey_greenstein(color absorption, color scattering,
color emission, float g);
closure color volume_matte();
Example shader outputting a closure:
shader simple_diffuse(
color albedo = 0.8,
float opacity = 1.0,
output closure color result = 0)
{
result = opacity * albedo * diffuse(N) + (1 - opacity) * transparent();
}
Trace
The OSL trace()
function is supported, for tracing probe rays. The shade
argument to perform shading is not supported. The traceset
argument is supported, using an inclusive traceset by default, and exclusive if the traceset name is prefixed with a -
characeter.
Information about the hit may be retrieved with getmessage()
, supporting hit
, hitdist
, P
, N
, u
, v
, and arbitrary user data and parameter lookups on the object that was hit.
int hit = trace(origin, direction, "traceset", tracesetname);
if (hit)
{
// query hit distance
float hitdist;
getmessage("trace", "hitdist", hitdist);
// query parameter on object that was hit
string name;
getmessage("trace", "name", name);
// query user data on position where object was hit
color Cs;
if (getmessage("trace", "Cs", Cs))
...;
}
Raytype
The OSL raytype(
Supported ray types |
---|
camera |
shadow |
diffuse_transmit |
specular_transmit |
volume_scatter |
diffuse_reflect |
specular_reflect |
subsurface |
Performance
OSL and C++ shaders can be linked to a single shader network. However, there is a small overhead (perhaps 1-2 % overall render time) in connecting the output of an OSL shader into a C++ shader. For the OSL optimizer to be able to do aggressive whole network optimizations, as many OSL shader nodes as possible should be used.
Runtime compilation and optimization of OSL shaders happens the first time the shader is evaluated, during rendering. This increases the time to the first pixel but can pay off in reduced render time overall.
Debugging
The OSL_OPTIONS environment variable can be used to debug common errors in shaders or print more detailed information:
# Add (expensive) code to find array out of bounds access, NaN/Infs and use of uninitialized variables
OSL_OPTIONS="range_checking=1,debug_nan=1,debug_uninit=1"
# Issue info messages for every shader compiled
OSL_OPTIONS="compile_report=1"
# Number of warning calls that should be processed per thread
OSL_OPTIONS="max_warnings_per_thread=100"