mapbox-gl-js version 0.28.0
Nicely rendered lines
Very artifacty lines.
Similar to https://github.com/mapbox/mapbox-gl-js/issues/2316, but applies with any line-join.
cc @lucaswoj @jfirebaugh @pveugen
One potential fix: https://github.com/mapbox/mapbox-gl-style-spec/issues/286
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:
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):
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:
(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… 🤔
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:
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):
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:
(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… 🤔