Creating Physical Materials

Materials define the way the surfaces of your meshes react to light.

You define materials within a scene. When you create a mesh, you give each of its triangles a material by name. Then, when you create an instance of a mesh within a scene, each of its triangles uses the material in that scene whose name matches the name of the material assigned to it when the mesh was created. See Creating Meshes. You can also override the materials used in each instance of a mesh. See Creating Mesh Instances.

Beast uses a physical rendering system based on shaders expressed in the Open Shading Language (OSL). To customize the way each material interacts with light, you assign it a shader. Depending on the shader you use, you may also be able to set various input parameters (colors, textures, numeric values, etc.) for the material, which are passed to the shader as input values.

NOTE:The sections on this page detail the recommended way to set up materials in Beast, using the physically based rendering system introduced in release 2013.2. For instructions on using the older, fixed-shader materials system, see Creating Classic Materials.

What is the Open Shading Language?

Open Shading Language is an open-source shading system developed by Sony Pictures Imageworks for use in major feature films.

For more information, including source code, and a complete language specification that also outlines the library of functions you can use in your shaders, see: https://github.com/imageworks/OpenShadingLanguage

OSL in Beast

Since OSL was designed for VFX film production, some aspects of it are not relevant for use when baking textures for games. In particular, Beast does not support:

  • Displacement Shaders
  • Volumetric Shaders
  • BSSDRF shaders (sub surface scattering)
  • Networks of OSL shaders

If you find that you need support for these or any other unsupported aspects of OSL, please contact Autodesk Support. See Support.

Writing an OSL shader file

In order to set up a material, you need to provide a file that contains an OSL shader. A shader can be as simple or as complex as you need in order to express the way your material surface interacts with light to produce its final shading. You can use the sample shader files provided in the shaders sub-directory of the Beast SDK package, or write your own from scratch.

For example, the following code shows a simple shader that multiplies the diffuse closure in OSL with a texture file that is passed as an input parameter:

Surface                                                    //< All Beast shaders use the Surface type.
diffuseTexture                                             //< A name for this shader.
( string diffuseFile = ""                                  //< An input parameter.
     [[  string description = "Diffuse texture file" ]] )  //< Anything inside [[ ]] is optional metadata.
{
    Ci = (color)texture (filename, u, v) * diffuse(N);     //< Beast expects the shader to set the output
                                                           //< closure in the Ci variable.
}

For a more complex and interesting example, see the beastphong.osl file, which models a Phong-type shading similar to the one used by Beast to render "classic" non-physical materials (see also Creating Classic Materials).

Setting up a physical material

Once you have your shader file ready, you need to:

  1. Create a new ILBMaterialHandle.
  2. Initialize the handle by calling ILBCreateMaterial(). In your call, you have to specify the handle of the scene that will contain your material, and the name of the material. This name should match the name of the material assigned to the triangles in your mesh.
  3. Create a new ILBShaderHandle.
  4. Initialize the shader by calling ILBCreateShader(). In your call, you have to specify the handle of the scene that will contain your shader, and the path to your shader file.
  5. Assign your shader to your material by calling ILBSetShader().
  6. Set up any input parameters needed by the shader. See Creating Physical Materials below.
// Create and initialize the material.
ILBMaterialHandle material;
ILBCreateMaterial(scene, _T("materialWithShader"), &material);

// Create and initialize the shader.
ILBShaderHandle diffuseShader;
ILBCreateShader(scene, _T("diffuse"), _T("diffuseTexture.osl"), &diffuseShader);

// Assign the shader to the material.
ILBSetShader(material, diffuseShader);

// Create a new texture, and bind it to the material as an input parameter for the shader.
// Note that the call to ILBSetShaderParamTexture() specifies the name "diffuseFile", which is the
// name of the input parameter in the simple shader file shown above.
ILBTextureHandle tex; 
ILBReferenceTexture(manager, _T("AUniqueName"), _T("C:\textures\wood.exr"), &tex);
ILBSetShaderParamTexture(material, _T("diffuseFile"), tex);

Note that this is a very simple example. A more in-depth usage might involve iterating over the attributes accepted by the shader, querying the type of each attribute (float, color, texture, etc.), and binding the necessary values to the material.

For a more complex example, look at the source for the Maya plugin. A Python script is used to extract the attributes accepted by the shader. These attributes are used to set up the BeastOSL node in Maya. The user can set values in the Maya UI. The settings in the Maya node are then queried, parsed, and bound to the material using that shader. See the createMaterial() method in scenemanager.cpp.

Binding input parameters

You can use the Beast API to set the following types of input parameters for your shaders:

Each of these functions requires a material handle, a string that matches the name of the corresponding input parameter declared in the shader file, and the value that you want to pass to the shader.

Note that the input parameters are bound to a material handle, rather than to the shader handle. This allows you to use the same shader for multiple different kinds of materials, but to specify different input parameters for each of the different materials.

For example, in the examples-physical project, two different materials are created using the same shader, and set up with different parameters:

ILBShaderHandle phongishShader;
bex::apiCall(ILBCreateShader(bmh, _T("PhongishShader"), "../../data/phongish.osl", &phongishShader));
...

// Sphere 1 - Textured lambert
ILBMaterialHandle sm1;
bex::apiCall(ILBCreateMaterial(scene, _T("TexturedLambert"), &sm1));
bex::apiCall(ILBSetShader(sm1, phongishShader));
bex::apiCall(ILBSetShaderParamTexture(sm1, _T("DiffuseTexture"), xorTexture));
bex::apiCall(ILBSetShaderParamColor(sm1, _T("DiffuseColor"), &ILBLinearRGB(0.5f, 0.5f, 1.0f)));
bex::apiCall(ILBSetMaterialOverrides(sphereInstances[1], &sm1, 1));

// Sphere 2 - Phongish
ILBMaterialHandle sm2;
bex::apiCall(ILBCreateMaterial(scene, _T("Phongish"), &sm2));
bex::apiCall(ILBSetShader(sm2, phongishShader));
bex::apiCall(ILBSetShaderParamColor(sm2, _T("DiffuseColor"), &ILBLinearRGB(0.0f, 0.0f, 0.0f)));
bex::apiCall(ILBSetShaderParamColor(sm2, _T("SpecularColor"), &ILBLinearRGB(1.0f, 0.0f, 0.0f)));
bex::apiCall(ILBSetShaderParamColor(sm2, _T("Shininess"), &ILBLinearRGB(400.0f,400.0f,400.0f)));
bex::apiCall(ILBSetMaterialOverrides(sphereInstances[2], &sm2, 1));

Despite using the same shader, the result of rendering the two spheres is very different.

Thread safety

You can create multiple different materials simultaneously in multiple threads. In addition, multiple threads can find materials in the cache and use them simultaneously. You can also modify a single material from multiple different threads at the same time.

Related API functions

API functions related to the creation and setup of materials are declared in the beastmaterial.h file.