Share

OSL Shaders - Arnold User Guide

Note:

The OSL shaders used in the above examples can be found here.

It is possible to create your own shaders using Open Shading Language. OSL shaders can be used to implement anything from simple texture patterns to full materials using closures. They can be placed in the plugin search path, and will then be automatically loaded like other types of shaders. Once loaded, they can be inspected, instantiated, and linked in the same way as C++ shaders. The OSL node provides an alternate way to load OSL shaders, which can be used to write shader code for a specific material. When setting the shader name or code, the parameters from the OSL shader automatically appear on the node to be set or linked.

Limitations

  • The shaders must have unique names. If the name conflicts with an existing shader, the OSL shader won't load. You should see a warning in the log in this case.
  • No support for exposing input attributes of type Array and Matrix.
  • The name of the output attribute is not the same as the shader code.
  • Unable to do #includes in OSL shader code.

An example .osl shader and .mtd file can be found here.

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

Just like any other third-party shader libraries, OSL shaders placed in the shader search path are automatically registered as Arnold shader nodes. The OSL shader parameters are converted to Arnold parameters. Once loaded, they can be inspected, instantiated and linked in the same way as C++ shaders.

To render OSL shaders directly with Arnold, you must do the following:

  • Put the OSL shader (.osl and .mtd) in a folder, and set either ARNOLD_PLUGIN_PATH or the shader searchpath to point to this folder. For example:
ARNOLD_PLUGIN_PATH=C:\shaders\osl
  • Arnold will automatically compile the .osl file and produce a dll (.oso) file.

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();

The following closures are provided to be compatible with PBR materials generated by MaterialX, according to the API defined by OSL:

// Constructs a diffuse reflection BSDF based on the Oren-Nayar reflectance model.
//
//  \param  N           Normal vector of the surface point being shaded.
//  \param  albedo      Surface albedo.
//  \param  roughness   Surface roughness [0,1]. A value of 0.0 gives Lambertian reflectance.
//  \param  label       Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color oren_nayar_diffuse_bsdf(normal N, color albedo, float roughness, string label);

// Constructs a diffuse reflection BSDF based on the corresponding component of
// the Disney Principled shading model.
//
//  \param  N           Normal vector of the surface point being shaded.
//  \param  albedo      Surface albedo.
//  \param  roughness   Surface roughness [0,1].
//  \param  label       Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color burley_diffuse_bsdf(normal N, color albedo, float roughness, string label);

// Constructs a reflection and/or transmission BSDF based on a microfacet reflectance
// model and a Fresnel curve for dielectrics. The two tint parameters control the
// contribution of each reflection/transmission lobe. The tints should remain 100% white
// for a physically correct dielectric, but can be tweaked for artistic control or set
// to 0.0 for disabling a lobe.
// The closure may be vertically layered over a base BSDF for the surface beneath the
// dielectric layer. This is done using the layer() closure. By chaining multiple
// dielectric_bsdf closures you can describe a surface with multiple specular lobes.
// If transmission is enabled (transmission_tint > 0.0) the closure may be layered over
// a VDF closure describing the surface interior to handle absorption and scattering
// inside the medium.
//
//  \param  N                   Normal vector of the surface point being shaded.
//  \param  U                   Tangent vector of the surface point being shaded.
//  \param  reflection_tint     Weight per color channel for the reflection lobe. Should be (1,1,1) for a physically-correct dielectric surface,
//                              but can be tweaked for artistic control. Set to (0,0,0) to disable reflection.
//  \param  transmission_tint   Weight per color channel for the transmission lobe. Should be (1,1,1) for a physically-correct dielectric surface,
//                              but can be tweaked for artistic control. Set to (0,0,0) to disable transmission.
//  \param  roughness_x         Surface roughness in the U direction with a perceptually linear response over its range.
//  \param  roughness_y         Surface roughness in the V direction with a perceptually linear response over its range.
//  \param  ior                 Refraction index.
//  \param  distribution        Microfacet distribution. An implementation is expected to support the following distributions: { "ggx" }
//  \param  thinfilm_thickness  Optional float parameter for thickness of an iridescent thin film layer on top of this BSDF. Given in nanometers.
//  \param  thinfilm_ior        Optional float parameter for refraction index of the thin film layer.
//  \param  label1              Slot for setting thinfilm_thickness or thinfilm_ior, of iridescent thin film layer
//  \param  label2              Slot for setting thinfilm_thickness or thinfilm_ior, of iridescent thin film layer
//
closure color dielectric_bsdf(normal N, vector U,
                              color reflection_tint, color transmission_tint,
                              float roughness_x, float roughness_y,
                              float ior,
                              string distribution,
                              string label1, float value1,
                              string label2, float value2);

// Constructs a reflection BSDF based on a microfacet reflectance model.
// Uses a Fresnel curve with complex refraction index for conductors/metals.
// If an artistic parametrization is preferred the artistic_ior() utility function
// can be used to convert from artistic to physical parameters.
//
//  \param  N                   Normal vector of the surface point beeing shaded.
//  \param  U                   Tangent vector of the surface point beeing shaded.
//  \param  roughness_x         Surface roughness in the U direction with a perceptually linear response over its range.
//  \param  roughness_y         Surface roughness in the V direction with a perceptually linear response over its range.
//  \param  ior                 Refraction index.
//  \param  extinction          Extinction coefficient.
//  \param  distribution        Microfacet distribution. An implementation is expected to support the following distributions: { "ggx" }
//  \param  thinfilm_thickness  Optional float parameter for thickness of an iridescent thin film layer on top of this BSDF. Given in nanometers.
//  \param  thinfilm_ior        Optional float parameter for refraction index of the thin film layer.
//  \param  label               Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color conductor_bsdf(normal N, vector U,
                             float roughness_x, float roughness_y,
                             color ior, color extinction,
                             string distribution) BUILTIN;

// Constructs a reflection and/or transmission BSDF based on a microfacet reflectance model
// and a generalized Schlick Fresnel curve. The two tint parameters control the contribution
// of each reflection/transmission lobe.
// The closure may be vertically layered over a base BSDF for the surface beneath the
// dielectric layer. This is done using the layer() closure. By chaining multiple
// dielectric_bsdf closures you can describe a surface with multiple specular lobes.
// If transmission is enabled (transmission_tint > 0.0) the closure may be layered over
// a VDF closure describing the surface interior to handle absorption and scattering
// inside the medium.
//
//  \param  N                   Normal vector of the surface point beeing shaded.
//  \param  U                   Tangent vector of the surface point beeing shaded.
//  \param  reflection_tint     Weight per color channel for the reflection lobe. Set to (0,0,0) to disable reflection.
//  \param  transmission_tint   Weight per color channel for the transmission lobe. Set to (0,0,0) to disable transmission.
//  \param  roughness_x         Surface roughness in the U direction with a perceptually linear response over its range.
//  \param  roughness_y         Surface roughness in the V direction with a perceptually linear response over its range.
//  \param  f0                  Reflectivity per color channel at facing angles.
//  \param  f90                 Reflectivity per color channel at grazing angles.
//  \param  distribution        Microfacet distribution. An implementation is expected to support the following distributions: { "ggx" }
//  \param  thinfilm_thickness  Optional float parameter for thickness of an iridescent thin film layer on top of this BSDF. Given in nanometers.
//  \param  thinfilm_ior        Optional float parameter for refraction index of the thin film layer.
//  \param  label               Optional string parameter to name this component. For use in AOVs / LPEs.
closure color generalized_schlick_bsdf(normal N, vector U,
                                       color reflection_tint, color transmission_tint,
                                       float roughness_x, float roughness_y,
                                       color f0, color f90,
                                       float exponent,
                                       string distribution,
                                       string label1, float value1,
                                       string label2, float value2);

// Constructs a translucent (diffuse transmission) BSDF based on the Lambert reflectance model.
//
//  \param  N           Normal vector of the surface point beeing shaded.
//  \param  albedo      Surface albedo.
//  \param  label       Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color translucent_bsdf(normal N, color albedo);

// Constructs a closure that represents straight transmission through a surface.
//
//  \param  label       Optional string parameter to name this component. For use in AOVs / LPEs.
//
// NOTE:
//  - This is not a node in the MaterialX library, but the surface shader constructor
//    node has an 'opacity' parameter to control textured cutout opacity.
//
closure color transparent_bsdf(string label);

// Constructs a BSSRDF for subsurface scattering within a homogeneous medium.
//
//  \param  N                   Normal vector of the surface point beeing shaded.
//  \param  albedo              Single-scattering albedo of the medium.
//  \param  transmission_depth  Distance traveled inside the medium by white light before its color becomes transmission_color by Beer's law.
//                              Given in scene length units, range [0,infinity). Together with transmission_color this determines the extinction
//                              coefficient of the medium.
//  \param  transmission_color  Desired color resulting from white light transmitted a distance of 'transmission_depth' through the medium.
//                              Together with transmission_depth this determines the extinction coefficient of the medium.
//  \param  anisotropy          Scattering anisotropy [-1,1]. Negative values give backwards scattering, positive values give forward scattering,
//                              and 0.0 gives uniform scattering.
//  \param  label               Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color subsurface_bssrdf(normal N,
                                color albedo,
                                float transmission_depth,
                                color transmission_color,
                                float anisotropy,
                                string label);

// Constructs a microfacet BSDF for the back-scattering properties of cloth-like materials.
// This closure may be vertically layered over a base BSDF, where energy that is not reflected
// will be transmitted to the base closure.
//
//  \param  N           Normal vector of the surface point beeing shaded.
//  \param  albedo      Surface albedo.
//  \param  roughness   Surface roughness [0,1].
//  \param  label       Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color sheen_bsdf(normal N,
                         color albedo,
                         float roughness);

// Constructs an EDF emitting light uniformly in all directions.
//
//  \param  emittance   Radiant emittance of light leaving the surface.
//  \param  label       Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color uniform_edf(color emittance);

// Constructs a VDF scattering light for a general participating medium, based on the Henyey-Greenstein
// phase function. Forward, backward and uniform scattering is supported and controlled by the anisotropy input.
//
//  \param  albedo      Volume single-scattering albedo.
//  \param  extinction  Volume extinction coefficient.
//  \param  anisotropy  Scattering anisotropy [-1,1]. Negative values give backwards scattering, positive values give forward scattering,
//                      and 0.0 gives uniform scattering.
//  \param  label       Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color anisotropic_vdf(color albedo,
                              color extinction,
                              float anisotropy);

// Vertically layer a layerable BSDF such as dielectric_bsdf, generalized_schlick_bsdf or
// sheen_bsdf over a BSDF or VDF. The implementation is target specific, but a standard way
// of handling this is by albedo scaling, using "base*(1-reflectance(top)) + top", where
// reflectance() calculates the directional albedo of a given top BSDF.
//
//  \param  top   Closure defining the top layer.
//  \param  base  Closure defining the base layer.
//
closure color layer(closure color top,
                    closure color base) BUILTIN;

An 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();
}

A simple example of a layered material constructed using the MaterialX closures:

#define BSDF closure color

BSDF glossy_diffuse(color base_color,
                    float base_diffuse_roughness,
                    color specular_color,
                    float specular_roughness,
                    float specular_ior,
                    normal N, vector U)
{
   float  rd = base_diffuse_roughness;
   color  Kd = base_color;
   float  rs = specular_roughness;
   float ior = specular_ior;
   color  Ks = specular_color;
   BSDF diffuse_bsdf = oren_nayar_diffuse_bsdf(N, Kd, rd);
   BSDF    coat_bsdf = dielectric_bsdf(N, U, Ks, color(0.0), rs, rs, ior, \"ggx\");
   return layer(coat_bsdf, diffuse_bsdf);
}

A simple example of a layered shader constructed using the MaterialX closures:

shader glossy_diffuse(color base_color = color(0.8,0.4,0.2),
                      float base_diffuse_roughness = 1.0,
                      color specular_color = color(1.0, 1.0, 1.0),
                      float specular_roughness = 0.01,
                      float specular_ior = 1.5,
                      vector U = 0,
                      output closure color out = 0)
{
   float  rd = base_diffuse_roughness;
   color  Kd = base_color;
   float  rs = specular_roughness;
   float ior = specular_ior;
   color  Ks = specular_color;
   closure color diffuse_bsdf = oren_nayar_diffuse_bsdf(N, Kd, rd);
   closure color    coat_bsdf = dielectric_bsdf(N, U, Ks, color(0.0), rs, rs, ior, "ggx");
   out = layer(coat_bsdf, diffuse_bsdf);
}
Coated diffuse material created with OSL MaterialX closures.

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. hlsl

 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() function returns 1 if the ray being shaded is of the given type, or 0 if the ray is not of that type or if the ray type name is not recognized by the renderer.

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"

More Information

Was this information helpful?