


v2.0.0 — A mod for Valheim on Apple Silicon (ARM Mac) that fixes broken/pink materials caused by mod shaders compiled without Metal GPU support.
Do not run this mod unless you are on an Apple Silicon Mac.
I created this mod for myself but it has made such a meaningful difference that I wanted to share it with the community. It is a work in progress and no guarantees are provided.
To get the most out of playing on an ARM Mac, take the time and watch this instructional video here: https://youtu.be/SQCVkaRel40?si=wxOIYrW1vIzIdtzh
Valheim mods ship Unity asset bundles with shaders compiled only for Windows (Direct3D 11). When loaded on macOS with the Metal graphics API, Unity cannot find a Metal-compatible shader variant and substitutes a bright magenta/pink error material. This happens silently — the mod author doesn't need to do anything wrong for it to occur.
BepInEx/plugins/ShaderHelperForMac-DrummerCraig/
ShaderHelperForMac.dll
AssetsTools.NET.dll
Located at BepInEx/config/drummercraig.shaderhelperformac.cfg.
| Setting | Default | Description |
|---|---|---|
Default All Transparent |
false |
When true, all unsupported shaders are made transparent automatically. When false, shaders are left pink unless the mod is explicitly listed in a config file or covered by a per-mod txt file. |
Force Transparent Shader Prefixes |
(empty) | Comma-separated shader name prefixes always made transparent, regardless of other settings. |
Force Fallback Shader Prefixes |
(empty) | Comma-separated shader name prefixes always replaced with the fallback shader, even if Unity reports them as supported. Use for shaders that render pink despite isSupported=true. |
Periodic Sweep Interval |
30 |
How often (seconds) to re-sweep all materials. Set to 0 to disable. |
Located at BepInEx/config/ShaderHelperForMac/. Create one file per mod, named after the mod's DLL (e.g. WardIsLove.txt). Use shaderhelper initmod <DllName> to auto-generate a starting file.
Each file uses section headers to control behaviour for that mod:
[forcefallback]
# Shader name prefixes that render pink despite isSupported=true.
# Start here when solid objects (armour, weapons, furniture) are pink.
SomeAuthor/SomePinkShader
[forcetransparent]
# Same as forcefallback but hides the object (for particles/effects).
[fallback]
# Specific materials to replace with the solid fallback shader.
mat:some_material_name
suffix:_glow # matches any material ending with _glow
[transparent]
# Specific materials to make invisible.
mat:some_particle
suffix:Trail
[original]
# Specific materials to leave completely untouched.
[default]
fallback # or: transparent, original
Files are reloaded automatically when changed. Run shaderhelper sweep to apply immediately.
Also located at BepInEx/config/ShaderHelperForMac/. These apply a single action to all broken-shader materials in a mod.
| File | Effect |
|---|---|
usetransparentshader.txt |
Makes all broken-shader materials in the mod invisible. |
usefallbackshader.txt |
Replaces all broken-shader materials with a solid visible placeholder. |
useoriginalshader.txt |
Leaves the mod's shaders completely untouched. |
One DLL name per line (without .dll), case-insensitive. Lines starting with # are comments.
Open the in-game console with F5.
| Command | Description |
|---|---|
shaderhelper sweep |
Re-run the material sweep immediately. Reloads config files first. Shows a HUD notification when complete. |
shaderhelper reload |
Reload all config files without running a sweep. |
shaderhelper initmod <DllName> |
Auto-generate a starter txt file for a mod based on what the plugin has already seen. |
shaderhelper initmod <DllName> force |
Regenerate the txt file even if one already exists. |
| Command | Description |
|---|---|
shaderhelper nearby [radius] |
Log all materials on objects within radius units (default 30). Shows shader name, support status, action, and DLL. Best tool for identifying pink objects you can see. |
shaderhelper nearby [radius] mod |
Same as above but filters out vanilla Valheim/Unity shaders — only shows mod materials. |
shaderhelper nearby [radius] <DllName> |
Same as above but filtered to a specific mod's DLL. |
shaderhelper pink |
Log all materials with unsupported shaders that currently have no fix rule. |
shaderhelper loaded [DllName] |
Log all materials in memory, optionally filtered to a specific DLL. Finds prefab assets that haven't been placed in the scene yet. |
shaderhelper scan <DllName> |
Log all loaded materials attributed to a specific DLL with shader name and support status. |
shaderhelper dumpmod <DllName> |
List all shader names the scanner recorded for a specific DLL. |
shaderhelper status |
Show plugin state — fallback shader, config counts, loaded rules. |
shaderhelper shaders |
Dump all shader objects in memory with names and support status. |
shaderhelper renderers |
Dump materials from active scene renderers only (faster than loaded). |
shaderhelper all |
Dump all materials in memory (both supported and unsupported). |
Objects are still pink
Stand near the pink objects and run shaderhelper nearby 15. Check BepInEx/LogOutput.log — it shows every nearby material with its shader name and action. If the action is Original, that material has no fix rule yet. Add a mat: entry to the appropriate mod's txt file and run shaderhelper sweep.
If the shader shows isSupported=True but still renders pink, add the shader name prefix to [forcefallback] in that mod's txt file.
Objects that should be visible are transparent
The plugin made them invisible because their shader was broken. Find the material name from shaderhelper nearby and add mat:MaterialName to [fallback] in the mod's txt file. Run shaderhelper sweep to apply.
A mod looks wrong after being set to fallback (flat color, no textures)
Expected — the fallback shader has no textures. This is a compatibility placeholder. Correct visuals require the mod author to recompile with Metal support.
I don't know which DLL a mod uses
Look in BepInEx/plugins/. The .dll filename without the extension is what to use. If a mod bundles its shaders inside the DLL itself (rather than a separate bundle), the scanner may report it as a large embedded bundle — in that case, dll='unknown' is expected and per-material mat: rules in the txt file still work.
The plugin seems slow at startup
The scanner reads all mod bundles at launch. With many large mods this takes a few seconds. The log shows [N/total] 'ModName' — X shader(s) lines as it progresses.
Bundle byte layout varies by Unity version. The plugin reads shader names directly from the raw bundle binary. Different Unity versions and build tools place data at different byte offsets. If the layout doesn't match a known pattern, the shader's name can't be read — DLL-based attribution won't apply to it.
shader.isSupported is not always accurate. Some shaders report isSupported = true while still rendering pink on Metal. Use [forcefallback] in a mod's txt file to force replacement of these.
Shaders loaded after startup are not always caught immediately. The plugin sweeps at scene load, after ZNetScene.Awake, after DungeonGenerator.Generate, and when items are equipped. Shaders that appear between these points may stay pink until the next sweep. Run shaderhelper sweep in the console to force an immediate re-sweep.
Valheim's own shaders exist in both the game and mod bundles. Shaders like Custom/Piece and Custom/Creature are built into the game with Metal support, but mod bundles often ship their own copies without Metal support. The plugin distinguishes these using instance IDs captured at startup.
The fallback shader carries no textures. When a material is replaced with the fallback, it renders as a solid flat color with no textures, normal maps, or emission. This is a compatibility shim — correct visuals require the mod author to recompile their shaders with Metal support.
Some mods manage shader replacement themselves. Mods using Jotunn's JVLmock_ system replace their own shaders at runtime. The plugin is aware of these and skips them. Other mods doing custom runtime replacement may conflict — add them to useoriginalshader.txt.
At startup the plugin:
.dll and .bundle files in BepInEx/plugins/ for shader names, recording which DLL each shader came from.Custom/Piece, Custom/Creature, Custom/Vegetation in order, using the first one supported on Metal.ZNetScene.Awake, DungeonGenerator.GenerateDungeon, and VisEquipment.AttachItem postfixes trigger sweeps when new objects are spawned; AssetBundle.LoadAsset intercepts materials the moment a bundle is loaded; Shader.Find intercepts runtime shader lookups.