Mapbox-gl-js: Severe line-offset artifacts with non-miter joins

Created on 15 Dec 2016  Â·  3Comments  Â·  Source: mapbox/mapbox-gl-js

offset-yum

mapbox-gl-js version 0.28.0

Steps to Trigger Behavior

  1. set line-offset to > 0
  2. set line-round-limit to 1

Expected Behavior

Nicely rendered lines

Actual Behavior

Very artifacty lines.


Similar to https://github.com/mapbox/mapbox-gl-js/issues/2316, but applies with any line-join.

cc @lucaswoj @jfirebaugh @pveugen

bug

Most helpful comment

I dug into this a bit last week (also, this and https://github.com/mapbox/mapbox-gl-js/issues/2316 are more or less the same issue — same underlying problem, just present in different joins).

The crux of the issue is that miter joins are able to offset correctly because they contain only a single (well, two-way) join vertex, and its normal is the correct normal vector for that line vertex (that is, its normal is the sum of the normals of the lines that meet there). Our method of line offsetting is more or less just shifting it along its normal vertex. Here's a miter-joined line, offset, where you can see all line vertices are just shifted along their normals:

image

Now, a bevel join: The visual lines we draw are created with GL vertices with position attributes — that's the position of the center of the line — and then (simplified) data about the direction of their normal vertex (i.e. which way is perpendicular) and how wide to draw the line (how far out along the normal vertex do we visually draw). In bevel joins, we still draw the inner vertex at the true join normal (sum of the normals of both lines that meet there) but we draw two outer vertices, and calculate different normals to create a bevel. The problem is then when we offset all vertices in the direction of their normal vector, the bevel vertices go flying off in the wrong direction (this drawing isn't an exact reflection of what happens in mbgl, but conveys the general idea):

image

When what we need in all join cases is to offset all vertices involved in the direction of the line's actual join normal, which would look like this:

image

(Round line joins similarly employ modified normals and so suffer from the same problem.)

line-round-limit really just masks the problem by forcing round joins to render as miter joins. Additionally, this problem only becomes obvious at high offets because the funkiness is hidden within opaque lines, but it's technically present at any offset value.

To actually fix offset lines we would need to pack an additional join normal into all line vertices (so that we'd know both the render normal and the line join's true normal), which would cause some bloat… 🤔

All 3 comments

We ran into a similar issue on iOS when we changed the default line-round-limit in gl-native to 1, so we reverted the default to 1.05: mapbox/mapbox-gl-native#5258.

I dug into this a bit last week (also, this and https://github.com/mapbox/mapbox-gl-js/issues/2316 are more or less the same issue — same underlying problem, just present in different joins).

The crux of the issue is that miter joins are able to offset correctly because they contain only a single (well, two-way) join vertex, and its normal is the correct normal vector for that line vertex (that is, its normal is the sum of the normals of the lines that meet there). Our method of line offsetting is more or less just shifting it along its normal vertex. Here's a miter-joined line, offset, where you can see all line vertices are just shifted along their normals:

image

Now, a bevel join: The visual lines we draw are created with GL vertices with position attributes — that's the position of the center of the line — and then (simplified) data about the direction of their normal vertex (i.e. which way is perpendicular) and how wide to draw the line (how far out along the normal vertex do we visually draw). In bevel joins, we still draw the inner vertex at the true join normal (sum of the normals of both lines that meet there) but we draw two outer vertices, and calculate different normals to create a bevel. The problem is then when we offset all vertices in the direction of their normal vector, the bevel vertices go flying off in the wrong direction (this drawing isn't an exact reflection of what happens in mbgl, but conveys the general idea):

image

When what we need in all join cases is to offset all vertices involved in the direction of the line's actual join normal, which would look like this:

image

(Round line joins similarly employ modified normals and so suffer from the same problem.)

line-round-limit really just masks the problem by forcing round joins to render as miter joins. Additionally, this problem only becomes obvious at high offets because the funkiness is hidden within opaque lines, but it's technically present at any offset value.

To actually fix offset lines we would need to pack an additional join normal into all line vertices (so that we'd know both the render normal and the line join's true normal), which would cause some bloat… 🤔

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Scarysize picture Scarysize  Â·  3Comments

stevage picture stevage  Â·  3Comments

PBrockmann picture PBrockmann  Â·  3Comments

iamdenny picture iamdenny  Â·  3Comments

aderaaij picture aderaaij  Â·  3Comments