SMOOTH LIGHTING ON LOW POLY MODELS IN UNITY
The first time I imported a low poly tree into Unity I almost threw my keyboard. In Blender it looked exactly how I wanted, soft and rounded with that nice gradient across the leaves. In Unity it looked like a disco ball had a fight with a Christmas decoration. Every single triangle was lit independently, every edge was hard, and the whole thing screamed "I do not know what I am doing." I did not know what I was doing.
The problem is that low poly and smooth shading have a weird relationship. You want the faceted look most of the time, that's the whole point of the style. But sometimes you want certain parts to read as smooth curves while keeping the polygon count low. A tree trunk that looks like a hexagonal pillar is fine. A character's head that looks like a geode is not. Knowing when and how to control your normals is the difference between a low poly model that looks intentional and one that looks like you forgot a step.
This is the article I wish I had when I was getting started. I'm going to walk you through what's actually happening with normals in Unity, why your imports look weird, and the actual techniques to fix it. If you haven't built a low poly model yet, my low poly modeling guide is probably a better starting point. This one assumes you've made a few models and are now wondering why they look so bad in your game.
What Unity is actually doing
Before any of the fixes make sense you need to understand what's going on under the hood. Every vertex in your mesh has a normal vector attached to it, which is just a direction pointing away from the surface. When the GPU shades a pixel on a triangle, it interpolates the three vertex normals across the face and uses that interpolated normal to calculate how light hits that pixel.
Here's the key thing. If two triangles share an edge and the vertices on that edge have the same normal, the lighting will blend smoothly across the edge. If the vertices on that edge have different normals, you get a hard crease. That's it. That's the entire mechanism behind smooth versus flat shading.
So when you import a model and every face looks faceted, it means each triangle has its own three vertices with normals pointing straight out of that face. Even if two triangles are visually touching on screen, they don't share vertex data. They each have their own copy. That's what creates the hard edges, and it's what gives low poly its signature look.
Unity figures out which version to give you based on the smoothing data in your imported file and the import settings you pick. And both of those things tend to be wrong by default.
The import settings that matter
Open your model in the Unity inspector and look at the Model tab. There's a setting called Normals with a dropdown. The options are Import, Calculate, and None. This is where most of your problems live.
If you pick Import, Unity uses whatever normals are baked into your FBX or OBJ file. If you exported correctly from Blender with smoothing groups, this is what you want. If you didn't, you'll get garbage.
If you pick Calculate, Unity throws away your exported normals and computes new ones based on a smoothing angle you specify. Faces that meet at less than the angle get smoothed, faces that meet at a steeper angle stay sharp. The default is 60 degrees which is a reasonable starting point but almost never what you actually want for a low poly model.
If you pick None, Unity uses face normals only and you get pure flat shading on every triangle. This is the look most people want for low poly, but it's also the laziest option and you give up the ability to selectively smooth anything.
For most low poly work I use Calculate with the smoothing angle set to either 1 or 180 depending on the model. At 1 degree, basically nothing smooths and you get the full flat-shaded look. At 180, basically everything smooths. Between those two I rarely find a magic number that works for the whole model, which is why doing this properly means setting normals in Blender and using Import.
Doing it right in Blender
The professional workflow is to control your normals at the source, in Blender, and then export them with your FBX. Here's how that actually works.
Select your object, go into Edit Mode, and select all your faces. In the right click context menu you have two options that come up constantly: Shade Smooth and Shade Flat. Shade Smooth tells Blender that every face on the selection should blend smoothly into its neighbors. Shade Flat does the opposite. By default everything is flat, which is what you want for low poly.
Now here's the trick. You can mark specific edges as sharp. Select an edge, hit Ctrl+E to bring up the Edge menu, and choose Mark Sharp. Then add an Edge Split modifier or, better, use the auto smooth option in the object data properties. Auto smooth lets you set an angle and any edge sharper than that angle becomes a hard crease, plus any edge you marked sharp manually.
Auto smooth is the magic feature for low poly. You shade everything smooth, then you mark only the edges you want to be hard, and Blender handles the rest. When you export to FBX, those normals come along and Unity's Import setting respects them perfectly.
The export step matters too. In the FBX export dialog, expand the Geometry section and make sure Smoothing is set to Face or Edge. Face sends along the smoothing groups directly. Edge sends edge-by-edge sharp data. Face is more compatible across engines, Edge is more precise. Either works for Unity. The default is Normals Only which means your auto smooth data gets baked into the vertex normals before export, and that also works but loses you the ability to recompute later.
RecalculateNormals at runtime
Sometimes you're generating meshes procedurally and you don't have the luxury of fixing normals in Blender. Maybe you're building voxel terrain or a procedural dungeon. Unity has a method for this on the Mesh class called RecalculateNormals. It does the same thing as Calculate in the import settings but you can call it from script.
Here's the basic pattern:
Mesh mesh = new Mesh();
mesh.vertices = myVertices;
mesh.triangles = myTriangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
By default RecalculateNormals smooths everything below 60 degrees. There's an overload that takes a smoothing angle if you need to control it:
mesh.RecalculateNormals(180f);
Pass 180 and nothing smooths, pass 0 and everything smooths. Same logic as the import setting.
The catch is that RecalculateNormals assumes shared vertices smooth and unshared vertices don't. If you want hard edges in a procedural mesh, you have to duplicate the vertices on either side of that edge so each triangle has its own copy. This is annoying but it's how the system works. A cube with smooth shading has 8 vertices. A cube with flat shading has 24, three for each corner because each corner belongs to three faces and each face needs its own normal.
If you want full control, set the normals yourself:
Vector3[] normals = new Vector3[mesh.vertexCount];
for (int i = 0; i < normals.Length; i++)
{
normals[i] = ComputeMyOwnNormal(i);
}
mesh.normals = normals;
This bypasses Unity's smoothing logic entirely. You can do whatever weird thing you want, like blending normals from multiple triangles weighted by face area, or pointing all normals up for grass and foliage tricks.
The grass trick
While we're talking about pointing normals up, this deserves its own section because it's the single most useful normal manipulation for stylized low poly games. If you have grass, leaves, or any kind of foliage, set all the normals to point straight up along the world Y axis instead of out of the geometry. Suddenly your grass lights like one continuous surface instead of a thousand individual blades catching light at random angles.
It looks fantastic. It also matches how Breath of the Wild handles foliage, which is not a coincidence. Stylized games figured this out years ago.
You can do this in Blender by entering edit mode on your foliage mesh, selecting all vertices, and using the Mesh menu to find Normals, then Set From Faces or Average to set them manually. Or do it in script in Unity:
Vector3[] normals = mesh.normals;
for (int i = 0; i < normals.Length; i++)
{
normals[i] = Vector3.up;
}
mesh.normals = normals;
Try this on a tree and you'll never go back. The leaves stop flickering as the camera moves and the whole thing feels like a coherent object instead of a noise pattern.
Baked versus realtime lighting
Now we get to the part most tutorials skip. The lighting setup matters as much as the normals do. Low poly games tend to look way better with baked lighting than with realtime, and most people don't realize this.
Realtime lights compute everything per pixel every frame. They're accurate but expensive, and they're really good at exposing every flaw in your normals. A bad smoothing angle on a tree trunk will shimmer and pulse as you move around it under realtime lights.
Baked lighting computes the lighting once at design time and bakes it into lightmaps. The lighting looks more stable, you can use way more lights without performance penalty, and the slight blur of lightmap interpolation actually helps hide minor normal issues. For a stylized low poly game, baked lighting almost always looks better.
In Unity, mark your static objects as Contribute GI in the inspector. Then go to Window, Rendering, Lighting, and click Generate Lighting. Wait for the bake to finish. Your scene will look completely different, usually for the better.
If you're using URP, make sure you're using the Lit shader for objects that should receive baked lighting. Unlit shaders ignore lightmaps entirely.
URP and Shader Graph
The Universal Render Pipeline is what most people are using in 2026 for stylized games, and Shader Graph is its visual shader editor. For low poly work, Shader Graph is a gift because you can make custom lighting models without writing a single line of HLSL.
The most common stylized lighting effect for low poly is cel shading or toon shading. The basic idea is to take the dot product of the surface normal and the light direction, then run it through a step function so instead of a smooth gradient you get hard bands of color. Unity provides a Main Light node in URP Shader Graph that gives you the light direction and color. Combine that with your surface normal, run it through a step or smoothstep, and you have toon shading.
Here's a rough recipe. Drop a Main Light Direction node, a Normal Vector node set to World space, and a Dot Product node. Feed both into the Dot Product. Take the output, run it through a Step node with a threshold of about 0.5, and use that to lerp between a shadow color and a highlight color. Plug the result into the Base Color of a custom Master Stack.
You can extend this with multiple bands, rim lighting using the dot of view direction and normal, and specular highlights with another step function. The whole graph stays simple and gives you that hand-painted look that fits low poly perfectly.
For more advanced custom lighting you can use a Custom Function node with HLSL code, but I rarely need it. Shader Graph covers 90% of the stylized lighting tricks you actually want.
Selective smoothing with vertex normals
One thing low poly modelers learn the hard way: not every part of a model wants the same shading. A character might want hard edges on the armor and smooth shading on the skin. A vehicle might want hard panel edges and smooth windshields. Doing this in Blender with auto smooth and marked sharp edges is the way.
Sometimes you want to go further and explicitly paint normals. Blender has a workflow for this called normal editing. Select the vertices you want to modify, go into the N panel under Item, and you can manually set the normal direction. This is overkill for most low poly work but it's how AAA stylized games handle the tricky cases like a character's chin transitioning from hard jawline to smooth neck.
There's also a technique called transferring normals where you create a smooth proxy mesh, transfer its normals to your low poly mesh, and get smooth lighting on a faceted geometry. This is how a lot of stylized characters keep clean silhouettes while still lighting correctly. Look up the Data Transfer modifier in Blender if you want to go down that rabbit hole.
My actual workflow
Here's what I do for every low poly model that goes into one of my Unity games. Build the mesh in Blender. Shade everything flat by default. Enable auto smooth in the object data properties with an angle of 30 degrees. Mark any edges I specifically want to be hard creases. Export as FBX with smoothing set to Face. Import into Unity with Normals set to Import. Mark static objects as Contribute GI. Bake lighting.
For procedural meshes I duplicate vertices on hard edges and call RecalculateNormals at the end. For foliage I override normals to point up. For special characters I use Shader Graph to build a custom toon lighting model.
That's basically it. Once you understand what normals are doing, the rest is just choosing where you want hard edges and where you want smooth ones. The hardest part is breaking the assumption that low poly automatically means flat shaded. Some of the best looking stylized games have selective smoothing all over the place. The trick is being intentional about every edge.
The disco ball tree that started this whole journey for me eventually became a perfectly fine tree. The trunk had hard hexagonal edges, the branches blended smoothly into each other, and the leaves had their normals pointing up so they caught light like a single canopy. Same polycount as the disco ball version. Looked completely different. That's the entire skill.
Go fix your normals.
LIKED THIS? STAY IN THE LOOP
New posts, game updates, and things you won't find anywhere else.