-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Line features in GL don't render correctly #596
Comments
There are three separate bugs here. One is that some angles were miscalculated. A fix for that is part of PR #597. The second is the inside of acute angles. If a line segment is short enough, the interior vertex of the intersection is badly placed. The third is when a miter is too long, we truncate it in a manner that often collapses the line width. |
Here is an image that shows a variety of line artifacts. The blue lines are full-opacity lines with repeated junction points, while the grey lines are half-opacity lines without repeated junctions (i.e., if the grey line is [A, B, C, D], the blue line is [A, B, B, C, C, D]). From left to right:
|
In addressing this issue, we have the opportunity to also features to our lines. Features we can add:
Features that aren't being considered right now:
Currently, we draw each line as a pair of triangles. When we process the vertices in the vertex shader, we use three points and an offset for a total of 10 float values, plus the color (3 values), opacity, and line width, for a total of 15 float values per vertex. This means that on graphics card that allows 1Gb of memory for attributes that we can draw 17.8 million line vertices, of 8.9 million line segments that aren't joined to others. There are three technical approaches to fixing the errors we have and adding the features in the top list:
In thinking of how lines are requested, there are many varied conditions. For instance, if a series of connected line segments have repeated vertices, (a - b - b - c - d - d - d - e), I think this should look identical to the same series of segments without repeated vertices (a - b - c - d - e). Currently, if we have one repeated vertex, we handle it as if it wasn't repeated, but if we have multiple repeated vertices, we do odd things, such as treat them as a line segment of zero length that is oriented horizontally on the screen for purposes of mitering. This applies to functionally repeated vertices (one that have the same x and y in the screen coordinates). I'm currently considering option (B) to be the best compromise to handle repeated vertices, use less memory, and allow all of the options we care about. Thoughts and opinions welcome. |
Have you looked at this library? |
Option B is fine and I believe that's what mapbox does, however, the parallel processing of vertex shader might be faster in certain cases. The limitation there would be is that you won't get any additional vertices in WebGL 1.x so in that sense B is the way to go. It would be interesting to see the timing differences just with that change if possible. I think in general B will provide most flexibility for the options we talked about with different joints. I have looked at the mapbox gl code before and I think it would be worth picking some ideas from it. |
Also worth noting is that mapbox performs much (if not all) preprocessing in WebWorkers to achieve the performance necessary for tiling. I suspect we will hit bottlenecks with this unless we go down a similar route. |
This library looks interesting but seems to have inconsistent stroke widths on the corners; also the overlapping lines seems bit odd but it would be worth looking into it since it is using GL as well. |
+1, performance was my concern with too much work on JS side of things with webworkers. Should we look into using webworkes? Or would it be a post 1.0 thing? |
Making everything compatible with WebWorkers would take quite a lot of refactoring and make a significant departure from the current API. If it went in after 1.0, it would most likely warrant a 2.0 release. |
+1, I agree @manthey I guess in that case you may want to try out both B) and C) and see what works better in our current system. I would think B will provide most flexibility but may cost us performance and C) will probably give better performance but at the cost of more memory. I think memory would be less concern for me right now. |
This resolves issues #204 and #596. Previously, lines were not rendered correctly for several conditions: (a) miter locations were sometimes miscalculated, resulting in inconsistent line widths. (b) when the miter limit was exceeded, an attempt to prevevnt an excessively long line produced skewed segments. (c) On the inside of acute angles on short line segments, some of the triangles used to render lines were twisted. (d) when lines overlap, the overlap was inconsistent. All of this has been fixed. Additionally, we know support: - line caps: butt (default and what was avaulable before), round, square. These can vary by point. - line joins: miter (default), round, bevel, miter-clip. These can vary by point. Miter and miter-clip have a configurable miter limit (one value for the whol feature) which, if the miter would be longer than this, switches to using a bevel or a clipped miter. - antialiasing on edges and end caps. This can be specified for the whole feature. - strokeOffset: This can vary from -1 (shift left) to 1 (shift right), where 0 is the line centered on the vertices, and 1 is with the edge of the line on the vertices. All of this is done by rendering two triangles per line segment and performing much of the computation in the vertex and fragment shaders. There are a few limitations to the current implementation: - there is finite precision in the calculations, resulting in occasional artifacts along miter joins. These are mostly only noticeable on very wide lines using stroke offsets, and, even then, are subtle. - When the line width changes per vertex, sometimes the inside joins are not technically correct. Instead of using the angle of the lines meeting, the angle of the edges of the lines meeting would need to be calculated. - On short line segments, one edge of the miter can be by itself, which would ideally either be antialiased or wouldn't be trimmed. Currently, on wide short lines with multiple segments, the mitered edge can look blocky in limited instances. This method uses 18/15 as much memory as lines used before. There is a debug flag to show all of the pixels sent to the fragment shader. If debug is not specified when the feature is created, the debug code is not even compiled into the fragment shader. There could be some efficiency improvements in the shaders. For instance, it might worth it to have a quick test for simple line segments without joins. The new features and changes are exposed in one of the selenium tests (test/selenium/glLines/?wide=true), which can take a variety of query options, such as strokeOffset, lineCap, lineJoin, miterLimit, antialiasing, strokeWidth, strokeColor, strokeOpacity, and debug. This should be turned into an example that also tests number of lines.
This resolves issues #204 and #596. Previously, lines were not rendered correctly for several conditions: (a) miter locations were sometimes miscalculated, resulting in inconsistent line widths. (b) when the miter limit was exceeded, an attempt to prevent an excessively long line produced skewed segments. (c) On the inside of acute angles on short line segments, some of the triangles used to render lines were twisted. (d) when lines overlap, the overlap was inconsistent. All of this has been fixed. Additionally, we now support: - line caps: butt (default and what was avaulable before), round, square. These can vary by point. - line joins: miter (default), round, bevel, miter-clip. These can vary by point. Miter and miter-clip have a configurable miter limit (one value for the whole feature) which, if the miter would be longer than this, switches to using a bevel or a clipped miter. - antialiasing on edges and end caps. This can be specified for the whole feature. - strokeOffset: This can vary from -1 (shift left) to 1 (shift right), where 0 is the line centered on the vertices, and 1 is with the edge of the line on the vertices. All of this is done by rendering two triangles per line segment and performing much of the computation in the vertex and fragment shaders. There are a few limitations to the current implementation: - there is finite precision in the calculations, resulting in occasional artifacts along miter joins. These are mostly only noticeable on very wide lines using stroke offsets, and, even then, are subtle. - When the line width changes per vertex, sometimes the inside joins are not technically correct. Instead of using the angle of the lines meeting, the angle of the edges of the lines meeting would need to be calculated. - On short line segments, one edge of the miter can be by itself, which would ideally either be antialiased or wouldn't be trimmed. Currently, on wide short lines with multiple segments, the mitered edge can look blocky in limited instances. This method uses 18/15 as much memory as lines used before. There is a debug flag to show all of the pixels sent to the fragment shader. If debug is not specified when the feature is created, the debug code is not even compiled into the fragment shader. There could be some efficiency improvements in the shaders. For instance, it might worth it to have a quick test for simple line segments without joins. The new features and changes are exposed in one of the selenium tests (test/selenium/glLines/?wide=true), which can take a variety of query options, such as strokeOffset, lineCap, lineJoin, miterLimit, antialiasing, strokeWidth, strokeColor, strokeOpacity, and debug. This should be turned into an example that also tests number of lines.
This resolves issues #204 and #596. Previously, lines were not rendered correctly for several conditions: (a) miter locations were sometimes miscalculated, resulting in inconsistent line widths. (b) when the miter limit was exceeded, an attempt to prevent an excessively long line produced skewed segments. (c) On the inside of acute angles on short line segments, some of the triangles used to render lines were twisted. (d) when lines overlap, the overlap was inconsistent. All of this has been fixed. Additionally, we now support: - line caps: butt (default and what was avaulable before), round, square. These can vary by point. - line joins: miter (default), round, bevel, miter-clip. These can vary by point. Miter and miter-clip have a configurable miter limit (one value for the whole feature) which, if the miter would be longer than this, switches to using a bevel or a clipped miter. - antialiasing on edges and end caps. This can be specified for the whole feature. - strokeOffset: This can vary from -1 (shift left) to 1 (shift right), where 0 is the line centered on the vertices, and 1 is with the edge of the line on the vertices. All of this is done by rendering two triangles per line segment and performing much of the computation in the vertex and fragment shaders. There are a few limitations to the current implementation: - there is finite precision in the calculations, resulting in occasional artifacts along miter joins. These are mostly only noticeable on very wide lines using stroke offsets, and, even then, are subtle. - When the line width changes per vertex, sometimes the inside joins are not technically correct. Instead of using the angle of the lines meeting, the angle of the edges of the lines meeting would need to be calculated. - On short line segments, one edge of the miter can be by itself, which would ideally either be antialiased or wouldn't be trimmed. Currently, on wide short lines with multiple segments, the mitered edge can look blocky in limited instances. - When a line segment has zero length in screen space, it is not drawn at all. Adjacent line segments may not be drawn exactly as expected. Also, if line caps are used, perhaps zero-length segments should still be visible. This method uses 18/15 as much memory as lines used before. There is a debug flag to show all of the pixels sent to the fragment shader. If debug is not specified when the feature is created, the debug code is not even compiled into the fragment shader. There could be some efficiency improvements in the shaders. For instance, it might worth it to have a quick test for simple line segments without joins. The new features and changes are exposed in one of the selenium tests (test/selenium/glLines/?wide=true), which can take a variety of query options, such as strokeOffset, lineCap, lineJoin, miterLimit, antialiasing, strokeWidth, strokeColor, strokeOpacity, and debug. This should be turned into an example that also tests number of lines.
Fixed in PR #649. |
Wide lines at acute vertices appear to have the most trouble:
The text was updated successfully, but these errors were encountered: