Materials

The tables on this page are generated

From Asset Rules (Generated), which is the same JSON the validators read. Change a rule in the
bubbly-asset-rules repo, not here.

Naming

A material is named for the texture set it is built from, not for the mesh it lands on. One
wallpaper material goes on a wall, a screen and a scrap of torn paper, and naming it after any of
the three is naming it after an accident.

So M_ reads the texture category list, not the model one:

M_Surface_Wallpaper_01          the material of the T_Surface_Wallpaper_01 texture set
M_Surface_Wallpaper_01_Cream    a tint variant of it, sharing the same textures
M_Trim_AbandonedTavern_01    a kit's trim material, named for the Set
M_Cutout_Leaf_01             foliage, alpha-clipped
M_Effect_Smoke_01                unlit, read by a VFX graph

Swap the prefix to find the pair. T_Surface_Wallpaper_01 ↔ M_Surface_Wallpaper_01, the same way
SM_Prop_Mixer_01 ↔ P_Prop_Mixer_01. That is the whole reason the rule exists, and it only works
while nobody invents a nicer name for one half of it.

A material that blends several sets (a terrain material, a vertex-blended wall) is a new thing
and gets its own descriptor. A mask read over a surface does not make a second set.

Materials

Id Rule Severity Value
MAT001 Material built from one texture set does not carry that set's name warning true
MAT002 Material variant does not carry its base material's name warning
MAT003 Blender material does not name the mesh part it fills info

shape

Key Value
M_Surface_Wallpaper_01 the material built from the T_Surface_Wallpaper_01 texture set
M_Surface_Wallpaper_01_Cream a tint variant of it, sharing the same textures (see MAT002)
M_Trim_AbandonedTavern_01 the material for a kit's trim sheet, named for the Set the sheet is named for
M_Cutout_Leaf_01 a foliage material, alpha-clipped
M_Effect_Smoke_01 an unlit effect material read by a VFX graph
M_Mask_Rust_01 the rare case where a mask ships as its own material rather than as a channel of one
M_Surface_OldChair_01 a one-off baked material for a single prop; the texture set is T_Surface_OldChair_01

Pattern: M_[Category]_[SubCategory|Set_SetNum]_[Descriptor]_[Num]_[Variant]

variants

Key Value
suffixFormat PascalCase
suffixToken RoleSuffix

Variants

The RGB-mask workflow means several materials per texture set is normal: one greyscale base
texture, one tint mask, and a material per colourway. A variant is the base name plus one
PascalCase word, like M_Surface_Render_01_Cream, so sorting puts it next to what it varies.

The word names the variation, not the place it was first used. Cream, Wet, Mossy, Charred.
Not Tavern, which becomes a lie the second time the material is used.

Blender materials

SMM_ is a different job and keeps the model categories. It names a slot on a mesh (the part
of the model this material covers), so it carries the mesh's own name with a required suffix:
SM_Prop_Kitchen_Mixer_01 has SMM_Prop_Kitchen_Mixer_01_Body and SMM_Prop_Kitchen_Mixer_01_Bowl.

An SMM_ material does not survive import. Unity gets a slot with that name on it and you
assign a real M_ material to the slot. Which one goes in which slot is intentionally not derivable
from the names. The point of a slot is that the same Body takes M_Surface_Steel_01 on one variant
and M_Surface_Brass_01 on another.

Where they live

M_ materials live in the Unity project, under the game's own Materials folder. Where a project
splits that folder, name the subfolders exactly as the category (Materials/Surface,
Materials/Cutout), so a material name decides its folder the way a mesh name does.

Assets/Source/Materials is a different thing and keeps its name: it holds the InstaMat and
Substance Designer graphs that generate textures.

Assets/Exported/Materials is now Assets/Exported/Textures

It never held materials. It held textures, and the misnamed folder was most of the reason nobody
could say where a material goes. See Project Folder Structure.

Included Maps

There are a few different types of textures we use. The naming for them is here:

Textures

These are the only valid categories for textures.

Texture categories

Sprite · Effect · Mask · Trim · Cutout · Surface

They are not one axis, which is why a ladder is needed here too: Surface is what a texture is for,
Cutout is how it is authored, Trim is how it is laid out, Mask is what it carries,
Sprite is where it is drawn and Effect is what reads it. A leaf atlas answers to two of those at
once. See Naming Conventions > Which texture category is it below.


Role-Suffix

The role a file plays in a set of files. For a texture that means the map type, from a closed list;
for a SMM_ material it is the part of the mesh it belongs to, and it is required there.

Per-type overrides

Where a type needs something the single pattern cannot say.

SM_: 3D Model

Token Required Accepts
RoleSuffix optional PascalCase

Regex: ^SM_(?:Character|Effect|Kit|Prop)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:\d{2}))?(?:_(?:V\d+))?(?:_(?:[A-Z][A-Za-z0-9]*))?$

SMM_: Material (.fbx/.blend)

Token Required Accepts
$note optional any part
RoleSuffix yes PascalCase

Regex: ^SMM_(?:Character|Effect|Kit|Prop)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:\d{2}))?(?:_(?:V\d+))?_(?:[A-Z][A-Za-z0-9]*)$

M_: Material (Unity)

Token Required Accepts
$note optional any part
Category yes Sprite, Effect, Mask, Trim, Cutout, Surface
RoleSuffix optional PascalCase

Regex: ^M_(?:Sprite|Effect|Mask|Trim|Cutout|Surface)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:\d{2}))?(?:_(?:V\d+))?(?:_(?:[A-Z][A-Za-z0-9]*))?$

P_: Prefab

Token Required Accepts
$note optional any part
Category yes Character, Effect, Kit, Prop, UI, System, Gameplay, Audio
RoleSuffix optional PascalCase

Regex: ^P_(?:Character|Effect|Kit|Prop|UI|System|Gameplay|Audio)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:\d{2}))?(?:_(?:V\d+))?(?:_(?:[A-Z][A-Za-z0-9]*))?$

T_: Texture or Sprite

Token Required Accepts
$roleNote optional any part
Category yes Sprite, Effect, Mask, Trim, Cutout, Surface
RoleSuffix optional Albedo, Roughness, Normal, AO, Metallic, Emissive, Height, Damage, EdgeMask, Packed, Flipbook, Noise, Gradient, Distortion, MotionVector

Regex: ^T_(?:Sprite|Effect|Mask|Trim|Cutout|Surface)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:\d{2}))?(?:_(?:V\d+))?(?:_(?:Albedo|Roughness|Normal|AO|Metallic|Emissive|Height|Damage|EdgeMask|Packed|Flipbook|Noise|Gradient|Distortion|MotionVector))?$

SMA_: Static Mesh Animation

Token Required Accepts
Action optional PascalCase

Regex: ^SMA_(?:Character|Effect|Kit|Prop)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?(?:_(?:V\d+))?$

VFX_: Visual Effect

Token Required Accepts
Action optional PascalCase

Regex: ^VFX_(?:Character|Effect|Kit|Prop)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?(?:_(?:V\d+))?$

AU_: Audio Clip

Token Required Accepts
Category yes SFX, Music, Ambience, VoiceOver, UI
Action optional PascalCase

Regex: ^AU_(?:SFX|Music|Ambience|VoiceOver|UI)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?(?:_(?:V\d+))?$

SC_: Scene

Token order: Type_Descriptor_RoleSuffix

Token Required Accepts
$layerNote optional any part
RoleSuffix optional Base, Environment, Gameplay, Lighting, Audio, UI

Regex: ^SC_(?:[A-Z][A-Za-z0-9]*)(?:_(?:Base|Environment|Gameplay|Lighting|Audio|UI))?$

CAM_: Camera

Token order: Type_Descriptor_Descriptor2_Descriptor3_Num

Token Required Accepts
Num optional /^\d{2}$/

Regex: ^CAM_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?$

LGT_: Light

Token order: Type_Descriptor_Descriptor2_Descriptor3_Num

Token Required Accepts
Num optional /^\d{2}$/

Regex: ^LGT_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?$

VOL_: Volume

Token order: Type_Descriptor_Descriptor2_Descriptor3_Num

Token Required Accepts
Num optional /^\d{2}$/

Regex: ^VOL_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?$

SYS_: System

Token order: Type_Descriptor_Descriptor2_Descriptor3_Num

Token Required Accepts
Num optional /^\d{2}$/

Regex: ^SYS_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?$

DA_: Data Asset

Token Required Accepts
Category yes Achievement, Action, Audio, Brain, Character, Condition, Craft, Goal, Item, Narrative, Progression, Quest, Taxonomy, Thought, UI, World
Action optional PascalCase
RoleSuffix optional PascalCase

Regex: ^DA_(?:Achievement|Action|Audio|Brain|Character|Condition|Craft|Goal|Item|Narrative|Progression|Quest|Taxonomy|Thought|UI|World)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?_(?:[A-Z][A-Za-z0-9]*)(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:[A-Z][A-Za-z0-9]*))?_(?:\d{2})(?:_(?:[A-Z][A-Za-z0-9]*))?(?:_(?:\d{2}))?(?:_(?:V\d+))?(?:_(?:[A-Z][A-Za-z0-9]*))?$

Every material ships Albedo, Roughness and Normal. The rest are optional.

Unity applies the import settings for each of these automatically, from the role suffix in the file
name. See Texture Workflow > Import Settings.

Channel Packing

We use channel packing where we can to make it all more efficient, but the shader supports both.
A packed texture is named _Packed.

Channel Type
R Metallic
G AO
B Detail
A Roughness

RGB Masking

For colour variation on modular assets, the base texture is painted in values only and a mask tints
it per material. Each channel maps to one of the material's colours:

Channel Colour
R Primary
G Secondary
B Tertiary

See Modular 3D Assets - Texturing > RGB Masking for why, and for the worked example.