Your GLB loads fine when you drag it into the scene, and fine when you press Play in the editor. Then you build the player, load the same file through glTFast's runtime API, and every surface comes back solid magenta. Nothing in the console. Nothing in the log. The file isn't broken — Unity's shader stripper never saw a reason to keep the shaders glTFast needs, so it cut them from the build.
Why the editor lies to you
![]()
glTFast has two completely different import paths. Drag a `.glb` into the Project window and its `ScriptedImporter` runs, generating a real Material asset that sits in your AssetDatabase and references its shader directly — the build stripper sees that reference and keeps the shader. Call `GltfImport.Load()` at runtime instead, and materials get built in memory, on the fly, from shader variants nothing in your project ever references on disk. Unity's stripper only keeps shaders that something points to at build time. A material instantiated by code at 2 a.m. during actual gameplay doesn't count.
The unity glTFast project setup docs confirm this is expected behavior, not a bug: "In the Editor this shader variant will be built on-demand, but in order for materials to work in your build, you have to make sure all shader variants that you're going to need are included." Play mode in the editor still compiles on-demand too, which is why testing there won't catch it — you have to actually build.
The proof this isn't file-specific
Two independent reports this year hit the identical symptom from different angles: a developer embedding Unity via `HwndHost` in a WPF BIM viewer got pink materials only outside the editor, and a separate GitHub issue (#714) showed the same pink-material failure on a mobile build while the desktop editor rendered correctly. Different platforms, different pipelines, same root cause — build-time stripping, not a corrupt GLB or a Draco decode failure.
The package itself ships shader graphs across four separate folders depending on your render pipe: `Runtime/Shader` (URP 12+/HDRP 10+), `Runtime/Shader/HDRP` for HDRP-specific material types, `Runtime/Shader/Legacy` for older SRP versions, and `Runtime/Shader/Built-In` for the built-in pipeline. Whichever folder matches your project is what you need Unity to keep.
Fix it: Always Included Shaders
![]()
For a small catalog of models, this is the fastest path:
- Open Edit → Project Settings → Graphics.
- Scroll to Always Included Shaders and increase the Size field by the number of shader assets you need.
- Drag in every shader asset from the glTFast package's matching `Runtime/Shader` folder for your pipeline (add the `HDRP` or `Built-In` subfolder too if you're on those pipes).
- Rebuild and test on-device, not just in Play mode.
```csharp
// Runtime load path that triggers the stripping issue if shaders aren't referenced anywhere
var settings = new ImportSettings { GenerateMipMaps = true };
var gltf = new GltfImport();
bool ok = await gltf.Load(path, settings);
if (!ok) { Debug.LogError("glTF load failed"); return; }
await gltf.InstantiateMainSceneAsync(transform);
```
This works, but it's blunt — every included shader ships in every build regardless of whether that day's session ever loads a model that needs it.
Fix it: Shader Preloading (better at catalog scale)
If you're pulling from a large library — say, batch-loading props from BitSoul's Recon Quadcopter through a dozen other assets in one scene — targeting only the variants you actually use keeps build size down:
- Run the scene that loads every GLB you expect, inside the editor.
- Go to Edit → Project Settings → Graphics, scroll to the Shader Preloading section at the bottom.
- Save the currently tracked shaders and variants to a `ShaderVariantCollection` asset.
- Add that asset to the Preloaded Shaders list.
| Approach | Best for | Setup effort | Build size impact |
|---|---|---|---|
| Always Included Shaders | Small, fixed model set | One-time drag-and-drop | Ships every listed shader always |
| ShaderVariantCollection | Large or growing catalog | Requires a capture pass per new model type | Ships only variants you actually exercised |
| Placeholder materials in Resources | Projects that already build material assets | Moderate — one material per feature combo | Scales with feature combos, not model count |
If your GLB catalog changes often, recapture the ShaderVariantCollection whenever you add a model using a material feature combination (double-sided alpha, KHR_materials_emissive_strength, etc.) you haven't loaded before — the collection only knows what it saw during the capture run.
Gotchas that look like the same bug but aren't
Draco-compressed source files throw a separate, unrelated failure if the Draco for Unity package isn't installed — you'll see console errors about a missing decoder, not a silent pink material, so check the console before assuming it's the stripping issue. Texture-only failures (geometry fine, textures gray or missing) point instead at the *Unity Web Request Texture* and *Image Conversion* modules — both have to stay enabled if your GLBs carry PNG or JPEG textures rather than KTX2. And if you're on the built-in render pipeline and want the shader-graph materials instead of the legacy built-in shaders, that path is opt-in and experimental: install Shader Graph 12+ and add `GLTFAST_BUILTIN_SHADER_GRAPH` to Scripting Define Symbols, with known shading issues the docs flag directly.
One more real-world case worth knowing: a developer who fixed the pink-material issue by including the shaders still saw specific surfaces — glass and windows — rendering opaque gray instead of transparent. That's a separate, material-property issue (alpha mode not carrying through), not shader stripping, so don't keep re-chasing the Graphics settings once the pink is gone and something else is still off.
If you're loading models fetched programmatically rather than shipped in the build, the automated download workflow covers pulling GLBs via the REST API before you ever hand them to `GltfImport.Load()` — worth pairing with whichever shader-inclusion approach you pick here, since API-fetched files always take the runtime path, never the editor importer. A free account's two monthly downloads cover evaluation; commercial use is included with paid memberships.
Shader stripping bites in other Unity/GLB contexts too — the HDRP Mask Map chrome bug and the reasons GPU instancing does nothing under URP are both symptoms of the same category: something Unity's build pipeline can't see because it only exists at runtime.
---
*This post is part of the Ultimate Guide to Free 3D Game Assets — BitSoul's complete reference for formats, texturing, rigging, optimization, and engine integration.*