A shader is really two functions: one turns a vertex into a screen position, the other turns a pixel (fragment) into a color. Thousands of them run at once, each with no idea what the others are doing. And with a single floor() we turn a smooth gradient into pixel art — quantization, the mathematical heart of this lesson.
A shader splits into two stages. The vertex stage moves a mesh's vertices to screen positions; the fragment stage fills in a color for each pixel on screen. Both are short functions that handle just one vertex or one pixel — but the GPU runs that function on thousands of pixels at once, each completely independent of the others.
So when you write a shader, think like this: "I am one pixel. I have no idea what color my neighbor is. All I can do is read my own coordinates and sample a texture." Hover over the grid below. Only that one pixel ever gets highlighted — every other one has already finished its own calculation using nothing but its own UV.
Each pixel computed its own color independently, using only its own UV. Hover to highlight just one with a violet outline — "known" and "unknown" pop up in a speech bubble. The other 143 pixels don't know this is happening; they already finished.
A coordinate is just a value relative to an agreed-upon origin and axes. UV is a 0–1 coordinate glued to the mesh surface — move the object and the pattern moves with it. SCREEN_UV is a 0–1 coordinate glued to the screen — move the object and the pattern stays put. VERTEX is view space: coordinates with the camera as the origin.
In UV mode the gradient moves and rotates with the square — like a pattern printed on the object. In SCREEN_UV mode the gradient stays fixed to the screen, and the square is just a window sliding over it. SCREEN_UV is the seed of every post-processing effect (outlines included).
floor(x·n)/n — multiply, floor, divide back down. In plain words: force the resolution down. And one identity always holds: x = floor(x) + fract(x). floor gives you the stairs (integer part), fract gives you the sawtooth (fractional part) — add them back together and you always get the original value.
The two bands above are the original and its quantized result. The graph below is the decomposition — the stairs (cool) plus the sawtooth (warm) sum (solid gray line) lands exactly on the diagonal (violet, dashed). Raise n and the steps get finer, closer to the original.
The pipeline that actually makes 3D look like pixel art works like this: render the scene into a low-resolution SubViewport (say, 640×360), then upscale it by an integer factor with a Nearest filter to fill the screen. Upscale with Linear and the edges blur; with Nearest, every pixel stays a crisp square.
Lower the internal resolution and the pixel blocks get bigger. Nearest gives you crisp pixel art; Linear gives you a blurry smear — same source, just a different way of scaling it up.
(floor(UV.y*4)/4) + fract(UV.y*4) and see why the color clamps to 1 toward the top band. Hint: divide the fract term by 4 too (fract(UV.y*4)/4) and the identity x = floor(x) + fract(x) rebuilds the original gradient exactly.