When to make LODs Jason Booth 3 min read · Dec 26, 2021 -- Listen Share
Understanding model costs
I’m always amazed at how often I hear people talk about “poly counts” in modern rendering, as if that’s even a thing. But lore has a way of sticking around forever, far past its point of being useful. And quite frankly, I’m constantly seeing artwork created in substandard ways because of this lore.
First, the cost of rendering something doesn’t really have much relation to how many polygons it has. A vertex shader transforms vertices, not polygons, and a fragment shader rasterizes pixels, not polygons. A slightly wiser lore tells us to use vertex count as a measure of rendering performance; but even this is wildly outdated. A modern screen has 1920x1080 pixels or more. A modern game will render 10 times that many pixels easily — rendering into GBuffers, post processing, overdraw. That’s easily 20million pixels which will be computed. By comparison, for most shaders, the vertex stage does very little- usually passing some data from the model to the fragment shader, and transforming the vertex position from local to clip space. In most cases, the cost of rendering pixels dwarfs vertices, as we have to look up multiple textures, apply lighting, etc.
This bad lore has lead to an overabundance of LODs, LODs which cause popping, break batching, take memory, and take a lot of artist time to model and generally make the game look worse. Often I see 3–5 LODs for a simple rock.
So whats the real killer? What metric can I use?
You should be optimizing for MicroTriangles.
Small triangles can be incredibly costly on a GPU. To understand why, we have to know a bit about how GPUs rasterize pixels. First, the GPU has to transform enough vertices to begin rasterizing pixels. It will then attempt to work on 2x2 screen blocks of pixels in parallel. This is due to how MipMapping works.
Now let’s consider what happens when we have a small triangle. The triangle might only cover one of the 4 pixels in the quad. But the full 2x2 block is always computed, so we pay the cost of computing 4 pixels even though we are only using the result of a single pixel.
How bad is microtriangle cost? Here’s a great article on it. Suffice it to say, when you get a triangle under about 10x10 pixels in size, rendering cost gets exponentially more expensive — single pixel geometry can be 40–80 times slower.
So the lore we should be telling artists is likely something like this:
“Look in the wireframe view, when it starts to get close to solid, you need to swap to a lower LOD, which significantly reduces the density of the wireframe view”.
Note that Unity’s HDRP pipeline now has a heat map rendering mode (“Vertex Density”) that makes this easy as well.
In many cases, you’re better off with a single LOD and an imposter for the distance, or in some cases no LODs and just an imposter.
Original Forum post on this issue
The Future
This is one of the primary things Epic’s Nanite gets around, along with other GPU centric rendering techniques. Instead of rendering expensive pixel shaders for MicroTriangles, they use a very nice continuous LOD system to keep their geometry at around 1 triangle per pixel. Small triangles are rasterized by a compute shader into a buffer that stores what piece of geometry is used at each pixel. Large faces still go through the hardware rasterizer, but essentially the fragment shader in both cases is extremely light, only writing out an ID. After this is done, a screen space shader can be run which looks up that ID, then looks up the geometry in a buffer, and reconstructs the needed data for a traditional fragment shader to run (barycentric weights, uvs, etc). Essentially, the heavy fragment work is all done in screen space, on a large quad, avoiding all MicroTriangle issues. And all tiny triangles are rasterized to the index buffer using a compute shader with a very simple shader.
As such, the lore will eventually change again.