Skip to content

Commit

Permalink
Path layer vertex shader improvements (#4111)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress committed Jan 15, 2020
1 parent 0ead9dc commit cb74907
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 116 deletions.
21 changes: 9 additions & 12 deletions modules/layers/src/path-layer/path-layer-fragment.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,15 @@ varying float vPathLength;
void main(void) {
geometry.uv = vPathPosition;
// if joint is rounded, test distance from the corner
if (jointType > 0.0 && vMiterLength > 0.0 && length(vCornerOffset) > 1.0) {
// Enable to debug joints
// gl_FragColor = vec4(0., 1., 0., 1.);
// return;
discard;
}
if (jointType == 0.0 && vMiterLength > miterLimit) {
// Enable to debug joints
// gl_FragColor = vec4(0., 0., 1., 1.);
// return;
discard;
if (vPathPosition.y < 0.0 || vPathPosition.y > vPathLength) {
// if joint is rounded, test distance from the corner
if (jointType > 0.0 && length(vCornerOffset) > 1.0) {
discard;
}
// trim miter
if (jointType == 0.0 && vMiterLength > miterLimit + 1.0) {
discard;
}
}
gl_FragColor = vColor;
Expand Down
140 changes: 46 additions & 94 deletions modules/layers/src/path-layer/path-layer-vertex.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
export default `\
#define SHADER_NAME path-layer-vertex-shader
attribute vec3 positions;
attribute vec2 positions;
attribute float instanceTypes;
attribute vec3 instanceStartPositions;
Expand Down Expand Up @@ -61,144 +61,88 @@ float flipIfTrue(bool flag) {
// calculate line join positions
vec3 lineJoin(
vec3 prevPoint, vec3 currPoint, vec3 nextPoint,
float relativePosition, bool isEnd, bool isJoint,
vec2 width, vec2 widthPixels
vec2 width
) {
bool isEnd = positions.x > 0.0;
// side of the segment - -1: left, 0: center, 1: right
float sideOfPath = positions.y;
float isJoint = float(sideOfPath == 0.0);
vec2 deltaA = (currPoint.xy - prevPoint.xy) / width;
vec2 deltaB = (nextPoint.xy - currPoint.xy) / width;
float lenA = length(deltaA);
float lenB = length(deltaB);
// when two points are closer than PIXEL_EPSILON in pixels,
// assume they are the same point to avoid precision issue
lenA = lenA > EPSILON ? lenA : 0.0;
lenB = lenB > EPSILON ? lenB : 0.0;
vec2 dirA = lenA > 0. ? normalize(deltaA) : vec2(0.0, 0.0);
vec2 dirB = lenB > 0. ? normalize(deltaB) : vec2(0.0, 0.0);
vec2 perpA = vec2(-dirA.y, dirA.x);
vec2 perpB = vec2(-dirB.y, dirB.x);
// tangent of the corner
vec2 tangent = vec2(dirA + dirB);
vec2 tangent = dirA + dirB;
tangent = length(tangent) > 0. ? normalize(tangent) : perpA;
// direction of the corner
vec2 miterVec = vec2(-tangent.y, tangent.x);
// width offset from current position
// direction of the segment
vec2 dir = isEnd ? dirA : dirB;
// direction of the extrusion
vec2 perp = isEnd ? perpA : perpB;
// length of the segment
float L = isEnd ? lenA : lenB;
// cap super sharp angles
// A = angle of the corner
float sinHalfA = abs(dot(miterVec, perp));
float cosHalfA = abs(dot(dirA, miterVec));
bool turnsRight = dirA.x * dirB.y > dirA.y * dirB.x;
float offsetScale = 1.0 / max(sinHalfA, EPSILON);
float cornerPosition = isJoint ?
0.0 :
flipIfTrue(turnsRight == (relativePosition > 0.0));
// -1: right, 1: left
float turnDirection = flipIfTrue(dirA.x * dirB.y > dirA.y * dirB.x);
// do not bevel if line segment is too short
cornerPosition *=
float(cornerPosition <= 0.0 || sinHalfA < min(lenA, lenB) * cosHalfA);
// relative position to the corner:
// -1: inside (smaller side of the angle)
// 0: center
// 1: outside (bigger side of the angle)
float cornerPosition = sideOfPath * turnDirection;
float miterSize = 1.0 / max(sinHalfA, EPSILON);
// trim if inside corner extends further than the line segment
if (cornerPosition < 0.0) {
offsetScale = min(offsetScale, L / max(cosHalfA, EPSILON));
}
vMiterLength = cornerPosition >= 0.0 ?
mix(offsetScale, 0.0, cornerPosition) :
offsetScale * cornerPosition;
vMiterLength -= sinHalfA * jointType;
float offsetDirection = mix(
positions.y,
mix(
flipIfTrue(turnsRight),
positions.y * flipIfTrue(turnsRight == (positions.x == 1.)),
cornerPosition
),
miterSize = mix(
min(miterSize, max(lenA, lenB) / max(cosHalfA, EPSILON)),
miterSize,
step(0.0, cornerPosition)
);
vec2 offsetVec = mix(miterVec, -tangent, step(0.5, cornerPosition));
offsetScale = mix(offsetScale, 1.0 / max(cosHalfA, 0.001), step(0.5, cornerPosition));
vec2 offsetVec = mix(miterVec * miterSize, perp, step(0.5, cornerPosition))
* (sideOfPath + isJoint * turnDirection);
// special treatment for start cap and end cap
bool isStartCap = lenA == 0.0 || (!isEnd && (instanceTypes == 1.0 || instanceTypes == 3.0));
bool isEndCap = lenB == 0.0 || (isEnd && (instanceTypes == 2.0 || instanceTypes == 3.0));
bool isCap = isStartCap || isEndCap;
// 0: center, 1: side
cornerPosition = isCap ? (1.0 - positions.z) : 0.;
// start of path: use next - curr
if (isStartCap) {
offsetVec = mix(dirB, perpB, cornerPosition);
}
// end of path: use curr - prev
if (isEndCap) {
offsetVec = mix(dirA, perpA, cornerPosition);
}
// extend out a triangle to envelope the round cap
if (isCap) {
offsetScale = mix(4.0 * jointType, 1.0, cornerPosition);
vMiterLength = 1.0 - cornerPosition;
offsetDirection = mix(flipIfTrue(isStartCap), positions.y, cornerPosition);
offsetVec = mix(perp * sideOfPath, dir * jointType * 4.0 * flipIfTrue(isStartCap), isJoint);
}
vCornerOffset = offsetVec * offsetDirection * offsetScale;
// Generate variables for dash calculation
// Generate variables for fragment shader
vPathLength = L;
// vec2 offsetFromStartOfPath = isEnd ? vCornerOffset + deltaA : vCornerOffset;
vec2 offsetFromStartOfPath = vCornerOffset;
if (isEnd) {
offsetFromStartOfPath += deltaA;
}
vec2 dir = isEnd ? dirA : dirB;
vCornerOffset = offsetVec;
vMiterLength = dot(vCornerOffset, miterVec * turnDirection);
vMiterLength = isCap ? isJoint : vMiterLength;
vec2 offsetFromStartOfPath = vCornerOffset + deltaA * float(isEnd);
vPathPosition = vec2(
positions.y + positions.z * offsetDirection,
dot(offsetFromStartOfPath, perp),
dot(offsetFromStartOfPath, dir)
);
geometry.uv = vPathPosition;
float isValid = step(instanceTypes, 3.5);
vec3 offset = vec3(vCornerOffset * widthPixels * isValid, 0.0);
vec3 offset = vec3(offsetVec * width * isValid, 0.0);
DECKGL_FILTER_SIZE(offset, geometry);
return currPoint + vec3(offset.xy / widthPixels * width, 0.0);
}
// calculate line join positions
// extract params from attributes and uniforms
vec3 lineJoin(vec3 prevPoint, vec3 currPoint, vec3 nextPoint) {
// relative position to the corner:
// -1: inside (smaller side of the angle)
// 0: center
// 1: outside (bigger side of the angle)
float relativePosition = positions.y;
bool isEnd = positions.x > EPSILON;
bool isJoint = positions.z > EPSILON;
vec2 widthPixels = vec2(clamp(project_size_to_pixel(instanceStrokeWidths * widthScale),
widthMinPixels, widthMaxPixels) / 2.0);
vec2 width = billboard ? project_pixel_size_to_clipspace(widthPixels) : project_pixel_size(widthPixels);
return lineJoin(
prevPoint, currPoint, nextPoint,
relativePosition, isEnd, isJoint,
width, widthPixels
);
return currPoint + offset;
}
// In clipspace extrusion, if a line extends behind the camera, clip it to avoid visual artifacts
Expand All @@ -214,6 +158,9 @@ void main() {
geometry.worldPositionAlt = instanceEndPositions;
geometry.pickingColor = instancePickingColors;
vec2 widthPixels = vec2(clamp(project_size_to_pixel(instanceStrokeWidths * widthScale),
widthMinPixels, widthMaxPixels) / 2.0);
vColor = vec4(instanceColors.rgb, instanceColors.a * opacity);
float isEnd = positions.x;
Expand All @@ -237,10 +184,13 @@ void main() {
clipLine(nextPositionScreen, currPositionScreen);
clipLine(currPositionScreen, mix(nextPositionScreen, prevPositionScreen, isEnd));
vec2 width = project_pixel_size_to_clipspace(widthPixels);
vec3 pos = lineJoin(
prevPositionScreen.xyz / prevPositionScreen.w,
currPositionScreen.xyz / currPositionScreen.w,
nextPositionScreen.xyz / nextPositionScreen.w
nextPositionScreen.xyz / nextPositionScreen.w,
width
);
gl_Position = vec4(pos * currPositionScreen.w, currPositionScreen.w);
Expand All @@ -250,8 +200,10 @@ void main() {
currPosition = project_position(currPosition, currPosition64Low);
nextPosition = project_position(nextPosition, nextPosition64Low);
vec2 width = project_pixel_size(widthPixels);
vec4 pos = vec4(
lineJoin(prevPosition, currPosition, nextPosition),
lineJoin(prevPosition, currPosition, nextPosition, width),
1.0);
geometry.position = pos;
gl_Position = project_common_position_to_clipspace(pos);
Expand Down
13 changes: 3 additions & 10 deletions modules/layers/src/path-layer/path-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,33 +232,26 @@ export default class PathLayer extends Layer {
];

// [0] position on segment - 0: start, 1: end
// [1] side of path - -1: left, 0: center, 1: right
// [2] role - 0: offset point 1: joint point
// [1] side of path - -1: left, 0: center (joint), 1: right
const SEGMENT_POSITIONS = [
// bevel start corner
0,
0,
1,
// start inner corner
0,
-1,
0,
// start outer corner
0,
1,
0,
// end inner corner
1,
-1,
0,
// end outer corner
1,
1,
0,
// bevel end corner
1,
0,
1
0
];

return new Model(
Expand All @@ -269,7 +262,7 @@ export default class PathLayer extends Layer {
drawMode: GL.TRIANGLES,
attributes: {
indices: new Uint16Array(SEGMENT_INDICES),
positions: new Float32Array(SEGMENT_POSITIONS)
positions: {value: new Float32Array(SEGMENT_POSITIONS), size: 2}
}
}),
isInstanced: true
Expand Down

0 comments on commit cb74907

Please sign in to comment.