the symptom, in your words

"For runtime per-surface color, bake a texture — don't rely on complex.shader vertex color"

✓ verified on 26.07.15a
lane Getting art inposted

▸ SYMPTOM

You want to color a mesh per-surface at runtime — per-chunk terrain tints, team colors, a procedurally colored generated mesh — and reach for per-vertex color painting through the stock complex.shader, expecting it to read a vertex color stream as albedo. It doesn't do what you hoped, and there is no source to check why.

▸ CAUSE

complex.shader ships compiled-only and shows no vertex-color-as-albedo feature. On the installed build (engine 26.07), complex.shader exists only as core/shaders/complex.shader_c — there is no .shader source under addons/base/assets/shaders/ to inspect. A scan of its embedded identifiers shows no vertex-color/paint feature names.

Vertex color plumbing does exist in the engine (vVertexColor on TEXCOORD4, declared in common/pixelinput.hlsl), but shaders that use it — blendable.shader, for example — consume it as a layer mask, not as an albedo multiply. So "paint albedo per vertex through complex.shader" has no path.

▸ FIX

Use a baked texture + a per-instance material copy. Sandbox.Engine.xml documents the full runtime override path:

  • Material.CreateCopy(string) — "Create a copy of this material"
  • Material.Set(string, Texture) — "Override/Sets texture parameter (Color, Normal, etc)"
  • Material.Create(name, shader, anonymous) — from-scratch material creation

For per-chunk or per-instance surface coloring, bake a small texture and bind it as the color map:

snippet
// Build a tiny per-region color texture (the proven minimap recipe)
var tex = Texture.Create( width, height )
    .WithDynamicUsage()
    .Finish();
tex.Update( colors );        // Color32[] laid out per cell/region

var mat = baseMaterial.CreateCopy();
mat.Set( "Color", tex );     // "Color" is the albedo texture parameter
renderer.MaterialOverride = mat;

The "Color" parameter name does take in-editor on this build (verified: per-chunk blend colors render as their real values, not magenta/white), so the older "g_tColor" fallback is not needed here.

▸ WHY IT WORKS

complex.shader is a closed, general-purpose surface shader — you cannot add an albedo-from-vertex-color feature to something whose source you don't have. The documented, supported lane is the material override API: CreateCopy gives you a per-instance material, and Set("Color", tex) swaps its albedo texture for a small baked one you fill from a Color32[]. This whole path (CreateCopy + Set + Texture.Create/WithDynamicUsage) passes the in-editor code whitelist and compiles green, and it does not leak: creating many same-named material copies per regeneration fluctuates the working set with GC but fully reclaims — no unbounded climb across repeated regenerations.

Verified on engine 26.07.15a: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord