Materials for low poly models in Unity
Part of Flat shading in Unity for low poly models
A low poly model needs flat color as well as flat shading. The color has to come from somewhere, and where it comes from decides how many materials the model has, how many draw calls it costs, and how hard it is to recolor. This article sets up the three options in Unity and says which to pick. It covers the color part of flat shading in Unity. For the shading part, see the importer's smoothing angle or a flat shading shader. LODs and billboards explains why draw calls matter so much for trees.
The three ways
A palette texture. This is one small image, often 32 or 64 pixels square, with each color as a block of solid pixels. Every face's texture coordinates point at the middle of its block. The model has one material with that texture however many colors it uses, and you recolor it by repainting pixels. Every large low poly pack works this way. So does PolyMason's export, which is one mesh, one material and a palette PNG.
Vertex colors. Each vertex carries a color and the face is drawn in it. There's no texture and there are no texture coordinates, so there's no image file to lose in a folder. The catch is that Unity's Standard and Lit shaders ignore vertex colors, so you need a shader or graph that reads them. OBJ can't carry them at all, which means they only arrive through FBX and glTF.
One material per color. The mesh is split by color, and each part has its own material with a flat color. There's nothing to set up, and you can edit every color in the Inspector. The cost is that every material is a draw call. A tree with bark, two greens and blossom is four draw calls, where the same tree with a palette is one.
Setting up a palette texture
Import the model, its .mtl and the PNG together. Unity reads the .mtl, finds the texture by file name, and makes a material with the texture in its albedo slot. Then check two things:
- The texture's import settings. Select the PNG. Set Filter Mode to Point (no filter) if you want hard-edged blocks and no chance of bleed. Bilinear is fine when the coordinates sit in the middle of the blocks, which is how PolyMason writes them. Turn Generate Mip Maps off for a palette this small. With mip maps on, the smaller copies of the texture average neighboring blocks together, and distant models pick up a faint tint from the colors next to theirs. Wrap Mode doesn't change that. Set Compression to None as well, because block compression smears the edges of the color blocks. If your own palette image isn't a power of two, set Non-Power of 2 to None under Advanced, or Unity rescales it. PolyMason's is 512 by 512, so it isn't touched. Unity's defaults come close anyway. In a test import of the PolyMason palette into Unity 6 with every setting left alone, each of its 3,821 blocks came back within 1 of 255 per channel. With the settings above, every block was exact. Set Max Size no larger than the image, since there's no point upscaling.
- The material. In Built-in it's a Standard material, and in URP it's Lit. Set Smoothness to 0 and, in URP, turn Specular Highlights off, so the flat color doesn't pick up a wet-looking highlight. Unity reads the specular values in an MTL, and PolyMason's files import with Smoothness already at 0. For an unlit look, use Unlit with the same texture.
To edit the material as an asset instead of the importer's embedded copy, open the model's Materials tab and press Extract Materials, which writes it to a folder you choose.
Setting up vertex colors
Unity imports vertex colors from FBX and glTF into the mesh's color channel. To see them you need a shader that outputs them. In Shader Graph, wire a Vertex Color node into Base Color and the graph is finished. In Built-in, a surface shader with float4 color : COLOR in its Input struct and o.Albedo = i.color.rgb does it. The Particles shaders read vertex color too, so you can test with one of those before writing anything.
Setting up one material per color
There's nothing to do here, because the importer makes a material per usemtl and assigns each one to its sub-mesh. Edit the colors on the materials. This suits a hero prop with two or three colors that you want to tweak in the editor. Don't use it for anything that appears in large numbers, because every one of those materials is a draw call.
What batching changes
Draw calls are the reason the choice matters. Unity has two ways of combining meshes that share a material, and URP has a third feature that makes switching materials cheaper:
- Static batching combines meshes that are marked static and share a material into shared vertex and index buffers at build time. Unity still issues a draw call for each mesh, but with no material change between them those calls are cheap. A batch holds up to 64,000 vertices, and Unity starts another when it fills. A forest of a hundred palette-textured trees, all marked static, draws as one run with no state changes. The same forest with four materials per tree needs four runs.
- GPU instancing draws many copies of the same mesh with the same material in one call, static or not, and works with LOD Groups. Check Enable GPU Instancing on the material.
- The SRP Batcher in URP reduces the cost of switching between materials that use the same shader, which lowers the per-material penalty but doesn't remove it.
A palette texture works with all three with no further setup. That's why the packs use it, and it's why the choice matters most in a forest.
Recoloring
With a palette, a second color scheme is a second PNG with the same blocks in different colors, plus a material that points at it. Autumn trees are the summer trees with a different palette, and they need no new meshes. The Synty and Kenney packs ship several palettes per kit for this reason. With vertex colors, recoloring means editing the mesh. With a material per color, it means editing each material.
What PolyMason exports
The tree builder writes each tree as one mesh, and the .mtl names one material whose texture is the PolyMason palette. The palette is one PNG of more than a thousand color blocks across the full spectrum, and it's the same file for every model from here. The levels of detail, the dead tree, the stump, the logs and every other tree you build all share it. Drop them into a Unity folder together and they import as one material, so a whole scene of them batches as one. The colors were flat in the builder, so nothing is baked into them.
More in this guide
- Flat shading in Unity for low poly modelsWhy a low poly model goes smooth when it lands in Unity, what the importer's Normals and Smoothing Angle settings do, the three places you can fix it, and how to keep the colors flat as well. Three articles underneath go into the detail.
- Unity's smoothing angle and Model tab normalsWhat the Smoothing Angle in Unity's model importer does, how the Normals, Normals Mode and Weld Vertices settings affect it, which values give flat shading, and how to set them once for a whole folder with an importer preset.
- A flat shading shader for Unity (Built-in, URP)How to shade any mesh flat from the shader alone by using screen-space derivatives to get the face normal. Includes a working Built-in shader, a URP version, the Shader Graph node, and the caveats.
Or build a tree and see what comes out.