Recently, I needed to make a pixel-art flower sprite gradually fill with color from the exact center outward. I wanted it to respond to scene lights (meaning it had to be a Lit material) and expand outward linearly. It took some trial and error, a few broken graphs, and some flipped math nodes, but I finally got it working perfectly. If you're trying to figure out how to do a crisp, pixel-snapped radial fill for your own 2D games, here is exactly how I built it—including the mistakes I made along the way and how I fixed them.

Phase 1: The Pure Mathematical Approach (Radial UVs)

Our starting point is figuring out how to tell the shader to look at the center of our texture and grow outward in a circle. By default, Unreal's TextureCoordinate node reads from the top-left corner (0,0) to the bottom-right corner (1,1).To fix this, we have to offset the origin to the center: (0.5, 0.5).

The Node Setup

First, I set my material to Surface, Masked, and Default Lit. Then, I mapped out the core distance math:

Multiply that length by 1.414. Why 1.414? Because the distance from the center to the exact corners of a square texture is roughly sqrt{0.5^2 + 0.5^2} approx 0.707. Multiplying it by 1.414 scales our gradient perfectly so a progress value of 1.0 clears the extreme corners of the image.

  1. Grab a TextureCoordinate[0] node and Subtract a Constant2Vector set to (0.5, 0.5). This shifts our center point.
  2. Run that subtraction intoa Length node. The Length node calculates exactly how far away every single pixel is from that center point
  3. Multiply that length by 1.414. Why 1.414? Because the distance from the center to the exact corners of a square texture is roughly sqrt{0.5^2 + 0.5^2} approx 0.707. Multiplying it by 1.414 scales our gradient perfectly so a progress value of 1.0 clears the extreme corners of the image.

To animate it, I set up a loop using Time -> Multiply (for speed control) -> Frac (to create a looping 0.0 to 1.0 sawtooth wave). I hooked that time loop into Input A of a second Subtract node, and my distance math into Input B. Finally, passing it through a Step node gave me a hard on/off mask.

Radial UV Node Graph Figure 1: Node Graph for Radial Fill.

The Problem

When I plugged this into the Opacity Mask and hit preview, it technically worked... but it looked wrong for my game. Because the math is a perfect circle, the fill swept across the screen like a smooth vector graphic. It didn't care about the blocky, jagged edges of my pixel-art petals at all. It looked like an artificial circle overlaying a sprite rather than the flower itself actually blooming.

Phase 2 (Snapping to the Texture's Alpha)

To make the fill actually dictate the shape of the flower, we need to force our mathematical circle to play by the rules of our sprite's pixel silhouette. The secret weapon here? The texture's own Alpha (A) channel.

Tweaking the Logic for Shape

Instead of passing our perfect circular distance straight into the final subtraction, we intercept it. By multiplying our Length math by the Alpha channel of our Texture Sample, we essentially mask out the gradient. Because pixel art has hard transparent thresholds, multiplying by the alpha instantly forces the distance calculation to flatten out and conform to the steps of the actual petals.

Going Full Pixel-by-Pixel (Quantization)

To take it a step further and ensure it fills exactly one pixel row at a time rather than sweeping fluidly across texels, I added a quantization block based on my flower's actual canvas dimensions (87 x 87 pixels):

  1. Take the distance masked by the alpha.
  2. Multiply it by 87.0.
  3. Run it through a Floor node (which drops all decimals, locking the values into flat, integer-based pixel bands).
  4. Divide it back by 87.0 to return it to a clean 0.0 to 1.0 range.
Alpha Radial Node Graph Figure 2: Node graph for Alpha Radial.

Final Result

Now, when the time loop updates, the smooth circle is completely gone. The animation aggressively snaps along the texel rows of my 87 x 87 sprite canvas, making it look like the pixel art itself is organically illuminating outward from the core!