Fix rotation artefacts by rendering curves in two passes, clipping at X

inflection points as necessary.
This commit is contained in:
Patrick Walton 2017-12-03 13:20:46 -08:00
parent 801c25305f
commit 9b59ce2443
7 changed files with 358 additions and 93 deletions

View File

@ -23,6 +23,7 @@ export interface ShaderMap<T> {
direct3DInterior: T; direct3DInterior: T;
ecaaLine: T; ecaaLine: T;
ecaaCurve: T; ecaaCurve: T;
ecaaTransformedCurve: T;
mcaaCover: T; mcaaCover: T;
mcaaLine: T; mcaaLine: T;
mcaaCurve: T; mcaaCurve: T;
@ -32,6 +33,7 @@ export interface ShaderMap<T> {
xcaaMultiDirectCurve: T; xcaaMultiDirectCurve: T;
xcaaMultiDirectInterior: T; xcaaMultiDirectInterior: T;
xcaaMultiEdgeMaskCurve: T; xcaaMultiEdgeMaskCurve: T;
xcaaMultiEdgeMaskTransformedCurve: T;
xcaaMultiEdgeMaskLine: T; xcaaMultiEdgeMaskLine: T;
xcaaMultiResolve: T; xcaaMultiResolve: T;
} }
@ -57,11 +59,13 @@ export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'mcaaCurve', 'mcaaCurve',
'ecaaLine', 'ecaaLine',
'ecaaCurve', 'ecaaCurve',
'ecaaTransformedCurve',
'xcaaMonoResolve', 'xcaaMonoResolve',
'xcaaMonoSubpixelResolve', 'xcaaMonoSubpixelResolve',
'xcaaMultiDirectCurve', 'xcaaMultiDirectCurve',
'xcaaMultiDirectInterior', 'xcaaMultiDirectInterior',
'xcaaMultiEdgeMaskCurve', 'xcaaMultiEdgeMaskCurve',
'xcaaMultiEdgeMaskTransformedCurve',
'xcaaMultiEdgeMaskLine', 'xcaaMultiEdgeMaskLine',
'xcaaMultiResolve', 'xcaaMultiResolve',
'demo3DDistantGlyph', 'demo3DDistantGlyph',
@ -113,6 +117,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
fragment: "/glsl/gles2/xcaa-line.fs.glsl", fragment: "/glsl/gles2/xcaa-line.fs.glsl",
vertex: "/glsl/gles2/ecaa-line.vs.glsl", vertex: "/glsl/gles2/ecaa-line.vs.glsl",
}, },
ecaaTransformedCurve: {
fragment: "/glsl/gles2/xcaa-curve.fs.glsl",
vertex: "/glsl/gles2/ecaa-transformed-curve.vs.glsl",
},
mcaaCover: { mcaaCover: {
fragment: "/glsl/gles2/mcaa-cover.fs.glsl", fragment: "/glsl/gles2/mcaa-cover.fs.glsl",
vertex: "/glsl/gles2/mcaa-cover.vs.glsl", vertex: "/glsl/gles2/mcaa-cover.vs.glsl",
@ -153,6 +161,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-line.fs.glsl", fragment: "/glsl/gles2/xcaa-multi-edge-mask-line.fs.glsl",
vertex: "/glsl/gles2/ecaa-multi-edge-mask-line.vs.glsl", vertex: "/glsl/gles2/ecaa-multi-edge-mask-line.vs.glsl",
}, },
xcaaMultiEdgeMaskTransformedCurve: {
fragment: "/glsl/gles2/xcaa-multi-edge-mask-curve.fs.glsl",
vertex: "/glsl/gles2/ecaa-multi-edge-mask-transformed-curve.vs.glsl",
},
xcaaMultiResolve: { xcaaMultiResolve: {
fragment: "/glsl/gles2/xcaa-multi-resolve.fs.glsl", fragment: "/glsl/gles2/xcaa-multi-resolve.fs.glsl",
vertex: "/glsl/gles2/xcaa-multi-resolve.vs.glsl", vertex: "/glsl/gles2/xcaa-multi-resolve.vs.glsl",

View File

@ -781,9 +781,10 @@ export abstract class ECAAStrategy extends XCAAStrategy {
this.antialiasLinesOfObjectWithProgram(renderer, this.antialiasLinesOfObjectWithProgram(renderer,
objectIndex, objectIndex,
this.lineShaderProgramNames[0]); this.lineShaderProgramNames[0]);
this.antialiasCurvesOfObjectWithProgram(renderer, this.antialiasCurvesOfObjectWithPrograms(renderer,
objectIndex, objectIndex,
this.curveShaderProgramNames[0]); this.curveShaderProgramNames[0],
this.curveShaderProgramNames[1]);
} }
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): void { protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): void {
@ -850,10 +851,25 @@ export abstract class ECAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
} }
protected antialiasCurvesOfObjectWithProgram(renderer: Renderer, protected antialiasCurvesOfObjectWithPrograms(renderer: Renderer,
objectIndex: number, objectIndex: number,
programName: keyof ShaderMap<void>): stProgram: keyof ShaderMap<void>,
void { transformedProgram: keyof ShaderMap<void>):
void {
if (renderer.usesSTTransform) {
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, stProgram, 0);
return;
}
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, transformedProgram, 0);
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, transformedProgram, 1);
}
private antialiasCurvesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
programName: keyof ShaderMap<void>,
passIndex: number):
void {
if (renderer.meshData == null) if (renderer.meshData == null)
return; return;
@ -867,6 +883,7 @@ export abstract class ECAAStrategy extends XCAAStrategy {
gl.useProgram(curveProgram.program); gl.useProgram(curveProgram.program);
const uniforms = curveProgram.uniforms; const uniforms = curveProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex); this.setAAUniforms(renderer, uniforms, objectIndex);
gl.uniform1i(uniforms.uPassIndex, passIndex);
const vao = this.curveVAOs[programName]; const vao = this.curveVAOs[programName];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
@ -1061,7 +1078,7 @@ export class ECAAMonochromeStrategy extends ECAAStrategy {
} }
protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> { protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaCurve']; return ['ecaaCurve', 'ecaaTransformedCurve'];
} }
} }
@ -1188,7 +1205,12 @@ export class ECAAMulticolorStrategy extends ECAAStrategy {
} }
protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> { protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaCurve', 'xcaaMultiEdgeMaskCurve']; return [
'ecaaCurve',
'ecaaTransformedCurve',
'xcaaMultiEdgeMaskCurve',
'xcaaMultiEdgeMaskTransformedCurve',
];
} }
private edgeMaskVAO: WebGLVertexArrayObject; private edgeMaskVAO: WebGLVertexArrayObject;
@ -1233,7 +1255,10 @@ export class ECAAMulticolorStrategy extends ECAAStrategy {
// Perform edge masking. // Perform edge masking.
gl.colorMask(false, false, false, false); gl.colorMask(false, false, false, false);
this.antialiasLinesOfObjectWithProgram(renderer, objectIndex, 'xcaaMultiEdgeMaskLine'); this.antialiasLinesOfObjectWithProgram(renderer, objectIndex, 'xcaaMultiEdgeMaskLine');
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, 'xcaaMultiEdgeMaskCurve'); this.antialiasCurvesOfObjectWithPrograms(renderer,
objectIndex,
'xcaaMultiEdgeMaskCurve',
'xcaaMultiEdgeMaskTransformedCurve');
gl.colorMask(true, true, true, true); gl.colorMask(true, true, true, true);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);

View File

@ -126,17 +126,6 @@ vec2 computeXCAAClipSpaceQuadPosition(vec4 extents, vec2 quadPosition, ivec2 fra
return convertScreenToClipSpace(position, framebufferSize); return convertScreenToClipSpace(position, framebufferSize);
} }
vec2 computeXCAAEdgeBoundedClipSpaceQuadPosition(vec2 leftPosition,
vec2 rightPosition,
vec2 quadPosition,
ivec2 framebufferSize) {
vec4 extents = vec4(leftPosition.x,
min(leftPosition.y, rightPosition.y),
rightPosition.x,
max(leftPosition.y, rightPosition.y));
return computeXCAAClipSpaceQuadPosition(extents, quadPosition, framebufferSize);
}
vec2 computeMCAAPosition(vec2 position, vec2 computeMCAAPosition(vec2 position,
vec4 hints, vec4 hints,
vec4 localTransformST, vec4 localTransformST,
@ -172,10 +161,11 @@ bool computeMCAAQuadPosition(out vec2 outPosition,
return false; return false;
} }
outPosition = computeXCAAEdgeBoundedClipSpaceQuadPosition(leftPosition, vec4 extents = vec4(leftPosition.x,
rightPosition, min(leftPosition.y, rightPosition.y),
quadPosition, rightPosition.x,
framebufferSize); max(leftPosition.y, rightPosition.y));
outPosition = computeXCAAClipSpaceQuadPosition(extents, quadPosition, framebufferSize);
return true; return true;
} }
@ -228,6 +218,31 @@ float computeECAAWinding(inout vec2 leftPosition, inout vec2 rightPosition) {
return rightPosition.x - leftPosition.x > EPSILON ? winding : 0.0; return rightPosition.x - leftPosition.x > EPSILON ? winding : 0.0;
} }
vec2 computeECAAQuadPositionFromTransformedPositions(vec2 leftPosition,
vec2 rightPosition,
vec2 quadPosition,
ivec2 framebufferSize,
vec4 localTransformST,
vec2 localTransformExt,
mat4 globalTransform,
vec4 bounds,
vec3 leftTopRightEdges) {
vec2 edgeBL = bounds.xy, edgeTL = bounds.xw, edgeTR = bounds.zw, edgeBR = bounds.zy;
edgeBL = transformECAAPosition(edgeBL, localTransformST, localTransformExt, globalTransform);
edgeBR = transformECAAPosition(edgeBR, localTransformST, localTransformExt, globalTransform);
edgeTL = transformECAAPosition(edgeTL, localTransformST, localTransformExt, globalTransform);
edgeTR = transformECAAPosition(edgeTR, localTransformST, localTransformExt, globalTransform);
// Find the bottom of the path, and convert to clip space.
//
// FIXME(pcwalton): Speed this up somehow?
float pathBottomY = max(max(edgeBL.y, edgeBR.y), max(edgeTL.y, edgeTR.y));
pathBottomY = (pathBottomY + 1.0) * 0.5 * float(framebufferSize.y);
vec4 extents = vec4(leftTopRightEdges, pathBottomY);
return computeXCAAClipSpaceQuadPosition(extents, quadPosition, framebufferSize);
}
// FIXME(pcwalton): Clean up this signature somehow? // FIXME(pcwalton): Clean up this signature somehow?
bool computeECAAQuadPosition(out vec2 outPosition, bool computeECAAQuadPosition(out vec2 outPosition,
out float outWinding, out float outWinding,
@ -259,12 +274,6 @@ bool computeECAAQuadPosition(out vec2 outPosition,
globalTransform, globalTransform,
framebufferSize); framebufferSize);
vec2 edgeBL = bounds.xy, edgeTL = bounds.xw, edgeTR = bounds.zw, edgeBR = bounds.zy;
edgeBL = transformECAAPosition(edgeBL, localTransformST, localTransformExt, globalTransform);
edgeBR = transformECAAPosition(edgeBR, localTransformST, localTransformExt, globalTransform);
edgeTL = transformECAAPosition(edgeTL, localTransformST, localTransformExt, globalTransform);
edgeTR = transformECAAPosition(edgeTR, localTransformST, localTransformExt, globalTransform);
float winding = computeECAAWinding(leftPosition, rightPosition); float winding = computeECAAWinding(leftPosition, rightPosition);
outWinding = winding; outWinding = winding;
if (winding == 0.0) { if (winding == 0.0) {
@ -272,49 +281,54 @@ bool computeECAAQuadPosition(out vec2 outPosition,
return false; return false;
} }
// Find the bottom of the path, and convert to clip space. vec3 leftTopRightEdges = vec3(leftPosition.x,
// min(leftPosition.y, rightPosition.y),
// FIXME(pcwalton): Speed this up somehow? rightPosition.x);
float pathBottomY = max(max(edgeBL.y, edgeBR.y), max(edgeTL.y, edgeTR.y)); outPosition = computeECAAQuadPositionFromTransformedPositions(leftPosition,
pathBottomY = (pathBottomY + 1.0) * 0.5 * float(framebufferSize.y); rightPosition,
quadPosition,
vec4 extents = vec4(leftPosition.x, framebufferSize,
min(leftPosition.y, rightPosition.y), localTransformST,
rightPosition.x, localTransformExt,
pathBottomY); globalTransform,
outPosition = computeXCAAClipSpaceQuadPosition(extents, quadPosition, framebufferSize); bounds,
leftTopRightEdges);
return true; return true;
} }
bool computeECAAMultiEdgeMaskQuadPosition(out vec2 outPosition, bool splitCurveAndComputeECAAWinding(out float outWinding,
inout vec2 leftPosition, out vec3 outLeftTopRightEdges,
inout vec2 rightPosition, inout vec2 leftPosition,
vec2 quadPosition, inout vec2 rightPosition,
ivec2 framebufferSize, vec2 controlPointPosition,
vec4 localTransformST, int passIndex) {
vec2 localTransformExt, // Split at the X inflection point if necessary.
mat4 globalTransform) { float num = leftPosition.x - controlPointPosition.x;
leftPosition = transformECAAPositionToScreenSpace(leftPosition, float denom = leftPosition.x - 2.0 * controlPointPosition.x + rightPosition.x;
localTransformST, float inflectionT = num / denom;
localTransformExt, if (inflectionT > EPSILON && inflectionT < 1.0 - EPSILON) {
globalTransform, vec2 newCP0 = mix(leftPosition, controlPointPosition, inflectionT);
framebufferSize); vec2 newCP1 = mix(controlPointPosition, rightPosition, inflectionT);
rightPosition = transformECAAPositionToScreenSpace(rightPosition, vec2 inflectionPoint = mix(newCP0, newCP1, inflectionT);
localTransformST, if (passIndex == 0) {
localTransformExt, controlPointPosition = newCP0;
globalTransform, rightPosition = inflectionPoint;
framebufferSize); } else {
controlPointPosition = newCP1;
float winding = computeECAAWinding(leftPosition, rightPosition); leftPosition = inflectionPoint;
if (winding == 0.0) { }
outPosition = vec2(0.0); } else if (passIndex != 0) {
return false; return false;
} }
outPosition = computeXCAAEdgeBoundedClipSpaceQuadPosition(leftPosition, float winding = computeECAAWinding(leftPosition, rightPosition);
rightPosition, outWinding = winding;
quadPosition, if (winding == 0.0)
framebufferSize); return false;
outLeftTopRightEdges = vec3(min(leftPosition.x, controlPointPosition.x),
min(min(leftPosition.y, controlPointPosition.y), rightPosition.y),
max(rightPosition.x, controlPointPosition.x));
return true; return true;
} }

View File

@ -16,6 +16,7 @@ uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST; uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions; uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt; uniform sampler2D uPathTransformExt;
uniform int uPassIndex;
attribute vec2 aQuadPosition; attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition; attribute vec2 aLeftPosition;
@ -40,23 +41,35 @@ void main() {
uPathTransformExtDimensions, uPathTransformExtDimensions,
pathID); pathID);
// Transform the points, and compute the position of this vertex. // Transform the points.
vec2 position; leftPosition = transformECAAPositionToScreenSpace(leftPosition,
if (computeECAAMultiEdgeMaskQuadPosition(position, pathTransformST,
leftPosition, pathTransformExt,
rightPosition, uTransform,
aQuadPosition, uFramebufferSize);
uFramebufferSize, rightPosition = transformECAAPositionToScreenSpace(rightPosition,
pathTransformST, pathTransformST,
pathTransformExt, pathTransformExt,
uTransform)) { uTransform,
controlPointPosition = transformECAAPositionToScreenSpace(controlPointPosition, uFramebufferSize);
pathTransformST, controlPointPosition = transformECAAPositionToScreenSpace(controlPointPosition,
pathTransformExt, pathTransformST,
uTransform, pathTransformExt,
uFramebufferSize); uTransform,
uFramebufferSize);
float winding = computeECAAWinding(leftPosition, rightPosition);
if (winding == 0.0) {
gl_Position = vec4(0.0);
return;
} }
vec4 extents = vec4(leftPosition.x,
min(leftPosition.y, rightPosition.y),
rightPosition.y,
max(leftPosition.y, rightPosition.y));
vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID); float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0); gl_Position = vec4(position, depth, 1.0);

View File

@ -38,16 +38,29 @@ void main() {
uPathTransformExtDimensions, uPathTransformExtDimensions,
pathID); pathID);
// Transform the points, and compute the position of this vertex. // Transform the points.
vec2 position; leftPosition = transformECAAPositionToScreenSpace(leftPosition,
computeECAAMultiEdgeMaskQuadPosition(position, pathTransformST,
leftPosition, pathTransformExt,
rightPosition, uTransform,
aQuadPosition, uFramebufferSize);
uFramebufferSize, rightPosition = transformECAAPositionToScreenSpace(rightPosition,
pathTransformST, pathTransformST,
pathTransformExt, pathTransformExt,
uTransform); uTransform,
uFramebufferSize);
float winding = computeECAAWinding(leftPosition, rightPosition);
if (winding == 0.0) {
gl_Position = vec4(0.0);
return;
}
vec4 extents = vec4(min(leftPosition.x, rightPosition.x),
min(leftPosition.y, rightPosition.y),
max(rightPosition.x, rightPosition.x),
max(leftPosition.y, rightPosition.y));
vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID); float depth = convertPathIndexToViewportDepthValue(pathID);

View File

@ -0,0 +1,83 @@
// pathfinder/shaders/gles2/ecaa-multi-edge-mask-transformed-curve.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
uniform mat4 uTransform;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt;
uniform int uPassIndex;
attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aControlPointPosition;
attribute vec2 aRightPosition;
attribute float aPathID;
varying vec4 vEndpoints;
varying vec2 vControlPoint;
void main() {
vec2 leftPosition = aLeftPosition;
vec2 controlPointPosition = aControlPointPosition;
vec2 rightPosition = aRightPosition;
int pathID = int(aPathID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
// Transform the points.
leftPosition = transformECAAPositionToScreenSpace(leftPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
rightPosition = transformECAAPositionToScreenSpace(rightPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
controlPointPosition = transformECAAPositionToScreenSpace(controlPointPosition,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
float winding;
vec3 leftTopRightEdges;
if (!splitCurveAndComputeECAAWinding(winding,
leftTopRightEdges,
leftPosition,
rightPosition,
controlPointPosition,
uPassIndex)) {
gl_Position = vec4(0.0);
return;
}
vec4 extents = vec4(leftTopRightEdges,
max(max(leftPosition.y, rightPosition.y), controlPointPosition.y));
vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vEndpoints = vec4(leftPosition, rightPosition);
vControlPoint = controlPointPosition;
}

View File

@ -0,0 +1,105 @@
// pathfinder/shaders/gles2/ecaa-transformed-curve.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
uniform mat4 uTransform;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathTransformExtDimensions;
uniform sampler2D uPathTransformExt;
uniform ivec2 uPathBoundsDimensions;
uniform sampler2D uPathBounds;
uniform vec2 uEmboldenAmount;
uniform int uPassIndex;
attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aControlPointPosition;
attribute vec2 aRightPosition;
attribute float aPathID;
attribute vec3 aNormalAngles;
varying vec4 vEndpoints;
varying vec2 vControlPoint;
varying float vWinding;
void main() {
vec2 leftPosition = aLeftPosition;
vec2 controlPointPosition = aControlPointPosition;
vec2 rightPosition = aRightPosition;
int pathID = int(aPathID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
vec4 bounds = fetchFloat4Data(uPathBounds, pathID, uPathBoundsDimensions);
// Transform the points.
leftPosition = computeECAAPosition(leftPosition,
aNormalAngles.x,
uEmboldenAmount,
uHints,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
rightPosition = computeECAAPosition(rightPosition,
aNormalAngles.z,
uEmboldenAmount,
uHints,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
controlPointPosition = computeECAAPosition(controlPointPosition,
aNormalAngles.y,
uEmboldenAmount,
uHints,
pathTransformST,
pathTransformExt,
uTransform,
uFramebufferSize);
float winding;
vec3 leftTopRightEdges;
if (!splitCurveAndComputeECAAWinding(winding,
leftTopRightEdges,
leftPosition,
rightPosition,
controlPointPosition,
uPassIndex)) {
gl_Position = vec4(0.0);
return;
}
vec2 position = computeECAAQuadPositionFromTransformedPositions(leftPosition,
rightPosition,
aQuadPosition,
uFramebufferSize,
pathTransformST,
pathTransformExt,
uTransform,
bounds,
leftTopRightEdges);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vEndpoints = vec4(leftPosition, rightPosition);
vControlPoint = controlPointPosition;
vWinding = winding;
}