LOW POLYGON MODELS: OPTIMIZING FOR PERFORMANCE
I once shipped a prototype with 240 trees in a forest scene, each one around 800 triangles, each with its own material and its own 2048x2048 texture. My framerate was 11. Eleven. On a desktop with a 3080. I spent a week convinced my shader was broken before I finally opened the profiler and realized I was pushing 240 draw calls a frame for things that all looked identical.
That was the day I learned that low poly is not the same thing as optimized. You can make a scene out of 200-triangle cubes and still grind a modern GPU to a halt if you set it up wrong. The polygon count is one lever of about fifteen that matter, and honestly it's not even the most important one in 2026. Modern hardware eats triangles for breakfast. What it chokes on is state changes, memory bandwidth, and fill rate.
This is the guide I wish I had when I was trying to figure out why my stylized low poly game ran worse than a photorealistic AAA title on the same machine. Let's get into the actual work.
Triangle budgets are guidelines, not commandments
Before you touch a single vertex, you need to know what you're budgeting for. The numbers I use when planning a low poly project look roughly like this.
For mobile, hero props and important characters sit in the 500 to 2000 triangle range. Background clutter drops to 100 to 400. A mobile scene shouldn't push past about 100k visible triangles at any given time, and ideally you're well under that. Thermal throttling will wreck you long before the raw compute does.
For desktop stylized games, hero characters can breathe a little more, around 2000 to 8000 triangles. Environment props land around 500 to 3000 depending on importance. A typical scene can carry 500k to a few million visible triangles without breaking a sweat on mid-range hardware, but the distribution matters more than the total.
For consoles, current gen is closer to desktop than mobile. The real constraint is memory bandwidth, not compute.
Here's the thing nobody tells beginners though. These numbers are starting points, not rules. I've shipped characters at 12k triangles in a "low poly" game because the silhouette demanded it, and I've shipped rocks at 40 triangles that looked gorgeous. The right poly count is whatever reads clearly at the distance the player will see it from. If your character is on screen filling half the frame, spend the triangles. If it's a background extra at 50 meters, 200 triangles is plenty. I went deeper on the modeling side of this in my low poly modeling guide if you're still getting comfortable with the fundamentals.
Vertex count is what actually matters
Quick reality check that surprises a lot of people. The GPU doesn't really care about triangles. It cares about vertices. And a vertex isn't just a point in space, it's a bundle of data: position, normal, UV, tangent, color, maybe bone weights. Every hard edge in your model splits a vertex into two. Every UV seam splits a vertex. Every material break splits a vertex.
A cube has 8 corners in 3D space. But when you load it into a GPU with hard edges on every face and a proper UV unwrap, it has 24 vertices. That's 3x the data. For a low poly model with lots of faceted edges (which is half the aesthetic), your actual vertex count can be double or triple your raw triangle count.
What does this mean practically? Mark edges as smooth wherever you can get away with it. If two faces meet at a shallow angle and you're not trying to sell a hard crease, let them share a vertex with an averaged normal. Your silhouette won't change and your vertex buffer shrinks. Auto Smooth with a 30 degree angle threshold is my default starting point in Blender. I tune from there.
Also weld duplicate vertices. Blender has a "Merge by Distance" operation that I run on basically every model before export. It's embarrassing how often I've shipped models with stray doubles that bloated the vertex count by 15% for literally no visual benefit.
UV unwrapping for batching, not beauty
This is where most low poly artists go wrong. They unwrap each model into its own clean, organized UV layout with perfect texel density and then wonder why they have 400 draw calls.
The secret is to unwrap your entire asset pack onto a single shared texture atlas. I'm talking every prop, every piece of environment dressing, every static mesh that doesn't animate. One texture, one material, one draw call for everything that shares it (assuming you batch correctly, which we'll get to).
For the classic low poly aesthetic where you're using flat colors and gradients rather than detailed textures, this is almost free. A 512x512 atlas can hold the color palette for an entire medieval village. Each face on each model just needs a tiny UV island pointing at the right color swatch. I've done whole game levels with a single 1024x1024 gradient palette texture.
If you're using actual texture detail on your low poly models, atlasing gets more complicated but it's still worth it. Pack related assets together. All the wood props on one atlas. All the stone props on another. All the foliage on a third. You'll still end up with fewer materials than the naive approach. For a deep look at texture strategies specifically, I wrote a whole piece on low poly texture techniques.
One gotcha. When you atlas things, you lose the ability to tile textures on any individual face. If you need a brick wall that tiles 20 times across, that wall needs its own dedicated texture. Know when to break the atlas rule.
LODs, or the art of lying to the camera
Level of detail swapping is the single biggest performance win on most projects. Your 4000 triangle tree only needs to be 4000 triangles when the player is standing next to it. At 30 meters, 800 triangles looks identical. At 100 meters, 150 triangles is fine. At 200 meters, you're better off with a flat card that has a pre-rendered image of the tree on it.
My LOD setup for most props looks like this. LOD0 is the full quality model, used within about 15 meters of the camera. LOD1 is roughly 50% of the triangle count, kicking in from 15 to 40 meters. LOD2 is about 20% of the triangles, from 40 meters to whatever the far plane is. And for vegetation and crowds, I add an LOD3 that's either a billboard or a heavily merged cluster.
The good news for low poly art is that LODs are way easier to author than they are for realistic models. Realistic LODs need careful attention to silhouette preservation, normal map baking, and smooth pop-in. Low poly LODs can often just be "decimate and call it done" because the faceted look hides most of the discontinuity. Blender's Decimate modifier with collapse mode gets you 80% of the way there, and then I spend maybe 10 minutes per LOD fixing anything obviously broken.
Generate them. Even if you're lazy about it. Automatic LODs are better than no LODs, every single time.
Occlusion culling is doing more than you think
Your engine is already skipping draw calls for objects that aren't visible, but it can only do that if you set things up to help it. In Unity, mark static geometry as Occluder Static and Occludee Static. In Unreal, make sure your meshes are set up for precomputed visibility. In your own engine, implement a basic portal or frustum system.
The thing that bit me hard on my first open world prototype. I had these beautiful low poly mountain ranges in the distance, and I noticed performance tanked whenever the player faced away from the play area. Turned out the mountains were huge meshes marked as dynamic, so the engine couldn't occlusion cull them properly. Even though 90% of each mesh was behind the camera, the whole thing got processed every frame. Marked them static, rebuilt occlusion data, and picked up 30 fps.
The lesson. Static things should be static. Dynamic things should be small and local. Mixing them up costs you.
Batching, the actual game changer
Batching is the technique where the engine combines multiple meshes into a single draw call. Done right, it turns that 240 tree scene from 240 draw calls into maybe 5. Done wrong, it does nothing or makes things worse.
Static batching is the easiest win. Any mesh that doesn't move, mark it static. The engine combines all static meshes that share a material at bake time, and you get one draw call per material per batch. This is why the atlasing work from earlier pays off so hard. If all your trees share one material, all your trees become one draw call. If they each have their own material, you get nothing.
Dynamic batching is trickier. It happens at runtime, has size limits (usually around 300 vertices per mesh in Unity), and it costs CPU time to do the batching. For small props and particle-ish stuff, it's great. For your hero character, skip it. GPU instancing is what you actually want for anything repeated at scale, like a forest of identical trees or a field of grass patches.
GPU instancing is worth a whole section on its own, but the short version is this. The GPU draws the same mesh hundreds or thousands of times with different positions, rotations, and scales in a single draw call. Enable it on your material for any asset that gets duplicated more than a handful of times. Grass, trees, rocks, enemies, bullets, particles. Instance all of them.
One warning. GPU instancing requires all instances to share the same mesh and material. If you have LODs (you should), each LOD level is a different mesh and instances separately. This is fine, but it means your 100 distant trees on LOD2 are one instanced draw call, and your 10 nearby trees on LOD0 are another instanced draw call, not one combined batch.
When to combine meshes, and when not to
There's a tempting optimization where you take a bunch of static meshes and merge them into one giant mesh at bake time. No batching needed, it's already one mesh, one draw call. I've done this on house interiors, on cliff formations, on props that always appear together.
It works, but there are real tradeoffs. First, you lose occlusion culling granularity. The engine can't cull half of a combined mesh, it has to draw the whole thing or none of it. If your combined mesh is a giant cliff that wraps around a mountain, you're drawing the far side of the mountain every frame even though it's invisible. Second, you lose lightmap flexibility. Giant meshes need giant lightmaps, and the UV packing gets awkward. Third, you lose iteration speed. Want to move one rock? You have to rebuild the combined mesh.
My rule of thumb. Combine meshes that are small, always seen together, and rarely edited. Individual rock clusters, building kitbash pieces, furniture arrangements. Don't combine whole terrain chunks or entire buildings unless you know what you're doing.
Lighting and shader cost, the hidden killers
Triangle count is one axis. Shader complexity is another, and on mobile especially it's often the bigger cost. A scene with 50k triangles and a fancy PBR shader can run slower than a scene with 500k triangles and a flat color shader. This is very much true for low poly games because the aesthetic often doesn't need expensive lighting at all.
Use unlit or simple lit shaders whenever the art style allows. If your game has no dynamic shadows and no dynamic lights, a basic color + gradient shader is all you need. Save the expensive shaders for hero objects or specific effects.
Bake your lighting. If your world is static, bake static lighting into lightmaps and skip all runtime light calculations. This is such an underused technique in low poly games where dynamic lighting often doesn't add much anyway. I went into the specifics for Unity projects in smooth lighting for low poly scenes, which covers the vertex color tricks I use to keep things cheap.
Watch your overdraw. Fill rate is the killer on mobile and integrated GPUs. Overdraw happens when multiple polygons stack up in the same pixel, and you pay shader cost for each one. Particle effects, alpha-blended foliage, and large transparent UI are the common offenders. Profile it with the overdraw visualization in your engine, it's eye-opening.
Profile before you optimize
Every tip above is a tradeoff. Some of them only matter at certain scales. Some will waste your time on a project that's already fast enough. The only way to know what's actually slowing your game down is to profile it, with real data, on your target hardware.
Unity Profiler, Unreal Insights, RenderDoc. Learn at least one. Find the actual bottleneck. Is your CPU maxed out on a single thread? Probably draw calls, start with batching. Is your GPU maxed out but the CPU is idle? Probably fill rate or shader cost, simplify materials and look at overdraw. Is memory bandwidth saturated? Probably texture sizes or too many vertex attributes.
The worst thing you can do is optimize blindly based on blog posts like this one. I've watched devs (myself included) spend days trying to shave triangles off a model when the actual bottleneck was a single expensive post-process effect. Profile first. Every time.
Low poly as an aesthetic gives you a massive performance head start. You're already skipping the expensive stuff. But there's a gap between "uses low poly art" and "actually runs well," and closing that gap is the difference between a pretty tech demo and a shippable game. If you're still deciding on the style itself, I covered the visual and artistic side in my piece on low poly graphics before I got obsessed with the optimization rabbit hole.
Now go profile your game. You'll find something surprising. You always do.
LIKED THIS? STAY IN THE LOOP
New posts, game updates, and things you won't find anywhere else.