Replace the MCAA shader with an extended Loop-Blinn approach and an

approximation of area based on approximate first-order line distance.

This enables support for full affine transforms in MCAA. It also greatly
simplifies the shader and reduces the amount of work that the GPU needs
to do for fragment shading.

Experimental testing has shown the area approximation to be accurate to
about 4%.
This commit is contained in:
Patrick Walton 2018-01-30 09:34:34 -08:00
parent 5bd68dec65
commit 5879d9778d
9 changed files with 483 additions and 360 deletions

View File

@ -1,6 +1,6 @@
// pathfinder/client/src/meshes.ts
//
// Copyright © 2017 The Pathfinder Project Developers.
// Copyright © 2018 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
@ -87,6 +87,8 @@ const SEGMENT_LINE_SIZE: number = 4 * 4;
const SEGMENT_CURVE_SIZE: number = 4 * 6;
const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
bBoxPathIDs: { type: 'Uint16', size: 1 },
bBoxes: { type: 'Float32', size: 20 },
bQuadVertexInteriorIndices: { type: 'Uint32', size: 1 },
bQuadVertexPositionPathIDs: { type: 'Uint16', size: 6 },
bQuadVertexPositions: { type: 'Float32', size: 12 },
@ -99,6 +101,8 @@ const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
};
const BUFFER_TYPES: Meshes<BufferType> = {
bBoxPathIDs: 'ARRAY_BUFFER',
bBoxes: 'ARRAY_BUFFER',
bQuadVertexInteriorIndices: 'ELEMENT_ARRAY_BUFFER',
bQuadVertexPositionPathIDs: 'ARRAY_BUFFER',
bQuadVertexPositions: 'ARRAY_BUFFER',
@ -118,6 +122,7 @@ const MESH_LIBRARY_FOURCC: string = 'PFML';
// Must match the FourCCs in `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into()`.
const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
bbox: 'bBoxes',
bqii: 'bQuadVertexInteriorIndices',
bqvp: 'bQuadVertexPositions',
scur: 'segmentCurves',
@ -129,6 +134,7 @@ const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
// Must match the FourCCs in
// `pathfinder_partitioner::mesh_library::MeshLibrary::serialize_into::write_path_ranges()`.
const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = {
bbox: 'bBoxPathRanges',
bqii: 'bQuadVertexInteriorIndexPathRanges',
bqvp: 'bQuadVertexPositionPathRanges',
scur: 'segmentCurveRanges',
@ -136,6 +142,7 @@ const PATH_RANGE_TYPE_FOURCCS: PathRangeTypeFourCCTable = {
};
const RANGE_TO_COUNT_TABLE: RangeToCountTable = {
bBoxPathRanges: 'bBoxCount',
bQuadVertexInteriorIndexPathRanges: 'bQuadVertexInteriorIndexCount',
bQuadVertexPositionPathRanges: 'bQuadVertexPositionCount',
segmentCurveRanges: 'segmentCurveCount',
@ -143,6 +150,7 @@ const RANGE_TO_COUNT_TABLE: RangeToCountTable = {
};
const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = {
bBoxPathRanges: 'bBoxPathIDs',
bQuadVertexPositionPathRanges: 'bQuadVertexPositionPathIDs',
segmentCurveRanges: 'segmentCurvePathIDs',
segmentLineRanges: 'segmentLinePathIDs',
@ -151,6 +159,7 @@ const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = {
const RANGE_KEYS: Array<keyof PathRanges> = [
'bQuadVertexPositionPathRanges',
'bQuadVertexInteriorIndexPathRanges',
'bBoxPathRanges',
'segmentCurveRanges',
'segmentLineRanges',
];
@ -160,12 +169,14 @@ type BufferType = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER';
export interface Meshes<T> {
readonly bQuadVertexPositions: T;
readonly bQuadVertexInteriorIndices: T;
readonly bBoxes: T;
readonly segmentLines: T;
readonly segmentCurves: T;
readonly segmentLineNormals: T;
readonly segmentCurveNormals: T;
bQuadVertexPositionPathIDs: T;
bBoxPathIDs: T;
segmentLinePathIDs: T;
segmentCurvePathIDs: T;
}
@ -173,6 +184,7 @@ export interface Meshes<T> {
interface MeshDataCounts {
readonly bQuadVertexPositionCount: number;
readonly bQuadVertexInteriorIndexCount: number;
readonly bBoxCount: number;
readonly segmentLineCount: number;
readonly segmentCurveCount: number;
}
@ -180,6 +192,7 @@ interface MeshDataCounts {
interface PathRanges {
bQuadVertexPositionPathRanges: Range[];
bQuadVertexInteriorIndexPathRanges: Range[];
bBoxPathRanges: Range[];
segmentCurveRanges: Range[];
segmentLineRanges: Range[];
}
@ -187,6 +200,9 @@ interface PathRanges {
export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts, PathRanges {
readonly bQuadVertexPositions: ArrayBuffer;
readonly bQuadVertexInteriorIndices: ArrayBuffer;
readonly bBoxes: ArrayBuffer;
readonly bBoxSigns: ArrayBuffer;
readonly bBoxIndices: ArrayBuffer;
readonly segmentLines: ArrayBuffer;
readonly segmentCurves: ArrayBuffer;
readonly segmentLineNormals: ArrayBuffer;
@ -194,15 +210,18 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
readonly bQuadVertexPositionCount: number;
readonly bQuadVertexInteriorIndexCount: number;
readonly bBoxCount: number;
readonly segmentLineCount: number;
readonly segmentCurveCount: number;
bQuadVertexPositionPathIDs: ArrayBuffer;
bBoxPathIDs: ArrayBuffer;
segmentCurvePathIDs: ArrayBuffer;
segmentLinePathIDs: ArrayBuffer;
bQuadVertexPositionPathRanges: Range[];
bQuadVertexInteriorIndexPathRanges: Range[];
bBoxPathRanges: Range[];
segmentCurveRanges: Range[];
segmentLineRanges: Range[];
@ -241,6 +260,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
B_QUAD_VERTEX_POSITION_SIZE;
this.bQuadVertexInteriorIndexCount = this.bQuadVertexInteriorIndices.byteLength /
INDEX_SIZE;
this.bBoxCount = this.bBoxes.byteLength / (FLOAT32_SIZE * 6);
this.segmentCurveCount = this.segmentCurves.byteLength / SEGMENT_CURVE_SIZE;
this.segmentLineCount = this.segmentLines.byteLength / SEGMENT_LINE_SIZE;
@ -292,7 +312,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
const firstBQuadVertexIndex = bQuadVertexCopyResult.originalStartIndex;
const lastBQuadVertexIndex = bQuadVertexCopyResult.originalEndIndex;
// Copy over indices.
// Copy over B-vertex indices.
copyIndices(expandedArrays.bQuadVertexInteriorIndices,
expandedRanges.bQuadVertexInteriorIndexPathRanges,
originalBuffers.bQuadVertexInteriorIndices as Uint32Array,
@ -301,6 +321,23 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
lastBQuadVertexIndex * 6,
expandedPathID);
// Copy over B-boxes.
const bBoxVertexCopyResult = copyVertices(['bBoxes'],
'bBoxPathRanges',
expandedArrays,
expandedRanges,
originalBuffers,
originalRanges,
expandedPathID,
originalPathID);
if (bBoxVertexCopyResult == null)
continue;
const firstExpandedBBoxIndex = bBoxVertexCopyResult.expandedStartIndex;
const firstBBoxIndex = bBoxVertexCopyResult.originalStartIndex;
const lastBBoxIndex = bBoxVertexCopyResult.originalEndIndex;
// Copy over segments.
copySegments(['segmentLines', 'segmentLineNormals'],
'segmentLineRanges',
@ -361,9 +398,11 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
const rangeBufferKey = RANGE_TO_RANGE_BUFFER_TABLE[rangeKey];
const instanceCount = this[RANGE_TO_COUNT_TABLE[rangeKey]];
const fieldCount = MESH_TYPES[rangeBufferKey].size;
const ranges = this[rangeKey as keyof PathRanges];
const meshType = MESH_TYPES[rangeBufferKey];
const fieldCount = meshType.size;
const destBuffer = new Uint16Array(instanceCount * fieldCount);
let destIndex = 0;
for (let pathIndex = 0; pathIndex < ranges.length; pathIndex++) {
@ -385,6 +424,10 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly bQuadVertexPositions: WebGLBuffer;
readonly bQuadVertexPositionPathIDs: WebGLBuffer;
readonly bQuadVertexInteriorIndices: WebGLBuffer;
readonly bBoxes: WebGLBuffer;
readonly bBoxSigns: WebGLBuffer;
readonly bBoxIndices: WebGLBuffer;
readonly bBoxPathIDs: WebGLBuffer;
readonly segmentLines: WebGLBuffer;
readonly segmentCurves: WebGLBuffer;
readonly segmentLinePathIDs: WebGLBuffer;
@ -394,6 +437,7 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly bQuadVertexPositionPathRanges: Range[];
readonly bQuadVertexInteriorIndexPathRanges: Range[];
readonly bBoxPathRanges: Range[];
readonly segmentCurveRanges: Range[];
readonly segmentLineRanges: Range[];

View File

@ -267,14 +267,7 @@ export abstract class Renderer {
}
setTransformUniform(uniforms: UniformMap, pass: number, objectIndex: number): void {
let transform;
if (this.antialiasingStrategy == null)
transform = glmatrix.mat4.create();
else
transform = this.antialiasingStrategy.worldTransformForPass(this, pass);
glmatrix.mat4.mul(transform, transform, this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
const transform = this.computeTransform(pass, objectIndex);
this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
}
@ -284,10 +277,7 @@ export abstract class Renderer {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const transform = glmatrix.mat4.clone(this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
const translation = glmatrix.vec4.clone([transform[12], transform[13], 0.0, 1.0]);
const transform = this.computeTransform(0, objectIndex);
gl.uniform4f(uniforms.uTransformST,
transform[0],
@ -296,6 +286,22 @@ export abstract class Renderer {
transform[13]);
}
setTransformAffineUniforms(uniforms: UniformMap, objectIndex: number): void {
// FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an affine matrix is ugly and
// fragile. Refactor.
const renderContext = this.renderContext;
const gl = renderContext.gl;
const transform = this.computeTransform(0, objectIndex);
gl.uniform4f(uniforms.uTransformST,
transform[0],
transform[5],
transform[12],
transform[13]);
gl.uniform2f(uniforms.uTransformExt, transform[1], transform[4]);
}
uploadPathColors(objectCount: number): void {
const renderContext = this.renderContext;
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
@ -481,7 +487,10 @@ export abstract class Renderer {
this.initImplicitCoverInteriorVAO(objectIndex, instanceRange, renderingMode);
// Draw direct interior parts.
this.setTransformUniform(directInteriorProgram.uniforms, pass, objectIndex);
if (renderingMode === 'conservative')
this.setTransformAffineUniforms(directInteriorProgram.uniforms, objectIndex);
else
this.setTransformUniform(directInteriorProgram.uniforms, pass, objectIndex);
this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
this.setHintsUniform(directInteriorProgram.uniforms);
this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0);
@ -737,6 +746,18 @@ export abstract class Renderer {
gl.bufferData(gl.ARRAY_BUFFER, vertexIDs, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
private computeTransform(pass: number, objectIndex: number): glmatrix.mat4 {
let transform;
if (this.antialiasingStrategy == null)
transform = glmatrix.mat4.create();
else
transform = this.antialiasingStrategy.worldTransformForPass(this, pass);
glmatrix.mat4.mul(transform, transform, this.worldTransform);
glmatrix.mat4.mul(transform, transform, this.getModelviewTransform(objectIndex));
return transform;
}
}
function getMeshIndexRange(indexRanges: Range[], pathRange: Range): Range {

View File

@ -272,6 +272,7 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
this.vertexArrayObjectExt = this.gl.getExtension('OES_vertex_array_object');
this.gl.getExtension('EXT_frag_depth');
this.gl.getExtension('OES_element_index_uint');
this.gl.getExtension('OES_standard_derivatives');
this.gl.getExtension('OES_texture_float');
this.gl.getExtension('WEBGL_depth_texture');

View File

@ -42,10 +42,12 @@ const PATCH_VERTICES: Float32Array = new Float32Array([
1.0, 1.0,
]);
const MCAA_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3, 3, 5, 4]);
const MCAA_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 1, 3, 2]);
const ECAA_CURVE_PATCH_INDICES: Uint8Array = new Uint8Array([0, 1, 2, 0, 2, 3, 2, 5, 3]);
export type TransformType = 'dilation' | 'affine' | '3d';
export abstract class XCAAStrategy extends AntialiasingStrategy {
abstract readonly directRenderingMode: DirectRenderingMode;
@ -56,7 +58,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
return 1;
}
protected abstract get usesDilationTransforms(): boolean;
protected abstract get transformType(): TransformType;
protected abstract get patchIndices(): Uint8Array;
@ -255,10 +257,17 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
if (this.usesDilationTransforms)
switch (this.transformType) {
case 'dilation':
renderer.setTransformSTUniform(uniforms, 0);
else
break;
case 'affine':
renderer.setTransformAffineUniforms(uniforms, 0);
break;
case '3d':
renderer.setTransformUniform(uniforms, 0, 0);
break;
}
gl.uniform2i(uniforms.uFramebufferSize,
this.supersampledFramebufferSize[0],
@ -372,8 +381,8 @@ export class MCAAStrategy extends XCAAStrategy {
return MCAA_PATCH_INDICES;
}
protected get usesDilationTransforms(): boolean {
return true;
protected get transformType(): TransformType {
return 'affine';
}
protected get mightUseAAFramebuffer(): boolean {
@ -434,9 +443,7 @@ export class MCAAStrategy extends XCAAStrategy {
gl.depthFunc(gl.GREATER);
gl.depthMask(false);
gl.enable(gl.DEPTH_TEST);
gl.frontFace(gl.CCW);
gl.cullFace(gl.BACK);
gl.enable(gl.CULL_FACE);
gl.disable(gl.CULL_FACE);
}
protected clearForResolve(renderer: Renderer): void {
@ -485,80 +492,68 @@ export class MCAAStrategy extends XCAAStrategy {
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges);
const bBoxRanges = renderer.meshData[meshIndex].bBoxPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bBoxRanges);
gl.useProgram(shaderProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, this.patchVertexBuffer);
gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions);
gl.vertexAttribPointer(attributes.aUpperLeftEndpointPosition,
2,
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aTessCoord, 2, gl.FLOAT, false, FLOAT32_SIZE * 2, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bBoxes);
gl.vertexAttribPointer(attributes.aRect,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 0);
gl.vertexAttribPointer(attributes.aUpperControlPointPosition,
2,
FLOAT32_SIZE * 20,
FLOAT32_SIZE * 0 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aUV,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 2);
gl.vertexAttribPointer(attributes.aUpperRightEndpointPosition,
2,
FLOAT32_SIZE * 20,
FLOAT32_SIZE * 4 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aDUVDX,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4);
gl.vertexAttribPointer(attributes.aLowerRightEndpointPosition,
2,
FLOAT32_SIZE * 20,
FLOAT32_SIZE * 8 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aDUVDY,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 6);
gl.vertexAttribPointer(attributes.aLowerControlPointPosition,
2,
FLOAT32_SIZE * 20,
FLOAT32_SIZE * 12 + offset * FLOAT32_SIZE * 20);
gl.vertexAttribPointer(attributes.aSignMode,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8);
gl.vertexAttribPointer(attributes.aLowerLeftEndpointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 10);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperLeftEndpointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperRightEndpointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerRightEndpointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerLeftEndpointPosition, 1);
FLOAT32_SIZE * 20,
FLOAT32_SIZE * 16 + offset * FLOAT32_SIZE * 20);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositionPathIDs);
gl.enableVertexAttribArray(attributes.aTessCoord);
gl.enableVertexAttribArray(attributes.aRect);
gl.enableVertexAttribArray(attributes.aUV);
gl.enableVertexAttribArray(attributes.aDUVDX);
gl.enableVertexAttribArray(attributes.aDUVDY);
gl.enableVertexAttribArray(attributes.aSignMode);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRect, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aUV, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aDUVDX, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aDUVDY, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aSignMode, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bBoxPathIDs);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
UINT16_SIZE * 6,
UINT16_SIZE * offset);
UINT16_SIZE,
offset * UINT16_SIZE);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.enableVertexAttribArray(attributes.aTessCoord);
gl.enableVertexAttribArray(attributes.aUpperLeftEndpointPosition);
gl.enableVertexAttribArray(attributes.aUpperControlPointPosition);
gl.enableVertexAttribArray(attributes.aUpperRightEndpointPosition);
gl.enableVertexAttribArray(attributes.aLowerRightEndpointPosition);
gl.enableVertexAttribArray(attributes.aLowerControlPointPosition);
gl.enableVertexAttribArray(attributes.aLowerLeftEndpointPosition);
gl.enableVertexAttribArray(attributes.aPathID);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.patchIndexBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderer.renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
@ -571,7 +566,7 @@ export class MCAAStrategy extends XCAAStrategy {
objectIndex: number,
shaderProgram: PathfinderShaderProgram):
void {
if (renderer.meshData == null)
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
@ -593,11 +588,13 @@ export class MCAAStrategy extends XCAAStrategy {
this.setBlendModeForAA(renderer);
this.setAADepthState(renderer);
const bQuadRanges = renderer.meshData[meshIndex].bQuadVertexPositionPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRanges);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
const bBoxRanges = renderer.meshData[meshIndex].bBoxPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bBoxRanges);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 12, gl.UNSIGNED_BYTE, 0, count);
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
gl.disable(gl.DEPTH_TEST);
@ -641,8 +638,8 @@ export class ECAAStrategy extends XCAAStrategy {
private lineVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
private curveVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
protected get usesDilationTransforms(): boolean {
return false;
protected get transformType(): TransformType {
return '3d';
}
get directRenderingMode(): DirectRenderingMode {

View File

@ -10,7 +10,8 @@
use bincode::{self, Infinite};
use byteorder::{LittleEndian, WriteBytesExt};
use euclid::{Point2D, Vector2D};
use euclid::approxeq::ApproxEq;
use euclid::{Point2D, Rect, Size2D, Vector2D};
use lyon_path::PathEvent;
use lyon_path::iterator::PathIterator;
use pathfinder_path_utils::normals::PathNormals;
@ -32,6 +33,7 @@ pub struct MeshLibrary {
pub b_quad_vertex_interior_indices: Vec<u32>,
pub b_vertex_positions: Vec<Point2D<f32>>,
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
pub b_boxes: Vec<BBox>,
pub segments: MeshLibrarySegments,
pub segment_normals: MeshLibrarySegmentNormals,
}
@ -46,6 +48,7 @@ impl MeshLibrary {
b_quad_vertex_interior_indices: vec![],
b_vertex_positions: vec![],
b_vertex_loop_blinn_data: vec![],
b_boxes: vec![],
segments: MeshLibrarySegments::new(),
segment_normals: MeshLibrarySegmentNormals::new(),
}
@ -58,6 +61,7 @@ impl MeshLibrary {
self.b_quad_vertex_interior_indices.clear();
self.b_vertex_positions.clear();
self.b_vertex_loop_blinn_data.clear();
self.b_boxes.clear();
self.segments.clear();
self.segment_normals.clear();
}
@ -78,45 +82,121 @@ impl MeshLibrary {
}
pub(crate) fn add_b_quad(&mut self, b_quad: &BQuad) {
let BQuadVertexPositions {
upper_left_vertex_position: ul,
upper_right_vertex_position: ur,
lower_left_vertex_position: ll,
lower_right_vertex_position: lr,
..
} = self.get_b_quad_vertex_positions(b_quad);
if ul.x.approx_eq(&ur.x) || ll.x.approx_eq(&lr.x) {
return
}
self.b_quads.push(*b_quad);
let upper_left_position =
self.b_vertex_positions[b_quad.upper_left_vertex_index as usize];
let upper_right_position =
self.b_vertex_positions[b_quad.upper_right_vertex_index as usize];
let lower_left_position =
self.b_vertex_positions[b_quad.lower_left_vertex_index as usize];
let lower_right_position =
self.b_vertex_positions[b_quad.lower_right_vertex_index as usize];
let mut b_quad_vertex_positions = BQuadVertexPositions {
upper_left_vertex_position: upper_left_position,
upper_control_point_position: upper_left_position,
upper_right_vertex_position: upper_right_position,
lower_left_vertex_position: lower_left_position,
lower_control_point_position: lower_right_position,
lower_right_vertex_position: lower_right_position,
};
if b_quad.upper_control_point_vertex_index != u32::MAX {
let upper_control_point_position =
self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize];
b_quad_vertex_positions.upper_control_point_position = upper_control_point_position;
}
if b_quad.lower_control_point_vertex_index != u32::MAX {
let lower_control_point_position =
self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize];
b_quad_vertex_positions.lower_control_point_position = lower_control_point_position;
}
self.add_b_quad_vertex_positions(b_quad);
self.add_b_box(b_quad);
}
fn add_b_quad_vertex_positions(&mut self, b_quad: &BQuad) {
let b_quad_vertex_positions = self.get_b_quad_vertex_positions(b_quad);
let first_b_quad_vertex_position_index = (self.b_quad_vertex_positions.len() as u32) * 6;
self.push_b_quad_vertex_position_interior_indices(first_b_quad_vertex_position_index,
&b_quad_vertex_positions);
self.b_quad_vertex_positions.push(b_quad_vertex_positions);
}
fn add_b_box(&mut self, b_quad: &BQuad) {
let BQuadVertexPositions {
upper_left_vertex_position: ul,
upper_control_point_position: uc,
upper_right_vertex_position: ur,
lower_left_vertex_position: ll,
lower_control_point_position: lc,
lower_right_vertex_position: lr,
} = self.get_b_quad_vertex_positions(b_quad);
let rect = Rect::from_points([ul, uc, ur, ll, lc, lr].into_iter());
let (edge_ucl, edge_urc, edge_ulr) = (uc - ul, ur - uc, ul - ur);
let (edge_lcl, edge_lrc, edge_llr) = (lc - ll, lr - lc, ll - lr);
let (edge_len_ucl, edge_len_urc) = (edge_ucl.length(), edge_urc.length());
let (edge_len_lcl, edge_len_lrc) = (edge_lcl.length(), edge_lrc.length());
let (edge_len_ulr, edge_len_llr) = (edge_ulr.length(), edge_llr.length());
let (uv_upper, uv_lower, sign_upper, sign_lower, mode_upper, mode_lower);
if edge_len_ucl < 0.01 || edge_len_urc < 0.01 || edge_len_ulr < 0.01 ||
edge_ucl.dot(-edge_ulr) > 0.9999 * edge_len_ucl * edge_len_ulr {
uv_upper = Uv::line(&rect, &ul, &ur);
sign_upper = -1.0;
mode_upper = -1.0;
} else {
uv_upper = Uv::curve(&rect, &ul, &uc, &ur);
sign_upper = (edge_ucl.cross(-edge_ulr)).signum();
mode_upper = 1.0;
}
if edge_len_lcl < 0.01 || edge_len_lrc < 0.01 || edge_len_llr < 0.01 ||
edge_lcl.dot(-edge_llr) > 0.9999 * edge_len_lcl * edge_len_llr {
uv_lower = Uv::line(&rect, &ll, &lr);
sign_lower = 1.0;
mode_lower = -1.0;
} else {
uv_lower = Uv::curve(&rect, &ll, &lc, &lr);
sign_lower = -(edge_lcl.cross(-edge_llr)).signum();
mode_lower = 1.0;
}
let b_box = BBox {
upper_left_position: rect.origin,
lower_right_position: rect.bottom_right(),
upper_left_uv_upper: uv_upper.origin,
upper_left_uv_lower: uv_lower.origin,
d_upper_uv_dx: uv_upper.d_uv_dx,
d_lower_uv_dx: uv_lower.d_uv_dx,
d_upper_uv_dy: uv_upper.d_uv_dy,
d_lower_uv_dy: uv_lower.d_uv_dy,
upper_sign: sign_upper,
lower_sign: sign_lower,
upper_mode: mode_upper,
lower_mode: mode_lower,
};
self.b_boxes.push(b_box);
}
fn get_b_quad_vertex_positions(&self, b_quad: &BQuad) -> BQuadVertexPositions {
let ul = self.b_vertex_positions[b_quad.upper_left_vertex_index as usize];
let ur = self.b_vertex_positions[b_quad.upper_right_vertex_index as usize];
let ll = self.b_vertex_positions[b_quad.lower_left_vertex_index as usize];
let lr = self.b_vertex_positions[b_quad.lower_right_vertex_index as usize];
let mut b_quad_vertex_positions = BQuadVertexPositions {
upper_left_vertex_position: ul,
upper_control_point_position: ul,
upper_right_vertex_position: ur,
lower_left_vertex_position: ll,
lower_control_point_position: lr,
lower_right_vertex_position: lr,
};
if b_quad.upper_control_point_vertex_index != u32::MAX {
let uc = &self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize];
b_quad_vertex_positions.upper_control_point_position = *uc;
}
if b_quad.lower_control_point_vertex_index != u32::MAX {
let lc = &self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize];
b_quad_vertex_positions.lower_control_point_position = *lc;
}
b_quad_vertex_positions
}
fn push_b_quad_vertex_position_interior_indices(&mut self,
first_vertex_index: u32,
b_quad: &BQuadVertexPositions) {
@ -282,6 +362,7 @@ impl MeshLibrary {
try!(write_simple_chunk(writer, b"bqua", &self.b_quads));
try!(write_simple_chunk(writer, b"bqvp", &self.b_quad_vertex_positions));
try!(write_simple_chunk(writer, b"bqii", &self.b_quad_vertex_interior_indices));
try!(write_simple_chunk(writer, b"bbox", &self.b_boxes));
try!(write_simple_chunk(writer, b"slin", &self.segments.lines));
try!(write_simple_chunk(writer, b"scur", &self.segments.curves));
try!(write_simple_chunk(writer, b"snli", &self.segment_normals.line_normals));
@ -331,6 +412,7 @@ impl MeshLibrary {
path_ranges,
|ranges| &ranges.b_quad_vertex_interior_indices));
try!(write_path_range(writer, b"bver", path_ranges, |ranges| &ranges.b_vertices));
try!(write_path_range(writer, b"bbox", path_ranges, |ranges| &ranges.b_boxes));
try!(write_path_range(writer, b"slin", path_ranges, |ranges| &ranges.segment_lines));
try!(write_path_range(writer, b"scur", path_ranges, |ranges| &ranges.segment_curves));
Ok(())
@ -359,6 +441,7 @@ impl MeshLibrary {
b_quad_vertex_positions: self.b_quad_vertex_positions.len() as u32,
b_quad_vertex_interior_indices: self.b_quad_vertex_interior_indices.len() as u32,
b_vertices: self.b_vertex_positions.len() as u32,
b_boxes: self.b_boxes.len() as u32,
}
}
}
@ -374,6 +457,7 @@ pub(crate) struct MeshLibraryLengths {
b_quad_vertex_positions: u32,
b_quad_vertex_interior_indices: u32,
b_vertices: u32,
b_boxes: u32,
}
#[derive(Clone, Debug)]
@ -382,6 +466,7 @@ pub struct PathRanges {
pub b_quad_vertex_positions: Range<u32>,
pub b_quad_vertex_interior_indices: Range<u32>,
pub b_vertices: Range<u32>,
pub b_boxes: Range<u32>,
pub segment_lines: Range<u32>,
pub segment_curves: Range<u32>,
}
@ -393,6 +478,7 @@ impl PathRanges {
b_quad_vertex_positions: 0..0,
b_quad_vertex_interior_indices: 0..0,
b_vertices: 0..0,
b_boxes: 0..0,
segment_lines: 0..0,
segment_curves: 0..0,
}
@ -406,6 +492,7 @@ impl PathRanges {
self.b_quad_vertex_interior_indices =
start.b_quad_vertex_interior_indices..end.b_quad_vertex_interior_indices;
self.b_vertices = start.b_vertices..end.b_vertices;
self.b_boxes = start.b_boxes..end.b_boxes;
}
}
@ -474,3 +561,134 @@ pub struct CurveSegmentNormals {
pub control_point: f32,
pub endpoint_1: f32,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct BBox {
pub upper_left_position: Point2D<f32>,
pub lower_right_position: Point2D<f32>,
pub upper_left_uv_upper: Point2D<f32>,
pub upper_left_uv_lower: Point2D<f32>,
pub d_upper_uv_dx: Vector2D<f32>,
pub d_lower_uv_dx: Vector2D<f32>,
pub d_upper_uv_dy: Vector2D<f32>,
pub d_lower_uv_dy: Vector2D<f32>,
pub upper_sign: f32,
pub lower_sign: f32,
pub upper_mode: f32,
pub lower_mode: f32,
}
#[derive(Clone, Copy, Debug)]
struct CornerPositions {
upper_left: Point2D<f32>,
upper_right: Point2D<f32>,
lower_left: Point2D<f32>,
lower_right: Point2D<f32>,
}
#[derive(Clone, Copy, Debug)]
struct CornerValues {
upper_left: Point2D<f32>,
upper_right: Point2D<f32>,
lower_left: Point2D<f32>,
lower_right: Point2D<f32>,
}
#[derive(Clone, Copy, Debug)]
struct Uv {
origin: Point2D<f32>,
d_uv_dx: Vector2D<f32>,
d_uv_dy: Vector2D<f32>,
}
impl Uv {
fn from_values(origin: &Point2D<f32>, origin_right: &Point2D<f32>, origin_down: &Point2D<f32>)
-> Uv {
Uv {
origin: *origin,
d_uv_dx: *origin_right - *origin,
d_uv_dy: *origin_down - *origin,
}
}
fn curve(rect: &Rect<f32>, left: &Point2D<f32>, ctrl: &Point2D<f32>, right: &Point2D<f32>)
-> Uv {
let origin_right = rect.top_right();
let origin_down = rect.bottom_left();
let (lambda_origin, denom) = to_barycentric(left, ctrl, right, &rect.origin);
let (lambda_origin_right, _) = to_barycentric(left, ctrl, right, &origin_right);
let (lambda_origin_down, _) = to_barycentric(left, ctrl, right, &origin_down);
let uv_origin = lambda_to_uv(&lambda_origin, denom);
let uv_origin_right = lambda_to_uv(&lambda_origin_right, denom);
let uv_origin_down = lambda_to_uv(&lambda_origin_down, denom);
return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down);
// https://gamedev.stackexchange.com/a/23745
fn to_barycentric(a: &Point2D<f32>, b: &Point2D<f32>, c: &Point2D<f32>, p: &Point2D<f32>)
-> ([f64; 2], f64) {
let (a, b, c, p) = (a.to_f64(), b.to_f64(), c.to_f64(), p.to_f64());
let (v0, v1, v2) = (b - a, c - a, p - a);
let (d00, d01) = (v0.dot(v0), v0.dot(v1));
let d11 = v1.dot(v1);
let (d20, d21) = (v2.dot(v0), v2.dot(v1));
let denom = d00 * d11 - d01 * d01;
([(d11 * d20 - d01 * d21), (d00 * d21 - d01 * d20)], denom)
}
fn lambda_to_uv(lambda: &[f64; 2], denom: f64) -> Point2D<f32> {
(Point2D::new(lambda[0] * 0.5 + lambda[1], lambda[1]) / denom).to_f32()
}
}
fn line(rect: &Rect<f32>, left: &Point2D<f32>, right: &Point2D<f32>) -> Uv {
let (values, line_bounds);
if f32::abs(left.y - right.y) < 0.01 {
values = CornerValues {
upper_left: Point2D::new(0.0, 0.5),
upper_right: Point2D::new(0.5, 1.0),
lower_right: Point2D::new(1.0, 0.5),
lower_left: Point2D::new(0.5, 0.0),
};
line_bounds = Rect::new(*left + Vector2D::new(0.0, -1.0),
Size2D::new(right.x - left.x, 2.0));
} else {
if left.y < right.y {
values = CornerValues {
upper_left: Point2D::new(1.0, 1.0),
upper_right: Point2D::new(0.0, 1.0),
lower_left: Point2D::new(1.0, 0.0),
lower_right: Point2D::new(0.0, 0.0),
};
} else {
values = CornerValues {
upper_left: Point2D::new(0.0, 1.0),
upper_right: Point2D::new(1.0, 1.0),
lower_left: Point2D::new(0.0, 0.0),
lower_right: Point2D::new(1.0, 0.0),
};
}
line_bounds = Rect::from_points([*left, *right].into_iter());
}
let origin_right = rect.top_right();
let origin_down = rect.bottom_left();
let uv_origin = bilerp(&line_bounds, &values, &rect.origin);
let uv_origin_right = bilerp(&line_bounds, &values, &origin_right);
let uv_origin_down = bilerp(&line_bounds, &values, &origin_down);
return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down);
fn bilerp(rect: &Rect<f32>, values: &CornerValues, position: &Point2D<f32>)
-> Point2D<f32> {
let upper = values.upper_left.lerp(values.upper_right,
(position.x - rect.min_x()) / rect.size.width);
let lower = values.lower_left.lerp(values.lower_right,
(position.x - rect.min_x()) / rect.size.width);
upper.lerp(lower, (position.y - rect.min_y()) / rect.size.height)
}
}
}

View File

@ -11,6 +11,7 @@
#version 100
#extension GL_EXT_frag_depth : require
#extension GL_OES_standard_derivatives : require
#define LCD_FILTER_FACTOR_0 (86.0 / 255.0)
#define LCD_FILTER_FACTOR_1 (77.0 / 255.0)
@ -104,65 +105,11 @@ float convertPathIndexToViewportDepthValue(int pathIndex) {
return float(pathIndex) / float(MAX_PATHS) * 2.0 - 1.0;
}
/// Packs the given path ID into a floating point value suitable for storage in the depth buffer.
///
/// This function returns values in window space (i.e. what `gl_FragDepth`/`gl_FragDepthEXT` is
/// in).
float convertPathIndexToWindowDepthValue(int pathIndex) {
return float(pathIndex) / float(MAX_PATHS);
}
/// Displaces the given point by the given distance in the direction of the normal angle.
vec2 dilatePosition(vec2 position, float normalAngle, vec2 amount) {
return position + vec2(cos(normalAngle), -sin(normalAngle)) * amount;
}
vec2 offsetPositionVertically(vec2 position, ivec2 framebufferSize, bool roundUp) {
position = convertClipToScreenSpace(position, framebufferSize);
position.y = roundUp ? ceil(position.y + 1.0) : floor(position.y - 1.0);
return convertScreenToClipSpace(position, framebufferSize);
}
vec2 computeMCAAPosition(vec2 position,
vec4 hints,
vec4 localTransformST,
vec4 globalTransformST,
ivec2 framebufferSize) {
if (position == vec2(0.0))
return position;
position = hintPosition(position, hints);
position = transformVertexPositionST(position, localTransformST);
position = transformVertexPositionST(position, globalTransformST);
return convertClipToScreenSpace(position, framebufferSize);
}
vec2 computeMCAASnappedPosition(vec2 position,
vec4 hints,
vec4 localTransformST,
vec4 globalTransformST,
ivec2 framebufferSize,
float slope,
bool snapToPixelGrid) {
position = hintPosition(position, hints);
position = transformVertexPositionST(position, localTransformST);
position = transformVertexPositionST(position, globalTransformST);
position = convertClipToScreenSpace(position, framebufferSize);
float xNudge;
if (snapToPixelGrid) {
xNudge = fract(position.x);
if (xNudge < 0.5)
xNudge = -xNudge;
else
xNudge = 1.0 - xNudge;
} else {
xNudge = 0.0;
}
return position + vec2(xNudge, xNudge * slope);
}
vec2 transformECAAPosition(vec2 position,
vec4 localTransformST,
vec2 localTransformExt,
@ -409,21 +356,6 @@ float computeCoverage(vec2 p0X, vec2 dPX, float pixelCenterY, float winding) {
return abs(area) * winding * 0.5;
}
/// Returns true if the line runs through this pixel or false otherwise.
///
/// The line must run left-to-right and must already be clipped to the left and right sides of the
/// pixel, which implies that `dP.x` must be within the range [0.0, 1.0].
///
/// * `p0X` is the start point of the line.
/// * `dPX` is the vector from the start point to the endpoint of the line.
/// * `pixelCenterY` is the Y coordinate of the center of the pixel in window coordinates (i.e.
/// `gl_FragCoord.y`).
bool isPartiallyCovered(vec2 p0X, vec2 dPX, float pixelCenterY) {
float pixelTop;
vec2 dP = clipLineToPixelRow(p0X, dPX, pixelCenterY, pixelTop).zw;
return !isNearZero(dP.x) || !isNearZero(dP.y);
}
/// Solves the equation:
///
/// x = p0x + t^2 * (p0x - 2*p1x + p2x) + t*(2*p1x - 2*p0x)
@ -482,3 +414,11 @@ vec4 fetchPathAffineTransform(out vec2 outPathTransformExt,
pathTransformExtDimensions);
return fetchFloat4Data(pathTransformSTTexture, pathID, pathTransformSTDimensions);
}
float detMat2(mat2 m) {
return m[0][0] * m[1][1] - m[0][1] * m[1][0];
}
mat2 invMat2(mat2 m) {
return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) / detMat2(m);
}

View File

@ -1,6 +1,6 @@
// pathfinder/shaders/gles2/direct-interior.vs.glsl
// pathfinder/shaders/gles2/conservative-interior.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
// Copyright (c) 2018 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
@ -17,8 +17,9 @@
precision highp float;
/// A 3D transform to be applied to all points.
uniform mat4 uTransform;
/// An affine transform to be applied to all points.
uniform vec4 uTransformST;
uniform vec2 uTransformExt;
/// Vertical snapping positions.
uniform vec4 uHints;
/// The framebuffer size in pixels.
@ -52,21 +53,21 @@ void main() {
int pathID = int(aPathID);
int vertexID = int(aVertexID);
vec2 pathTransformExt;
vec4 pathTransformST = fetchPathAffineTransform(pathTransformExt,
uPathTransformST,
uPathTransformSTDimensions,
uPathTransformExt,
uPathTransformExtDimensions,
pathID);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
vec2 position = hintPosition(aPosition, uHints);
position = transformVertexPositionAffine(position, pathTransformST, pathTransformExt);
position = transformVertexPosition(position, uTransform);
position = offsetPositionVertically(position, uFramebufferSize, imod(vertexID, 6) < 3);
mat2 globalTransformLinear = mat2(uTransformST.x, uTransformExt, uTransformST.y);
mat2 localTransformLinear = mat2(transformST.x, 0.0, 0.0, transformST.y);
mat2 transformLinear = globalTransformLinear * localTransformLinear;
vec2 translation = uTransformST.zw + globalTransformLinear * transformST.zw;
float onePixel = 2.0 / float(uFramebufferSize.y);
float dilation = length(invMat2(transformLinear) * vec2(0.0, onePixel));
vec2 position = aPosition + vec2(0.0, imod(vertexID, 6) < 3 ? dilation : -dilation);
position = transformLinear * position + translation;
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
gl_Position = vec4(position, depth, 1.0);
vColor = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
}

View File

@ -1,6 +1,6 @@
// pathfinder/shaders/gles2/mcaa.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
// Copyright (c) 2018 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
@ -12,68 +12,43 @@
//! (MCAA). This one shader handles both lines and curves.
//!
//! This shader expects to render to a standard RGB color buffer.
//!
//! Use this shader only when *both* of the following are true:
//!
//! 1. You are rendering multiple multicolor paths. (Otherwise, consider the
//! other MCAA shaders, which render with higher quality.)
//!
//! 2. Your transform is only a scale and/or translation, not a perspective,
//! rotation, or skew. (Otherwise, consider repartitioning the path to
//! generate a new mesh, or, alternatively, use the direct Loop-Blinn
//! shaders.)
precision highp float;
/// True if multiple colors are being rendered; false otherwise.
///
/// If this is true, then points will be snapped to the nearest pixel.
uniform bool uMulticolor;
varying vec4 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor;
varying vec4 vUV;
varying vec4 vSignMode;
float computeCoverageForSide(vec2 p0, vec2 cp, vec2 p1, float winding) {
// Compute pixel extents.
vec2 pixelCenter = gl_FragCoord.xy;
vec2 pixelColumnBounds = pixelCenter.xx + vec2(-0.5, 0.5);
// Cubic approximation to the square area coverage, accurate to about 4%.
float estimateArea(float dist) {
if (dist >= 0.707107)
return 1.0;
// Catch NaNs here.
if (!(dist > -0.707107))
return 0.0;
return 0.5 + 1.14191 * dist - 0.83570 * dist * dist * dist;
}
vec2 clippedP0, clippedDP;
if (cp == vec2(0.0)) {
vec4 p0DPX = clipLineToPixelColumn(p0, p1 - p0, pixelCenter.x);
clippedP0 = p0DPX.xy;
clippedDP = p0DPX.zw;
} else {
// Clip the curve to the left and right edges to create a line.
vec2 t = solveCurveT(p0.x, cp.x, p1.x, pixelColumnBounds);
float computeAlpha(vec2 uv, float curveSign, float mode) {
vec2 dUVDX = dFdx(uv), dUVDY = dFdy(uv);
// Handle endpoints properly. These tests are negated to handle NaNs.
if (!(p0.x < pixelColumnBounds.x))
t.x = 0.0;
if (!(p1.x > pixelColumnBounds.y))
t.y = 1.0;
clippedP0 = mix(mix(p0, cp, t.x), mix(cp, p1, t.x), t.x);
clippedDP = mix(mix(p0, cp, t.y), mix(cp, p1, t.y), t.y) - clippedP0;
// u^2 - v for curves inside uv square; u - v otherwise.
float g = uv.x;
vec2 dG = vec2(dUVDX.x, dUVDY.x);
if (mode > 0.0 && uv.x > 0.0 && uv.x < 1.0 && uv.y > 0.0 && uv.y < 1.0) {
g *= uv.x;
dG *= 2.0 * uv.x;
}
g -= uv.y;
dG -= vec2(dUVDX.y, dUVDY.y);
return computeCoverage(clippedP0, clippedDP, pixelCenter.y, winding);
float signedDistance = g / length(dG);
return estimateArea(signedDistance * curveSign);
}
void main() {
float alpha = computeCoverageForSide(vLowerEndpoints.xy,
vControlPoints.zw,
vLowerEndpoints.zw,
-1.0);
alpha += computeCoverageForSide(vUpperEndpoints.xy,
vControlPoints.xy,
vUpperEndpoints.zw,
1.0);
// Compute area.
vec4 color = uMulticolor ? vColor : vec4(1.0);
gl_FragColor = alpha * color;
float alpha = 1.0;
alpha -= computeAlpha(vUV.xy, vSignMode.x, vSignMode.z);
alpha -= computeAlpha(vUV.zw, vSignMode.y, vSignMode.w);
gl_FragColor = alpha * vColor;
}

View File

@ -34,10 +34,9 @@
precision highp float;
/// A dilation (scale and translation) to be applied to the object.
/// A scale and transform to be applied to the object.
uniform vec4 uTransformST;
/// Vertical snapping positions.
uniform vec4 uHints;
uniform vec2 uTransformExt;
/// The framebuffer size in pixels.
uniform ivec2 uFramebufferSize;
/// The size of the path transform buffer texture in texels.
@ -54,120 +53,47 @@ uniform sampler2D uPathColors;
uniform bool uMulticolor;
attribute vec2 aTessCoord;
attribute vec2 aUpperLeftEndpointPosition;
attribute vec2 aUpperControlPointPosition;
attribute vec2 aUpperRightEndpointPosition;
attribute vec2 aLowerRightEndpointPosition;
attribute vec2 aLowerControlPointPosition;
attribute vec2 aLowerLeftEndpointPosition;
attribute vec4 aRect;
attribute vec4 aUV;
attribute vec4 aDUVDX;
attribute vec4 aDUVDY;
attribute vec4 aSignMode;
attribute float aPathID;
varying vec4 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor;
varying vec4 vUV;
varying vec4 vSignMode;
void main() {
vec2 tlPosition = aUpperLeftEndpointPosition;
vec2 tcPosition = aUpperControlPointPosition;
vec2 trPosition = aUpperRightEndpointPosition;
vec2 blPosition = aLowerLeftEndpointPosition;
vec2 bcPosition = aLowerControlPointPosition;
vec2 brPosition = aLowerRightEndpointPosition;
vec2 tessCoord = aTessCoord;
int pathID = int(floor(aPathID));
vec4 color;
if (uMulticolor)
color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
else
color = vec4(1.0);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
if (abs(transformST.x) > 0.001 && abs(transformST.y) > 0.001) {
vec4 color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vec2 topVector = trPosition - tlPosition, bottomVector = brPosition - blPosition;
mat2 globalTransformLinear = mat2(uTransformST.x, uTransformExt, uTransformST.y);
mat2 localTransformLinear = mat2(transformST.x, 0.0, 0.0, transformST.y);
mat2 rectTransformLinear = mat2(aRect.z - aRect.x, 0.0, 0.0, aRect.w - aRect.y);
mat2 transformLinear = globalTransformLinear * localTransformLinear * rectTransformLinear;
float topSlope = topVector.y / topVector.x;
float bottomSlope = bottomVector.y / bottomVector.x;
if (abs(topSlope) > MAX_SLOPE)
topSlope = sign(topSlope) * MAX_SLOPE;
if (abs(bottomSlope) > MAX_SLOPE)
bottomSlope = sign(bottomSlope) * MAX_SLOPE;
vec2 translation = transformST.zw + localTransformLinear * aRect.xy;
translation = uTransformST.zw + globalTransformLinear * translation;
// Transform the points, and compute the position of this vertex.
tlPosition = computeMCAASnappedPosition(tlPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topSlope,
uMulticolor);
trPosition = computeMCAASnappedPosition(trPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topSlope,
uMulticolor);
tcPosition = computeMCAAPosition(tcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
blPosition = computeMCAASnappedPosition(blPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope,
uMulticolor);
brPosition = computeMCAASnappedPosition(brPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope,
uMulticolor);
bcPosition = computeMCAAPosition(bcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
float onePixel = 2.0 / float(uFramebufferSize.y);
float dilation = length(invMat2(transformLinear) * vec2(0.0, onePixel));
tessCoord.y += tessCoord.y < 0.5 ? -dilation : dilation;
float depth = convertPathIndexToViewportDepthValue(pathID);
vec2 position = transformLinear * tessCoord + translation;
vec4 uv = aUV + tessCoord.x * aDUVDX + tessCoord.y * aDUVDY;
float depth = convertPathIndexToViewportDepthValue(pathID);
// Use the same side--in this case, the top--or else floating point error during
// partitioning can occasionally cause inconsistent rounding, resulting in cracks.
vec2 position;
if (tessCoord.y < 0.5) {
if (tessCoord.x < 0.25)
position = tlPosition;
else if (tessCoord.x < 0.75)
position = tcPosition;
else
position = trPosition;
position.y = floor(position.y - 0.5);
} else {
if (tessCoord.x < 0.25)
position = blPosition;
else if (tessCoord.x < 0.75)
position = bcPosition;
else
position = brPosition;
position.y = ceil(position.y + 0.5);
}
if (!uMulticolor) {
if (tessCoord.x < 0.25)
position.x = floor(position.x);
else if (tessCoord.x >= 0.75)
position.x = ceil(position.x);
}
position = convertScreenToClipSpace(position, uFramebufferSize);
gl_Position = vec4(position, depth, 1.0);
vUpperEndpoints = vec4(tlPosition, trPosition);
vLowerEndpoints = vec4(blPosition, brPosition);
vControlPoints = vec4(tcPosition, bcPosition);
vColor = color;
} else {
gl_Position = vec4(0.0);
}
gl_Position = vec4(position, depth, 1.0);
vColor = color;
vUV = uv;
vSignMode = aSignMode;
}