Open Shading Language (OSL) Support

Overview

3ds Max supports the Open Shading Language (OSL), in 3ds Max 2019 and higher. OSL is implemented as an "OSL Map", which is an execution environment for OSL shaders inside of 3d Max, and is exposed to the API as a regular C++ 3d Max shader (that is, it is a subclass of Texmap and implements EvalColor(), EvalNormal() and EvalNormalPerturb() methods).

OSL works in any renderer that supports the 3ds Max shader API, such as Scanline, vRay, Corona, and so on. It can also work with renderers that support OSL natively, such as Arnold. In this case, the renderer rather than 3ds Max executes the OSL code.

OSL also works outside of renderers, anywhere in 3ds Max where a regular map is requested, such as in the Displacement modifier.

OSL is a simple to understand shading language, and writing a shader in OSL is orders of magnitude less effort than developing the equivalent functionality as a 3ds Max C++ shader. Instead of dealing with complex plug-ins, building the UI manually, and working with "validity intervals" and other details of the 3ds Max APIs, you simply type some OSL code in a text file.

OSL also benefits from JIT compilation and optimization of entire shade trees at once, as long as all the shaders in the shade tree are OSL shaders. 

Developing OSL Maps

There are two fundamental workflows for the OSL Map, using OSL Maps in the Material Browser, and developing (editing, changing, or writing) OSL Maps. We will discuss the development workflow here. See the 3ds Max User Guide for general usage information.

OSL shaders that populate the material browser come from subfolders named OSL inside plug-in directories, both the standard 3ds Max plug-in directories, and third-party plug-in directories added on the Customize > Configure System Paths dialog. There is also a "system" OSL folder which contains basic shaders shipped directly by Autodesk, where things like the OSL basic headers reside.
Shaders are loaded from:

Tip: If the OSL source file is put in a subfolder of one of these OSL folders, it will show up in the Map Browser in a subcategory named after the folder.

In this workflow, all shaders are hosted in files in the plug-in folders, and the code in the file is what is being used at render time. Add a parameter to the code in the file, and the shader will update with the new parameter. Change the algorithm, and the rendering result will change.

There are two ways you can work with OSL Map source files, in memory or on disk. When the OSL Map is linked to the file on disk, changes to that file are detected and re-loaded and re-evaluated. This is true whether the file is loaded in the OSL source editor, or in another external editor.

When OSL source files are "unlinked" from the source file on disk, they are held in memory, and persist in the scene file across save/load operations. Changes are not saved to the original OSL file, unless you re-link or save under another file name. The scene can be rendered on other machines, since there are no external file dependencies. To make the shader available to other scenes, the map can be saved to one of the OSL directories as a new file, and will then appear in the OSL section of the Material Map Browser.

You can also start with an OSL Map template, which contains skeleton code for creating a new OSL shader from scratch. This shader is in the General maps section named "OSL Map". This is an empty OSL shader that is in "unlinked" mode by default.

You can switch between linked and unlinked mode during the shader development process, depending on how you want to manage the OSL source. In general, operating in unlinked mode allows you to iterate and test changes as you progress, without altering the original source. You can re-link to either save as a new shader or update the original source, and to make the shader available to other scenes.

Writing an OSL shader for 3ds Max

A complete description of OSL is available in the Open Shading Language 1.9 Specification.

General points about OSL in 3ds Max:

OSL Specifics in 3ds Max 2019

This section covers some of the specifics about how OSL is supported in 3ds Max, including some of its limitations.

OSL Closures Not Supported

Most importantly, it can only be used for procedural texturing. OSL Closures are not supported in this version which means it is useful for texturing, but not for making materials.

There are several reasons for this, mostly technical, but one of them is that while the OSL language is well specified in its feature-set, the set of closures supported is renderer dependent, and can shift from renderer to renderer. We at Autodesk have a vision of making textures and materials interchangeable, and before a standard for closures is established, that is hard to accomplish.

Therefore, the recommended workflow is to build your procedural texture maps in OSL, but connect the outputs to a standardized, well defined, renderer-independent material, such as the 3ds Max Physical Material.

Parameter Type Support

A second limitation is that only base types are supported for parameters. This means integers, floats, strings, and point/vector/normal data types. Complex types like structs or arrays are not supported.

Note that this only applies to parameters, structs and arrays can be used inside the shader code itself.

No OSL Include Directive

Finally, due to the mode of embedding the OSL code into the OSL Map itself, file inclusion is not supported. The standard OSL include file "stdosl.h" is implicitly included automatically by the compiler, you do not need to include it explicitly. If you have this line in your source, you can delete it:

#include <stdosl.h>

Including other files will not work. For a shader that uses include files, you need to copy the relevant content from that file into the OSL source itself.

Global Variables and Attributes

OSL by default works in a "common" coordinate space that can (in theory) be different from renderer to renderer. In practice, most renderers use world space for this coordinate space, and this is true also for the OSL Map.

This means that the OSL global parameter P is in world space. Many shaders use the above mentioned feature of a computed default so that the shader works immediately, by having a line such as

    vector Input = P

This will make the Input parameter have the world space point as a default. Since texturing things in world space may not be what a user expects (since moving the object would drag it "through" the texture space, it is probably more useful to most users to instead use object space, which would look like this:

    vector Input = transform("object", P),

Or, if it is a 2D texture, use the default UV space since 3d Max populates the global OSL variables u and v with the default 3d Max UVW map channel 1:

    vector Input = vector(u, v, 0),

Also, since in theory (according to the OSL specification) P is in "common" space, and you actually want it in world space, the correct way of transforming it is:

    vector Input = transform("world", P),

However, in the OSL Map's execution environment, this does nothing, since P is already in world space.

Scene Attributes

The OSL rendering state is filled in before 3d Max executes the shader. The standard variables such as P, N, and I are filled in, where the u and v variables gets pre-populated with map channel 1's UV coordinate.

OSL also allows the generic getattribute() function to get a named attribute from the scene. It is up to the renderer executing the OSL code to populate these named attributes, which means it is outside the control of the OSL Map itself to guarantee whether these attributes are defined.

This is the set of attributes the OSL Map itself uses, that can be guaranteed to exist when rendering inside of 3ds Max and using the OSL Map as the execution environment:

Attribute Name Data type Description
UVxx point The 3D UVW coordinate for map channel xx (UV0 to UV99), for example UV1.
mtlID int The material ID of a face, or particle ID of a particle.
gBufID int The Object ID in the 3d Max object properties dialog.
nodeName string The name of the instance in 3d Max. OSL has a built-in attribute geom:name, but that will be what the renderer calls the object, which isn't necessarily the same thing.
nodeID int What NodeID() in the ShadeContext returns.
nodeRenderID int What INode::GetRenderID() returns.
nodeHandle int What INode::GetHandle() returns.
wireColor color The instances wireframe color.
paLife float Normalized particle age (0.0-1.0 over the age of the particle)
usr_xxxxx int
float
string
float,float,float
Any value from the 3d Max Object Properties User Defined page, as an integer, float, string, or three comma-separated floats.
For example,Hello=3.0"`, will allow you to get the float attribute usr_Hello from the OSL code to retrieve the value 3.0.

Remember this list is defined by the renderer, and is only guaranteed when run inside the OSL Map. For example, Arnold uses a slightly different naming to store UV coordinates. Therefore we recommend you do not deal with UV channels directly in your OSL code. Instead, add an input for the coordinates, defaulting it to vector(u, v, 0), which is guaranteed to yield a meaningful default value for 2D textures. Then, let the user connect the Autodesk-supplied GetUVW shader, which has code in it to understand the different UV attribute formats for the supported renderers.

Shader Metadata

In OSL metadata can be assigned to whole shaders, or individual parameters. 3ds Max supports a subset of those metadata settings, as well as some of its own.

For the shader, the following metadata are supported

Name Meaning
label The "display name" of the shader. This is what is displayed the Material Browser, as well as the title in the Material Editor and on the node in Slate Material Editor.
help A short help text displayed the top of the material editor.
This can contain a subset of HTML formatting.
URL A link to a more extensive help page about the shader.
logo An image loaded from the same folder as the OSL shader, and displayed at the bottom of the material editor in place of the default logo. This Allows you to personalize the look of your shaders, or even include a sample image of what it does directly in the UI. If the file cannot be found, the default logo is displayed.
category Sorts this shader in a particular category in the 3d Max Map browser.
By default, categories are determined by the subdirectory the shader is in, but this can be overridden by a specific category metadata value. It allows nested categories separated by a backslash, for example "Math\Color" puts the in the "Math" category, with a sub-category of "Color".

For individual parameters, the following metadata are supported:

NameDescription
labelThe "display name" of the parameter. The parameter name displayed in the Material Editor.
helpThe tooltip of the control. This can contain a subset of HTML formatting.
min, maxThe minimum and maximum value of the parameter.
widgetThe kind of UI control to use for the parameter. Supports the following OSL widget types:
  • "filename" - displays a file selector and makes the pointed-to file a 3d Max asset.
  • "checkBox" - displays an integer value as a checkbox.
  • "popup" - displays a popup list of choices.
  • "mapper" - displays a popup list of choices that maps to other values such as integers

See the OSL Specification for details

timeValueIf enabled (set to 1), any integer parameter this metadata is assigned to automatically gets populated with the current frame number, and any float parameter gets automatically populated with the current scene time, in seconds.

Metadata Example

Here is a visual example of how metadata shows up in an OSL shader:

3ds Max OSL Shaders on GitHub

There is a public GitHub for exchange of 3ds Max OSL shaders on the ADN Dev Tech Repository.