On this page |
|
Overview
What are stylesheets
A stylesheet is a collection of styles. A style contains target conditions that match certain primitives, and instructions for how to change the material and/or material parameters on the matching primitives. For example, you could create a style that essentially says "In the char1
object, in the skin
packed primitive, on faces in the shirt
group, change the diffuse color to blue".
Note
Mantra applies stylesheets at render time just before the renderer shades the geometry.
What are stylesheets for?
-
Stylesheets are a workaround for the limitation that you can’t select and apply materials to geometry inside a packed primitive. Instead, you must "target" the packed geometry with a style to set/change its materials.
-
Stylesheets can target geometry that is only "real" at render time. They let you apply material changes (including scripted/random changes) to instanced geometry and agents at render time. This gives you the benefit of instancing the same geometry, but still allows you to make the instances look different.
-
Stylesheets can let you change looks without having to edit the geometry or regenerate the scene description (IFD) file. This may be useful for studios working with huge scenes, where a quick tweak with a stylesheet might be faster than regenerating a huge IFD.
Stylesheet locations and priorities
You can apply stylesheets at different levels:
-
Stylesheet specified on the Mantra command line using the
-S
option. -
You can save stylesheets in a scene (
.hip
) file that can target any geometry in the file. -
You can add a stylesheet parameter to a Houdini object that can target any geometry inside the object.
-
You can specify a stylesheet on primitives using a
material_stylesheet
attribute. On a packed primitive, this can target the packed geometry inside.
Each level can target geometry at lower levels.
If two levels target the same geometry, the higher level stylesheet overrides the lower level. This ordering has a few advantages:
-
A stylesheet on a packed primitive can override any material assignments/parameters on the geometry inside.
-
It lets you override anything at render time by specifying a top-level stylesheet at the command line. This can be useful for huge scenes because you can change material assignments and parameters without having to regenerate the IFD.
Styles listed later in a stylesheet override styles listed earlier.
Viewport display
The viewport shows stylesheet material assignments and overrides on geometry. You can use controls in the viewport display options to enable or disable certain stylesheet features (overrides, packed primitive targets) or turn off stylesheet display completely.
There are some limitations to stylesheet display in the viewport:
-
The effects of stylesheets are only visible in the viewport if the stylesheet assigns a material – or overrides a material parameter – with a GL tag.
-
By default, the viewport will only display up to 100 materials on a single object to keep the display responsive. You can change this limit in the display options window’s Optimize tab.
-
Materials defined "on the fly" in a stylesheet do not appear in the viewport.
-
The viewport will update when the stylesheet changes. However, Houdini will not detect changes to any external scripts or CVEX nodes referenced by the stylesheet. To manually update the display, right click the View Materials button on the display options toolbar (to the right of the viewer) and choose Update stylesheets.
(Alternatively you can click the Update button on the display options window’s Optimize tab.)
-
The viewport cannot currently show material changes for stylesheets that change the target per packed instance if the target is within the packed primitive, when there are more than one of that instance.
Terms
Stylesheet
Contains information about how to override material assignments/parameters on geometry matching certain conditions. A stylesheet can contain styles as well as shared material definitions, shared scripts, and references to other stylesheets.
Style
Contains a target specification and a list of overrides to apply to the targeted geometry.
Styles can contain their own overrides, or reference a set of shared overrides that multiple styles can use.
Target
A list of conditions that match certain geometry. See targets below.
Override
A new material assignment and/or material parameter values to apply to targeted geometry. See overrides below.
Shared material
You can reference materials with overrides in stylesheets, and then assign them to targeted geometry.
Scripts
You can use CVEX scripts in overrides to compute material parameter values at render time. See scripts below.
Target binding
You can "bind" attributes on matched geometry to script arguments. See binding below.
Targets
The target part of a style specifies which geometry to apply the style’s overrides to.
A target can contain four items:
Target type
What type of geometry your rule will apply to.
Primitive
Geometry primitives (such as polygons).
Point instances
Points. You can apply materials to and set attributes on the points which will be inherited by instanced geometry.
Object
Top-level objects, such as characters and props.
Context dependent
Target objects if the stylesheet is on an object, or primitives if the stylesheet is in a primitive attribute.
Conditions
See conditions below.
Sub-targets (optional)
Targets can recursively contain sub-targets, allowing you to match objects within objects, or geometry inside packed primitives.
Target bindings (optional)
This lets you bind the results of matches in the "targets" part of a style to script arguments in the "overrides" part.
Conditions
A target can have the following conditions. If you have multiple conditions, the must all match. If you have no conditions, the style matches everything.
Self name or path
(When the Target’s type is "Context dependent".) If the stylesheet is on an object, This condition is equivalent to an object path condition. If it’s on a primitive, this condition is equivalent to a primitive name or path attribute condition.
Primitive name or path attribute
A primitive number or the value of a primitive’s name
or path
attribute, for example /alembicarchive/*book*
.
Object path
A pattern matching the name or path of an object, for example /obj/subnet1/character1
.
Object category
Matches objects by category tags.
Object bundle
Matches the objects inside the given bundle.
Primitive group
Matches primitives using group syntax.
Point group
Matches any primitives that share the points specified by group syntax.
Vertex group
Matches any primitives that share the vertices specified by group syntax.
Agent shape
Matches a "shape" geometry inside an agent primitive by name.
Multi-level Entity Path
(Deprecated. Only available after loading older style sheets that use this option.) This condition type can be used as an equivelent to the self name or path condition. In addition, this condition can specify multiple levels of nested entities by separate components with forward slashes, for example *book*/45
to specify primitive 45 inside any packed geometry with a name
or path
attribute that matches *book*
. Finally, to specify both an object and primitive path in one condition, the object and primitive parts must be separated by a colon, for example /obj/geo*:*book*/45
. This condition type has been deprecated because all the functionality it provides can be more clearly expressed using nested targets.
Overrides
The overrides of a style specifies how to change the material assigned to the matched geometry. You can assign a new material, and/or change parameter values on the material. You can also specify a CVEX script to compute the override values instead of using static values.
A style can contain different types of overrides. Each override type can be set using a constant value for all targeted geometry, or as a CVEX script that gets run for each targeted component to calculate that component’s value.
Set material
Changes the material assigned to the geometry.
Render property
Changes the value of a render property for the geometry. Render properties are settings that affect the way geometry is rendered but which are not parameters to the surface or displacement shaders. These settings allow you to control, for example, whether the geometry is rendered as a subdivision surface, or whether to use true displacement mapping.
Material parameter
Changes the value of a parameter on the geometry’s material. Overrides of this type affect both surface and displacement shaders.
Surface parameter
Changes the value of a parameter only on the geometry’s surface shader.
Displacement parameter
Changes the value of a parameter only on the geometry’s displacement shader.
Style sheet editor pane
You can edit style sheets using a tree editor in the Data Tree pane. The tree view shows any top-level stylesheets stored in the file, as well as the contents of any stylesheet parameters on objects.
Despite being easier than hand-editing JSON files and string attributes, the editor pane is a pretty thin interface over the JSON format. Items in the tree correspond to objects in the JSON. To add something to the tree, right click an item in the tree and choose the type of item to add under it.
To... | Do this |
---|---|
Create a material style sheet editor pane |
|
Create a new stylesheet |
|
Add a stylesheet to an object |
|
Add a style to a stylesheet |
In the editor pane, right click the stylesheet in the tree and choose Add style. |
Add a target specification to a style |
In the editor pane, right click the style in the tree and choose Add target.
|
Override the material assignment in a style |
You can only have one material override in a style (since it wouldn’t make sense to override the material more than once). |
Override a material parameter in a style |
|
Share the same overrides between multiple styles |
First, create a shared override set that multiple styles can reference:
Then, to reference the a shared override set in a style:
You can reference multiple shared override sets in the same style. This allows you to break common overrides into smaller sets and apply any that are needed in a particular style. |
Change the order of style sheets |
Drag them up or down in the tree. |
Reference the contents of another stylesheet |
|
Find items in the editor |
Use the Filter field to only show rows that contain certain text. For example, you can show the styles that assign a material with a given name. |
Get extra information |
Press on a style, target, or condition item. |
Save a stylesheet as JSON |
|
Solo and mute
When you're creating and editing stylesheets, it can be useful to limit the effects of different items so you can isolate which effects in the render are coming from which styles.
You can use the "solo" and "mute" controls in the stylesheet editor tree to quickly turn stylesheets and styles on and off.
To... | Do this |
---|---|
See only the effect of a single stylesheet or style |
Click the radio button in the Solo column next to the stylesheet or style. |
Hide the effect of a stylesheet or style |
Click the checkbox in the Mute column next to the stylesheet or style. |
Using CVEX scripts
Wherever stylesheets allow you to specify a new material name or material parameter value, you can use a CVEX script to compute the new value at render time instead of specifying a static value.
CVEX scripts are VEX language scripts. CVEX stands for "contextless VEX", meaning no context-specific functions (such as geometry manipulation functions) are available to the script.
The script must have a function with the cvex
return type. Because cvex
functions have no return type, you must use an export
argument to output the result of the script. If the function only has one export
argument, Mantra will use it automatically. If the function has more than one export
, or you can specify which to use as the output using the return
key in JSON.
Note
The name of the CVEX function does not matter. Mantra will simply call whatever function has the cvex
return type.
cvex mySharedFn(int packed_id=0; int unpacked_id=0; export vector c={0,0,0}) { c.r = 0.3 * float(packed_id); c.g = 0.2 * float(unpacked_id); }
Tip
When targeting point instances, you might want to use the point number in the script. Unfortunately this is not possible directly. Points do not have "intrinsics" (computed pseudo-attributes) for the CVEX script to bind like packed geometry does. As a workaround, you can create an attribute on the points containing their point number.
Setting up scripts in the stylesheet editor
To... | Do this |
---|---|
Create a script item to calculate a new material |
In the tree, right-click a style and choose Add override script. Click in the "Type" column to change the override to "Set Material". |
Create a script item to calculate a new material parameter value |
In the tree, right-click a style and choose Add override script. |
Create a script item to share between multiple overrides |
In the tree, right-click the "Shared scripts" item and choose Add shared script. Click in the "Override type" column to set the location of the script: Inline Put a single line of VEX code in the "Override value" column. This might be useful for quick tests or very short scripts, but usually not since you can’t use line breaks (although you can put a Shared script Click in the "Override value" column to choose one of the scripts under the "Shared scripts" item. External script file Enter or choose the location of a script file in the "Override value" column (either a file path or URL). Script from SHOP Choose a VEX SHOP or VOP CVEX SHOP as the "script" to generate the value. This lets you define "script" behavior by wiring up a VOP network. Attribute binding Generates CVEX code that simply sets a material parameter value based on an attribute value (or bound name). Enter the attribute/bound name in the "Override value" column. |
Script bindings
A script can "bind" the value of attributes on the matched geometry to named arguments of the CVEX function.
Higher target levels in a targeting hierarchy can also bind the value of attributes at that level. You can then make those bound data available as arguments to the CVEX script.
For example, suppose a style targets packed primitives 1
and 2
, and a sub-target
matches the color_prims
group inside the packed primitive. If you use a parameter override script, Mantra will run the script for each primitive in color_prims
. You might need the script to know the primitive number of the packed primitive. To accomplish this, you can bind the value of the intrinsic:indexorder
attribute to the name top_level_prim
in the top target. When the script is called, Mantra will pass it a top_level_prim
argument containing the packed primitive number.
To... | Do this |
---|---|
Add a binding to a script or target |
|
Tips
-
When you're creating preview renders in Houdini, Houdini will not automatically detect changes the included stylesheets. If you change an included stylesheet, you may need to manually re-render (for example, by clicking the Render button in the render view).
Examples
Basic style sheets
Basic use of style sheets: how to identify the primitive whose material to override and what to override it with. Also demonstrates the use of VEX scripting to randomize primitive colors.
Style Sheets for Packed Primitives
How to assign materials to primitives inside packed geometry, and nested packed geometry.
Basic Style Sheets for Crowds
How to assign a different texture map to each agent in a crowd.
Style Sheets for Crowd Shapes
How to assign materials to primitive groups inside the shapes that make up the crowd agent geometry.
Style sheet HOM scripting
The hou.styles module and the hou.StyleSheet object let you manipulate stylesheets programmatically in Python.
Style sheet JSON format
A style sheet is a JSON file . The stylesheet editor lets you edit the JSON as a graphical tree, but you can also generate the JSON by hand or programmatically.
The easiest way to see how the JSON works is to create the structure you want in the stylesheet editor and then right click the stylesheet in the tree and choose View JSON.
The top level object has four keys: importFiles, scriptDefinitions, materialDefinitions, and styles.
Example
{ "importFiles" : [ "mss/main_style.json" ], "scriptDefinitions" : { "mss_shared_script" : { "code" : "cvex mySharedFn(int packed_id=0; int unpacked_id=0; export vector c={0,0,0}) { c.r = 0.3 * float(packed_id); c.g = 0.2 * float(unpacked_id); }", "bindings": { "unpacked_id" : "intrinsic:indexorder", "packed_id" : "top_level_prim" } } }, "materialDefinitions" : { "mss_const_mat" : { "properties" : { "surface" : "opdef:/Shop/v_constant clr 0.1 0.1 0.8" } } }, "styles" : [ { "selfTarget" : { "objectName" : "/obj/colored_object" }, "overrides" : { "material" : { "name" : "mss_const_mat" }, "materialParameters" : { "clr" : [ 0.8, 0.1, 0.2 ] } } }, { "target" : { "group" : "1 2", "preBindings" : { "top_level_prim" : "intrinsic:indexorder" }, "subTarget" : { "subTarget" : { "group" : "colored_prims" } } }, "overrides" : { "materialParameters" : { "clr" : { "script" : { "importScript" : "mss_shared_script" } } } } } ] }
Imports
The importFiles
top-level key maps to a list of strings representing stylesheet locations (file paths or URLs). The data from these files are included in the current stylesheet. Shared scripts and override sets in the current file will override things with the same name in included stylesheets.
{ "importFiles" : [ "mss/main_style.json", "prim_style.json" ] }
Shared Scripts
The scriptDefinitions
top-level key maps to an object mapping shared script names to script objects.
{ "scriptDefinitions" : { "mss_shared_script" : { "code" : "cvex mySharedFn(int packed_id=0; int unpacked_id=0; export vector c={0,0,0}) { c.r = 0.3 * float(packed_id); c.g = 0.2 * float(unpacked_id); }", "bindings": { "unpacked_id" : "intrinsic:indexorder", "packed_id" : "top_level_prim" } } } }
Material Definitions
Theoretically, you can define materials inside a stylesheet. This would allow you to make a self-contained stylesheet that could introduce new materials into an already-generated IFD file. In practice, this requires that you know enough about IFD to write a raw IFD shader string.
Additionally, the VEX code to define the material’s surface or displacement shader can be embedded into the material definition. This requires using the reserved shader names "mss:surface_shader" or "mss:displace_shader" depending on the type of shader. The VEX code is then added as a property with that same name to the material definition. Again, this is most likely to be useful in a large pipeline where style sheets can be generated automatically by python scripts, rather than created manually.
{ "materialDefinitions" : { "mss_const_mat" : { "properties" : { "surface" : "opdef:/Shop/v_constant clr 0.1 0.1 0.8", "displace" : "mss:displace_shader parm 0 0 1", "mss:displace_shader" : "displace disp1(vector parm=0) { P = P + parm; }" } } } }
Styles
The styles
top-level key maps to a list of style objects.
A style object can have the following keys:
-
target
: target object -
overrides
: overrides object
{ "styles" : [ { "target" : { "type": "self", "objectName" : "/obj/colored_object" }, "overrides" : { "material" : { "name" : "mss_const_mat" }, "materialParameters" : { "clr" : [ 0.8, 0.1, 0.2 ] } } } ] }
{ "target" : { "group" : "1 2", "preBindings" : { "top_level_prim" : "intrinsic:indexorder" }, "subTarget" : { "subTarget" : { "group" : "color_prims" } } }, "overrides" : { "materialParameters" : { "clr" : { "script" : { "importScript" : "mss_shared_script" } } } } }
Targets
A target
object specifies which entities the style will affect. See targeting geometry.
It can have the following keys:
type
: "string"
If this key is set to "self"
, this target must match the thing the stylesheet is attached to, instead of the thing’s contents.
If the stylesheet is attached to a scene file, this has no effect.
If the stylesheet is attached to an object, the "self" target must match that object. If the stylesheet is attached to a primitive in an attribute, the "self" target must match that primitive. This only makes sense on the "top" target in a style, not on a sub-target.
name
: "object name"
For objects, the name or path of the object, for example /obj/geo*
or simply geo*
. For primitives, either the primitive number or the value of a name
or path
attribute.
objectName
: "object_path"
Matches the path to an object. You can use this even at the primitive level to match the object containing the primitives. This lets you avoid having to use two levels to target the object and then the primitives.
path
: "path string"
One or both of:
-
The network path to an object. For example,
/obj/subnet1/char1
. -
A path of "names" to a primitive. As with the
name
target type, a "name" can be a primitive number or the value of aname
orpath
attribute. For example,agent_0/shape/0
.
If you use both, you must separate them with a colon, for example: /obj/crowd_object:agent_0/shape/primitive
. This is equivalent to using multiple nested targets, but is less clear when expressed in the user interface, so this option has been deprectated in the Style Sheet Editor pane.
group
: "group spec"
Matches primitives using the group specification syntax.
{ "target" : { "group" : "0 5 10 some_group" } }
{ "target" : { "group" : "@Cd.r>0.5" } }
shape
: "string"
Match a certain "shape" inside an agent primitive by name.
subTarget
: target object
Recursively matches entities inside whatever entities match this target.
For example, this target object would match a packed primitive with primitive ID #0, and then match the primitive #10 packed inside:
{ "target" : { "group" : "0", "subTarget" : { "group": "10" } } }
preBindings
: {"attribute name": "bound name"}
An object takes attributes on the matched geometry, and assigns names to the values of those attributes, so they can be passed to CVEX scripts as arguments. See binding.
Overrides
An overrides
object specifies how to change the material on the primitives targeted by a style. See overrides.
An overrides object can have the following keys:
material
: {"name": "value"}
The value of the name
key inside can one of:
-
A string specifying the path to a material node.
-
If you are using embedded materials it can be the name of a shared material.
materialParameters
: {"parameter name": value}
Changes the values of parameters on the material assigned to the matched geometry.
{ "material" : { "name" : "/shop/material1" }, "materialParameters" : { "clr" : [ 0.8, 0.1, 0.2 ], "reflect": 0.5, } }
The values inside the three keys can be a literal value (a string, number, or array), or an object like {"script": script object}
, where the value of the script
key is a script object referencing a CVEX script to use to compute the value. See using CVEX scripts.
{ "materialParameters" : { "clr" : { "script" : { "importScript" : "mss_shared_script" } } } }
Script objects
See how to use CVEX scripts. The object has the following keys:
One of the following keys
command
: "path to .vex file"
Use the script at the given location (file path or URL).
importScript
: "shared script name"
References a shared script in the scriptDefinitions
object.
code
: "inline VEX code"
A single line of VEX code in a string.
return
: "argument name"
Since a cvex
function has no return type, you must use an export
argument to output the result of the function. If the function has more than one export
, this lets you specify which argument to use as the result.
bindings
:
An object mapping primitive attribute names to function arguments. This lets you pass the contents of an attribute/binding to the function as an argument. See bindings format.
{ "command" : "$HIP/cvex/recolor.vex", "bindings": { "unpacked_id" : "intrinsic:indexorder", "packed_id" : "top_level_prim" } }
Bindings object
A bindings object maps function arguments to attributes or other values by name. The keys in the object represent the function argument names. The stylesheet format supports a few different binding methods for the values.
The default binding style associates an argument name with an attribute name (note that this can include intrinsic attributes). The value is a string naming an attribute. If no attribute with the given name is found, it looks for a target binding by that name.
Alternatively you can specify a different type of binding using an object instead of a string. This object has a sourceType
key that determines the binding method:
default
This is the same as using the default format mapping to a string as shown above. The object has a name
key to specify the attribute (or target binding) name.
"bindings": { "foo": { "sourceType": "default", "name": "P" }, "bar": { "sourceType": "default", "name": "intrinsic:indexorder" } }
preBinding
This skips the check for a geometry attribute, and just looks for a target binding with the given name.
"bindings": { "foo": { "sourceType": "preBinding", "name": "aTargetName" } }
constant
This associates the function argument with a constant value.
"bindings": { "stringArg": { "sourceType": "constant", "value": "string" }, "intArg": { "sourceType": "constant", "value": 23 }, "vectorArg": { "sourceType": "constant", "value": [1.0, 1.0, 2.0] } }