The seam is two correct edges added wrong

Sooner or later a field-rendered element has to sit against something drawn another way: a sprite, a mesh, a UI quad, another pass. The two pieces are each correct, they are the same color, they touch exactly, and there is a line down the join.

A dome and a base of identical color, abutting exactly. Left: each edge antialiased and composited separately. Right: one field, one coverage.

Look along the horizon line where the two pieces meet. On the left there is a darker band; on the right there is nothing, because there is no join. Both halves draw the same shape in the same color.

The arithmetic, which is the whole explanation

Take a pixel sitting exactly on the shared edge. The dome covers half of it. The base covers the other half. Composite them one after the other:

result = bg
result = mix(result, mat, 0.5)   // base:  half background survives
result = mix(result, mat, 0.5)   // dome:  half of THAT survives

// total material coverage = 0.5 + 0.5 * (1 - 0.5) = 0.75

So a quarter of the background remains along the entire seam, forever, no matter how accurate each edge is. The two pieces are not overlapping and they are not leaving a gap. They are each correctly reporting partial coverage of a pixel that is in fact fully covered, and "over" has no way to know the two halves are adjacent rather than stacked.

Worth noticing that it is not a rounding error and it does not get better with more precision or more samples. It is what alpha compositing means.

The fixes, in order of preference

  1. Do not have a seam. Union the fields and resolve the edge once. This is available far more often than people assume, and it is the only fix that is exactly right rather than approximately right.
  2. Overlap the pieces. If they must be separate, make them intersect by a pixel or two rather than abut. The overlap region is fully covered by each piece, so there is no partial coverage to combine. Crude, universal, works with anything.
  3. Resolve coverage in one pass. Render both into a coverage buffer, take the max, and shade once. Correct, and it costs a target.
  4. Match the antialiasing width on both sides. This does not fix the arithmetic, it just stops the seam from also changing width along its length, which is what turns a faint line into a visibly wobbly one.

Everywhere this shows up

How to tell it apart from the things it looks like

A one-pixel dark line has several possible causes and they need different fixes:

Change the background to something loud and unmissable. If the seam turns that color, it is coverage, and the answer is on this page.

Rules of thumb

  1. Two abutting antialiased edges composite to 75% coverage, not 100%. That is definitional, not a bug to hunt.
  2. The best fix is to not have a seam: union the fields, resolve the edge once.
  3. If they must be separate pieces, overlap them by a pixel. Never abut.
  4. Matching antialiasing widths does not fix the seam, it just stops it from wobbling.
  5. Set the background to a loud color to identify it. A coverage seam shows exactly that color.

All 61 notes How to use them Credits