Multi-material: Using custom Inspectors and Scriptable Objects for UDIM materials
The Unity Labs team is experimenting with bringing film-quality assets, designed for maximum fidelity but non-realtime rendering, into the 60+ FPS world with Unity. Since film and TV formats and pipelines (such as Ptex, USD & UDIM) are not widely supported in realtime engines, working with these assets requires new techniques and tools. Beyond the obvious issues with the size and density of these assets, there can often be a large number of materials and texture files involved with a single asset (such as the case with UDIM), which we’ll need to easily interact with and modify. To streamline this workflow, we’ve created a new Multi-material Editor to help manage and interact with all these materials.
UDIM and other multi-tiled UV mapping techniques are not a new file type, but a pipeline for UVing and a set of conventions for naming source texture files. These multi-tiled UV mapping techniques can increase the number of materials beyond what is reasonable to simultaneously edit and keep in sync with the current material inspector, and multi-selecting materials every time is not very practical.
In the Multi-material Editor, we assume that the individual tiles have their own material assignment from the source asset, which is the most common pipeline. If that’s not the case in your pipeline, you could use a custom mesh importer to break the mesh up into individual submeshes with corresponding materials at asset import time.
Download the Multi-material Editor. To install, simply create a project in Unity 5.5 or newer. Then add this repo in the assets folder of your project. There are 2 new scriptable object types that can be accessed by right clicking in the project view in the Create menu.
Material Texture Settings is under Create > Multi Material > Material Texture Settings and can be used to assign textures to UDIM materials. The custom inspector is able to apply the texture setting to selected materials and assumes the material and texture are named according to UDIM conventions. Texture Name is the material property name of the texture field and Search Dir is the directory that contains the textures for that material property. Multi Material Data is under Create > Multi Material > Multi Material Data and holds an array of Materials for use in Multi Material Component or you can directly modify the materials from the scriptable object’s inspector.
The Multi-material Component can be added to a game object to control the Shared Materials array in the object’s renderer. If a Multi Material Data is created from the render, or assigned from the project, that data can be modified here. This can be useful if you have multiple objects that share a group of materials that need to be modified at once.
About the Tool
We need the tool to allow us to interact with multiple materials on an object or across objects as if they were only one material, except for the case of texture assignment. To allow the most options, we split the material data out into its own class so it could be created and serialized on a component based on the object’s renderer, or saved as an asset in a scriptable object, to be shared across multiple objects. We then built a custom inspector for viewing all the materials within this single custom inspector using a separate custom drawer class. This uses the standard material inspector, but also supports any custom material inspectors you may be using.
Materials, and the Unity Standard Shader in particular, use custom UI with logic built into the inspector, so we are unable to just use the standard Property Drawers for a material’s serialized properties. This means that to interact with a material in a meaningful way we need to use the Material Editor Inspector within our custom inspector. It is possible to insert the drawing of an editor (with its public GUI methods) inside of another editor or EditorWindow if we create and manage that inserted editor. In the Multi-material Editor, the MaterialArrayDrawers class lets us do just that with the Material Editor as seen in the MultiMaterialEditor and MultiMaterialDataEditor. The coding pattern used for this editor can also be used in other cases where you want to draw an inspector within another editor, which can be useful if you want to draw a custom inspector for a Scriptable Object directly in a component that uses that object.
There are some important things to keep in mind both for performance and interaction limitations in the Material Editor when trying to draw it inside another editor’s OnInspectorGUI. First, the header GUI can change the layout and rect context you set in the inspector. Also if the header contains any controls (for example, the shader menu), you cannot detect changes in that control with BeginChangeCheck and EndChangeCheck. This means we are not able to sync the changes on the shader selection when they happen. To keep the shaders in sync, we recreate the material header as close as possible with DrawMaterialHeaderMaterialView(). Note the isVisibleField where we have to reflect into the material editor to show the body of the editor, and ShaderPopup, where we try to replicate internal editor drawing styles but are not able to display the Unity internal shaders unless they are already loaded.
Second, since we are drawing the whole Material Editor’s OnInspectorGUI(), we do not know what property drawer has changed, just that something in the OnInspectorGUI() block changed. This means there are potential changes anywhere in the material that could need to be synced to the other materials. If we continuously sync all the properties of even a small array of materials with an interactive control like a color picker, we can significantly impact performance.
Therefore for best performance when syncing materials, we need a way to only change the properties that have been modified. We do a quick compare between the actively modified material and the first different material in our Multi-material, and only cache those serialized properties that are different—except textures—between the two materials. Then we only apply those cached changes to all of the other materials. Unfortunately, you can get some false positives if you have materials that are greatly different, or it can miss the change if it happens to match the compared material. As a workaround, we have added a Sync to Material button on the active material which copies all the properties across the Multi Material.
The Multi-material and its editors show how you can customize Unity’s UI for specific workflows you may want or need on your project. Some future areas to explore are building some more generic drawers to display multiple editors inside another component’s inspector, filtering Serialized Properties you want to sync (eg. not just ignoring textures), and handling animated materials and/or runtime-scripted interaction with the Multi-material.