Mod Loader/Custom shaders
A custom shader changes how a material is drawn. To use one on a fabric, build it into a Unity AssetBundle and load it from a C# plugin. Putting a .shader or .shadergraph file in a content pack won't load it.
If you only want a different tint, gloss, or texture size, start with Advanced fabric properties in Fabric Creator. Those settings use the game's existing shader.
Before you start
You need the Unity Editor, Mod Loader, and a C# plugin project. Follow Write a C# mod to set up the project and compile a DLL.
Check BepInEx/LogOutput.log and install the Unity version listed there. The example uses 6000.3.4f1. Create a separate Universal 3D Unity project. Dressmaker uses the Universal Render Pipeline (URP); a shader made for HDRP or the Built-in pipeline needs porting first. See Install Unity for the Editor setup.
The plugin below uses FabricRenderApi (Mod Loader 0.6.92 or later) to apply your shader to the shop preview and garment panels, including saved dresses.
Make a simple shader
Start with a textured Lit graph. Once it loads correctly, add your own effect.
- Create
Assets/Shadersin the Unity Project window. - Right-click that folder and choose Create > Shader Graph > URP > Lit Shader Graph. Name it
ExampleFabric. - Open the graph. In the Blackboard, add the six exposed properties below. Select each property and set its Reference name in the Graph Inspector.
| Property | Type | Reference | Default |
|---|---|---|---|
| Fabric texture | Texture2D | _MainTex
|
White |
| Tint | Color | _Color
|
White, alpha 1 |
| Smoothness | Float | _Smoothness
|
0.25 |
| Use garment UV | Float | _UseGarmentUV
|
0 |
| Pattern scale | Vector2 | _PatternScale
|
1, 1 |
| Pattern offset | Vector2 | _PatternOffset
|
0, 0 |
The Reference names matter: the plugin sets those properties by name. The plugin assigns the texture to _MainTex explicitly.
- Drag Fabric texture from the Blackboard onto the graph. Connect it to the Texture input of a Sample Texture 2D node. Create two UV nodes, one set to UV0 and one to UV1. Connect them to A and B of a Lerp node, and connect Use garment UV to T. Pass that result through Tiling And Offset, using Pattern scale for Tiling and Pattern offset for Offset. Connect its output to the texture sample's UV input.
- Drag Tint onto the graph. Multiply it by the sampled texture colour, then connect the result to the Fragment Base Color block.
- Connect Smoothness to the Fragment Smoothness block. Leave Metallic at zero.
- In Graph Settings, keep the surface Opaque. Save the graph.
Dressmaker stores a cut panel's fabric coordinates in UV1. Shop meshes use UV0. The plugin selects the channel for each view; using UV0 everywhere can make the print jump or stretch when the fabric becomes a garment.
Create a material using the graph and try it on a sphere in Unity. Assign a texture and change Tint and Smoothness to check the connections. Unity's first Shader Graph tutorial explains the graph window and material setup.
Build the shader bundle
Create Assets/Editor/BuildFabricShader.cs in the Unity project and paste this script into it:
using System.IO;
using UnityEditor;
public static class BuildFabricShader
{
[MenuItem("Mod tools/Build fabric shader for Windows")]
private static void Windows() => Build(BuildTarget.StandaloneWindows64);
[MenuItem("Mod tools/Build fabric shader for macOS")]
private static void Mac() => Build(BuildTarget.StandaloneOSX);
private static void Build(BuildTarget target)
{
string output = "ModBundles/" + target;
Directory.CreateDirectory(output);
var bundles = new[] {
new AssetBundleBuild {
assetBundleName = "fabric-shaders",
assetNames = new[] { "Assets/Shaders/ExampleFabric.shadergraph" },
addressableNames = new[] { "ExampleFabricShader" }
}
};
var result = BuildPipeline.BuildAssetBundles(output, bundles,
BuildAssetBundleOptions.ChunkBasedCompression, target);
if (result == null)
throw new System.Exception("Shader bundle build failed. Check the Unity Console.");
EditorUtility.RevealInFinder(output);
}
}
After Unity compiles it, choose Mod tools > Build fabric shader for Windows or the macOS entry. Copy the extensionless fabric-shaders file from the output folder. The accompanying manifest files aren't needed for this single bundle.
Build separate bundles for Windows and macOS. Steam Deck running the Windows game through Proton uses the Windows bundle. Include the graphics APIs you intend to support in the Unity project's Player settings; for example, Direct3D 11 for that Windows renderer and Metal for macOS. Test the shader on a shop bolt and a finished dress before sharing it.
For an existing hand-written shader, replace the graph path in assetNames with its .shader path. Keep the ExampleFabricShader asset alias, or change the plugin's load call to match. Existing shaders may need their own property names and keyword setup.
Register a fabric
Use this as the plugin class in your C# project:
using System;
using System.IO;
using BepInEx;
using DressmakerMods;
using UnityEngine;
[BepInPlugin("example.fabric-shader", "Example Fabric Shader", "1.0.0")]
[BepInDependency("yuu.dressmaker.mods", "0.6.92")]
public sealed class ExampleFabricShader : BaseUnityPlugin
{
private FabricShaderBundle shaderBundle;
private FabricRenderRegistration renderHook;
private bool stopping;
private void Awake()
{
FabricModApi.WhenReady(RegisterFabric);
}
private void RegisterFabric()
{
if (stopping) return;
try
{
string folder = Path.GetDirectoryName(Info.Location);
shaderBundle = FabricModApi.LoadShaderBundle(
Path.Combine(folder, "fabric-shaders"), "ExampleFabricShader");
Fabric fabric = FabricModApi.RegisterFromBase("CottonWhite",
"example.fabric-shader", "rose-cotton", "Rose Cotton", folder,
definition =>
{
definition.Texture = "rose-cotton.png";
definition.Colors = new System.Collections.Generic.List<string> { "Pink" };
definition.Cost = 15;
definition.UnlockLevel = 0;
});
var surfaces = FabricRenderSurface.Shop | FabricRenderSurface.Preview |
FabricRenderSurface.Garment | FabricRenderSurface.Animation | FabricRenderSurface.Unlock;
renderHook = FabricRenderApi.Register(fabric, surfaces, context =>
{
context.UseShader(shaderBundle.Shader);
Material material = context.Material;
material.shaderKeywords = new string[0];
material.renderQueue = -1;
material.SetTexture("_MainTex", fabric.texture);
material.SetColor("_Color", Color.white);
material.SetFloat("_Smoothness", 0.25f);
bool garment = context.Surface == FabricRenderSurface.Garment ||
context.Surface == FabricRenderSurface.Animation;
material.SetFloat("_UseGarmentUV", garment ? 1f : 0f);
Vector2 scale = fabric.material.mainTextureScale;
Vector2 offset = fabric.material.mainTextureOffset;
material.SetVector("_PatternScale", new Vector4(scale.x, scale.y, 0, 0));
material.SetVector("_PatternOffset", new Vector4(offset.x, offset.y, 0, 0));
});
}
catch (Exception error)
{
Logger.LogError("Could not register Rose Cotton: " + error);
renderHook?.Dispose();
shaderBundle?.Dispose();
shaderBundle = null;
}
}
private void OnDestroy()
{
stopping = true;
renderHook?.Dispose();
shaderBundle?.Dispose();
}
}
The example copies White Cotton's fabric type and style tags, then supplies a texture, price, and Pink colour category. A separate render hook applies the custom shader to the views listed in surfaces. Put your own image beside the DLL as rose-cotton.png. The image should already have the colours you want; the example leaves the material tint white.
WhenReady waits until fabrics are available. The callback changes the material used to draw your fabric in that part of the game. White Cotton keeps its original material. UseShader selects your shader. The other Unity material calls set its textures, colours, numbers, keywords, and render queue.
Choose where the shader is used
| Surface | Where it applies |
|---|---|
Shop
|
The fabric shop bolt. |
Preview
|
The bolt's unfolded preview and fabric preview camera. |
CuttingTable
|
The rolled and laid-out cloth on the cutting table. |
Garment
|
Cut panels and mannequin panels, including panels recreated when a dress is loaded. Some flat and mannequin panels share a material. |
Hover
|
The translucent panel preview while placing a piece. |
Animation
|
The moving panel during placement or removal. |
Unlock
|
Fabric bolts shown by the unlock celebration. |
Combine the values with |, or use FabricRenderSurface.All. The example keeps the game's shaders for CuttingTable and Hover. Those views need a cutting mask and a translucent placement preview, which this graph doesn't have.
Before adding CuttingTable, connect the game's pattern mask in your shader. Before adding Hover, enable transparent rendering and use the game's preview opacity. Keep those views out of surfaces until their shader support is ready.
The context lets you read the view's original values before changing the shader. For example, if your graph has a normal-map property named _NormalMap:
if (context.HasOriginalProperty("_Normal"))
context.Material.SetTexture("_NormalMap", context.GetOriginalTexture("_Normal"));
Connect that graph property through a normal-map texture sample to the Fragment Normal block. Use the same approach for _AmbientOcclusion: sample the texture in the graph and connect it to the matching output.
See the Fabric render API reference for every context member and registration option.
Change settings and remove an override
Read your plugin's settings in the callback. After a value changes, call renderHook.Refresh() on Unity's main thread. The loader restores the original view properties before reapplying callbacks, so repeated refreshes don't build up changes.
Call renderHook.Dispose() to remove your override and restore the game material, or any remaining hooks from other plugins. Keep the shader bundle alive until you dispose the hook. Register a new hook if you want to enable it again.
Don't register, refresh, or dispose hooks inside a material callback. A callback that throws has its material changes rolled back and its first error logged. Other callbacks can still run. Avoid creating textures or other Unity objects on each callback; load them once and keep them alive while the hook uses them.
Compile and install
Use the project setup and build command in the C# guide. Reference Assembly-CSharp.dll from the game's Managed folder as well as BepInEx, DressmakerMods, UnityEngine, and UnityEngine.CoreModule. If the compiler asks for netstandard, add the game's netstandard.dll reference too. If you call Unity's AssetBundle methods directly, add UnityEngine.AssetBundleModule.dll too.
With Dressmaker closed, copy these files into the game folder:
BepInEx/plugins/ExampleFabricShader/
ExampleFabricShader.dll
fabric-shaders
rose-cotton.png
This plugin includes a bundle and an image, so install the whole folder manually. The DLL-only installer won't copy those extra files. Start the game and find Rose Cotton in the fabric shop.
Share the whole plugin folder with anyone using this fabric. A dress export won't include the shader plugin or its bundle. Keep the plugin installed so it can add the fabric each time the game starts.
Keep example.fabric-shader and rose-cotton unchanged when updating this fabric after players have used it. Choose your own IDs before sharing your first version.
Test the fabric
- Check the shop bolt and its preview under different lighting.
- Buy the fabric, cut and sew a dress, and inspect it on the mannequin.
- Save and reopen the dress. Check which effects survive in each view.
- Test each platform and renderer you plan to support.
Common problems
| Problem | What to check |
|---|---|
| The fabric is missing | Read BepInEx/LogOutput.log for Could not register Rose Cotton. Check all three files are beside each other.
|
| Shader asset not found | The name passed to LoadShaderBundle must match addressableNames in the build script.
|
| Pink material or unsupported shader | Check the Unity Console for shader errors, then the bundle's platform, URP setup, graphics APIs, and required variants. The exact URP package version used by Dressmaker has not been confirmed. |
| The material is white or ignores its texture | Check the graph's texture Reference is _MainTex and its sampled colour reaches Base Color.
|
| It works on the bolt but looks ordinary on the dress | Check that Garment is included in the hook's surfaces, then check the log for callback failures. The plugin must register the hook again each time the game starts. |
If you add shader keywords, test the variants you actually use. A test material or ShaderVariantCollection can help keep required variants in a bundle; check that each effect still appears after loading the bundle in Dressmaker. See Unity's AssetBundle troubleshooting and the Fabric API reference.