Merge MCAA monochrome and multicolor shaders.

Not only is this a lot simpler, it's faster too!
This commit is contained in:
Patrick Walton 2018-01-02 19:15:19 -08:00
parent 017a2e2f8c
commit 11913a20f1
25 changed files with 418 additions and 1034 deletions

View File

@ -364,6 +364,10 @@ class ThreeDRenderer extends Renderer {
camera: PerspectiveCamera;
get isMulticolor(): boolean {
return false;
}
get usesSTTransform(): boolean {
return this.camera.usesSTTransform;
}
@ -702,7 +706,7 @@ class ThreeDRenderer extends Renderer {
gl.disable(gl.SCISSOR_TEST);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
// Draw textures for distant glyphs.
const cameraTransform = this.calculateCameraTransform(glmatrix.vec3.create(), TEXT_SCALE);

View File

@ -423,6 +423,10 @@ class BenchmarkTextRenderer extends Renderer {
camera: OrthographicCamera;
get isMulticolor(): boolean {
return false;
}
get usesSTTransform(): boolean {
return this.camera.usesSTTransform;
}

View File

@ -668,6 +668,10 @@ class ReferenceTestTextRenderer extends Renderer {
return null;
}
get isMulticolor(): boolean {
return false;
}
get bgColor(): glmatrix.vec4 {
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
}

View File

@ -67,6 +67,8 @@ export abstract class Renderer {
return this.meshes != null && this.meshData != null;
}
abstract get isMulticolor(): boolean;
abstract get destFramebuffer(): WebGLFramebuffer | null;
abstract get destAllocatedSize(): glmatrix.vec2;
abstract get destUsedSize(): glmatrix.vec2;
@ -131,14 +133,14 @@ export abstract class Renderer {
if (this.meshes == null)
return;
this.clearDestFramebuffer();
// Start timing rendering.
if (this.timerQueryPollInterval == null) {
renderContext.timerQueryExt.beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT,
renderContext.atlasRenderingTimerQuery);
}
this.clearDestFramebuffer();
const antialiasingStrategy = unwrapNull(this.antialiasingStrategy);
antialiasingStrategy.prepareForRendering(this);
@ -155,8 +157,16 @@ export abstract class Renderer {
// Antialias.
antialiasingStrategy.antialiasObject(this, objectIndex);
// Perform post-antialiasing tasks.
antialiasingStrategy.finishAntialiasingObject(this, objectIndex);
// End the timer, and start a new one.
// FIXME(pcwalton): This is kinda bogus for multipass.
if (this.timerQueryPollInterval == null && objectIndex === objectCount - 1 &&
pass === passCount - 1) {
renderContext.timerQueryExt
.endQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT);
renderContext.timerQueryExt
.beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT,
renderContext.compositingTimerQuery);
}
// Perform direct rendering (Loop-Blinn).
if (antialiasingStrategy.directRenderingMode !== 'none') {
@ -169,20 +179,15 @@ export abstract class Renderer {
this.directlyRenderObject(pass, objectIndex);
}
// Perform post-antialiasing tasks.
antialiasingStrategy.finishAntialiasingObject(this, objectIndex);
antialiasingStrategy.resolveAAForObject(this, objectIndex);
}
antialiasingStrategy.resolve(pass, this);
}
// End the timer, and start a new one.
// FIXME(pcwalton): Removed this for multipass; get it split out again somehow.
if (this.timerQueryPollInterval == null) {
renderContext.timerQueryExt.endQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT);
renderContext.timerQueryExt.beginQueryEXT(renderContext.timerQueryExt.TIME_ELAPSED_EXT,
renderContext.compositingTimerQuery);
}
// Draw the glyphs with the resolved atlas to the default framebuffer.
this.compositeIfNecessary();
@ -306,22 +311,24 @@ export abstract class Renderer {
uploadPathTransforms(objectCount: number): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
const pathTransforms = this.pathTransformsForObject(objectIndex);
let pathTransformBufferTextures;
if (objectIndex >= this.pathTransformBufferTextures.length) {
pathTransformBufferTextures = {
ext: new PathfinderBufferTexture(renderContext.gl, 'uPathTransformExt'),
st: new PathfinderBufferTexture(renderContext.gl, 'uPathTransformST'),
ext: new PathfinderBufferTexture(gl, 'uPathTransformExt'),
st: new PathfinderBufferTexture(gl, 'uPathTransformST'),
};
this.pathTransformBufferTextures[objectIndex] = pathTransformBufferTextures;
} else {
pathTransformBufferTextures = this.pathTransformBufferTextures[objectIndex];
}
pathTransformBufferTextures.st.upload(renderContext.gl, pathTransforms.st);
pathTransformBufferTextures.ext.upload(renderContext.gl, pathTransforms.ext);
pathTransformBufferTextures.st.upload(gl, pathTransforms.st);
pathTransformBufferTextures.ext.upload(gl, pathTransforms.ext);
}
}
@ -501,7 +508,7 @@ export abstract class Renderer {
gl.depthMask(false);
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
// Set up the direct curve VAO.
//

View File

@ -23,10 +23,7 @@ export interface ShaderMap<T> {
ecaaLine: T;
ecaaCurve: T;
ecaaTransformedCurve: T;
mcaaCover: T;
mcaaLine: T;
mcaaCurve: T;
mcaaMulti: T;
mcaa: T;
ssaaSubpixelResolve: T;
xcaaMonoResolve: T;
xcaaMonoSubpixelResolve: T;
@ -47,10 +44,7 @@ export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'direct3DCurve',
'direct3DInterior',
'ssaaSubpixelResolve',
'mcaaCover',
'mcaaLine',
'mcaaCurve',
'mcaaMulti',
'mcaa',
'ecaaLine',
'ecaaCurve',
'ecaaTransformedCurve',
@ -94,32 +88,20 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
vertex: "/glsl/gles2/direct-interior.vs.glsl",
},
ecaaCurve: {
fragment: "/glsl/gles2/xcaa-curve.fs.glsl",
fragment: "/glsl/gles2/ecaa-curve.fs.glsl",
vertex: "/glsl/gles2/ecaa-curve.vs.glsl",
},
ecaaLine: {
fragment: "/glsl/gles2/xcaa-line.fs.glsl",
fragment: "/glsl/gles2/ecaa-line.fs.glsl",
vertex: "/glsl/gles2/ecaa-line.vs.glsl",
},
ecaaTransformedCurve: {
fragment: "/glsl/gles2/xcaa-curve.fs.glsl",
fragment: "/glsl/gles2/ecaa-curve.fs.glsl",
vertex: "/glsl/gles2/ecaa-transformed-curve.vs.glsl",
},
mcaaCover: {
fragment: "/glsl/gles2/mcaa-cover.fs.glsl",
vertex: "/glsl/gles2/mcaa-cover.vs.glsl",
},
mcaaCurve: {
fragment: "/glsl/gles2/xcaa-curve.fs.glsl",
vertex: "/glsl/gles2/mcaa-curve.vs.glsl",
},
mcaaLine: {
fragment: "/glsl/gles2/xcaa-line.fs.glsl",
vertex: "/glsl/gles2/mcaa-line.vs.glsl",
},
mcaaMulti: {
fragment: "/glsl/gles2/mcaa-multi.fs.glsl",
vertex: "/glsl/gles2/mcaa-multi.vs.glsl",
mcaa: {
fragment: "/glsl/gles2/mcaa.fs.glsl",
vertex: "/glsl/gles2/mcaa.vs.glsl",
},
ssaaSubpixelResolve: {
fragment: "/glsl/gles2/ssaa-subpixel-resolve.fs.glsl",

View File

@ -21,18 +21,18 @@ import SSAAStrategy from './ssaa-strategy';
import {SVGLoader} from './svg-loader';
import {Range} from './utils';
import {RenderContext} from './view';
import {MCAAMulticolorStrategy, XCAAStrategy} from './xcaa-strategy';
import {MCAAStrategy, XCAAStrategy} from './xcaa-strategy';
interface AntialiasingStrategyTable {
none: typeof NoAAStrategy;
ssaa: typeof SSAAStrategy;
xcaa: typeof XCAAStrategy;
xcaa: typeof MCAAStrategy;
}
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
none: NoAAStrategy,
ssaa: SSAAStrategy,
xcaa: MCAAMulticolorStrategy,
xcaa: MCAAStrategy,
};
export abstract class SVGRenderer extends Renderer {
@ -40,6 +40,11 @@ export abstract class SVGRenderer extends Renderer {
camera: OrthographicCamera;
get isMulticolor(): boolean {
// FIXME(pcwalton): Only if the SVG is actually multicolor!
return true;
}
get bgColor(): glmatrix.vec4 {
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
}

View File

@ -64,6 +64,10 @@ export abstract class TextRenderer extends Renderer {
atlasFramebuffer: WebGLFramebuffer;
atlasDepthTexture: WebGLTexture;
get isMulticolor(): boolean {
return false;
}
get usesSTTransform(): boolean {
return this.camera.usesSTTransform;
}
@ -146,8 +150,11 @@ export abstract class TextRenderer extends Renderer {
}
setHintsUniform(uniforms: UniformMap): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const hint = this.createHint();
this.renderContext.gl.uniform4f(uniforms.uHints,
gl.uniform4f(uniforms.uHints,
hint.xHeight,
hint.hintedXHeight,
hint.stemHeight,

View File

@ -41,9 +41,8 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
}
protected abstract get usesDilationTransforms(): boolean;
protected abstract get usesAAFramebuffer(): boolean;
protected pathBoundsBufferTexture: PathfinderBufferTexture;
protected pathBoundsBufferTextures: PathfinderBufferTexture[];
protected supersampledFramebufferSize: glmatrix.vec2;
protected destFramebufferSize: glmatrix.vec2;
@ -72,6 +71,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
attachMeshes(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.createResolveVAO(renderer);
this.pathBoundsBufferTextures = [];
}
setFramebufferSize(renderer: Renderer): void {
@ -97,7 +97,9 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.initResolveFramebufferForObject(renderer, objectIndex);
if (this.usesAAFramebuffer) {
if (!this.usesAAFramebuffer(renderer))
return;
const usedSize = this.supersampledUsedSize(renderer);
gl.scissor(0, 0, usedSize[0], usedSize[1]);
gl.enable(gl.SCISSOR_TEST);
@ -108,7 +110,6 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
gl.depthMask(true);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
}
prepareToRenderObject(renderer: Renderer, objectIndex: number): void {}
@ -118,7 +119,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
antialiasObject(renderer: Renderer, objectIndex: number): void {
// Perform early preparations.
this.createPathBoundsBufferTextureForObject(renderer, objectIndex);
this.createPathBoundsBufferTextureForObjectIfNecessary(renderer, objectIndex);
// Set up antialiasing.
this.prepareAA(renderer);
@ -131,7 +132,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const resolveProgram = this.getResolveProgram(renderContext);
const resolveProgram = this.getResolveProgram(renderer);
if (resolveProgram == null)
return;
@ -172,6 +173,8 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
return glmatrix.mat4.create();
}
protected abstract usesAAFramebuffer(renderer: Renderer): boolean;
protected supersampledUsedSize(renderer: Renderer): glmatrix.vec2 {
const usedSize = glmatrix.vec2.create();
glmatrix.vec2.mul(usedSize, renderer.destUsedSize, this.supersampleScale);
@ -223,7 +226,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.supersampledFramebufferSize[1]);
renderer.pathTransformBufferTextures[0].ext.bind(gl, uniforms, 0);
renderer.pathTransformBufferTextures[0].st.bind(gl, uniforms, 1);
this.pathBoundsBufferTexture.bind(gl, uniforms, 2);
this.pathBoundsBufferTextures[objectIndex].bind(gl, uniforms, 2);
renderer.setHintsUniform(uniforms);
}
@ -239,8 +242,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
void {}
protected abstract clearForAA(renderer: Renderer): void;
protected abstract getResolveProgram(renderContext: RenderContext):
PathfinderShaderProgram | null;
protected abstract getResolveProgram(renderer: Renderer): PathfinderShaderProgram | null;
protected abstract setAADepthState(renderer: Renderer): void;
protected abstract clearForResolve(renderer: Renderer): void;
@ -254,7 +256,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
}
private initAAAlphaFramebuffer(renderer: Renderer): void {
if (!this.usesAAFramebuffer) {
if (!this.usesAAFramebuffer(renderer)) {
this.aaAlphaTexture = null;
this.aaDepthTexture = null;
this.aaFramebuffer = null;
@ -282,20 +284,27 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.aaFramebuffer = createFramebuffer(gl, this.aaAlphaTexture, this.aaDepthTexture);
}
private createPathBoundsBufferTextureForObject(renderer: Renderer, objectIndex: number): void {
private createPathBoundsBufferTextureForObjectIfNecessary(renderer: Renderer,
objectIndex: number):
void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathBounds = renderer.pathBoundingRects(objectIndex);
this.pathBoundsBufferTexture = new PathfinderBufferTexture(gl, 'uPathBounds');
this.pathBoundsBufferTexture.upload(gl, pathBounds);
if (this.pathBoundsBufferTextures[objectIndex] == null) {
this.pathBoundsBufferTextures[objectIndex] =
new PathfinderBufferTexture(gl, 'uPathBounds');
}
this.pathBoundsBufferTextures[objectIndex].upload(gl, pathBounds);
}
private createResolveVAO(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const resolveProgram = this.getResolveProgram(renderContext);
const resolveProgram = this.getResolveProgram(renderer);
if (resolveProgram == null)
return;
@ -317,11 +326,8 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
}
}
export class MCAAMonochromeStrategy extends XCAAStrategy {
protected coverVAO: WebGLVertexArrayObject | null;
protected lineVAOs: FastEdgeVAOs;
protected curveVAOs: FastEdgeVAOs;
export class MCAAStrategy extends XCAAStrategy {
protected vao: WebGLVertexArrayObject | null;
protected get usesDilationTransforms(): boolean {
return true;
@ -330,27 +336,25 @@ export class MCAAMonochromeStrategy extends XCAAStrategy {
attachMeshes(renderer: Renderer): void {
super.attachMeshes(renderer);
this.createCoverVAOIfNecessary(renderer);
this.createLineVAOs(renderer);
this.createCurveVAOs(renderer);
const renderContext = renderer.renderContext;
this.vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
}
antialiasObject(renderer: Renderer, objectIndex: number): void {
super.antialiasObject(renderer, objectIndex);
// Conservatively cover.
this.coverObjectIfNecessary(renderer, objectIndex);
// Antialias.
this.antialiasLinesOfObject(renderer, objectIndex);
this.antialiasCurvesOfObject(renderer, objectIndex);
const shaderProgram = this.edgeProgram(renderer);
this.antialiasEdgesOfObjectWithProgram(renderer, objectIndex, shaderProgram);
}
protected get usesAAFramebuffer(): boolean {
return true;
protected usesAAFramebuffer(renderer: Renderer): boolean {
return !renderer.isMulticolor;
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
protected getResolveProgram(renderer: Renderer): PathfinderShaderProgram | null {
const renderContext = renderer.renderContext;
if (renderer.isMulticolor)
return null;
if (this.subpixelAA !== 'none')
return renderContext.shaderPrograms.xcaaMonoSubpixelResolve;
return renderContext.shaderPrograms.xcaaMonoResolve;
@ -360,97 +364,52 @@ export class MCAAMonochromeStrategy extends XCAAStrategy {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
if (renderer.isMulticolor)
gl.clearColor(1.0, 1.0, 1.0, 1.0);
else
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clearDepth(0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
const gl = renderContext.gl;
gl.disable(gl.DEPTH_TEST);
}
protected clearForResolve(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
if (!renderer.isMulticolor) {
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
protected createCoverVAOIfNecessary(renderer: Renderer): void {
this.coverVAO = renderer.renderContext.vertexArrayObjectExt.createVertexArrayOES();
}
protected setBlendModeForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.blendEquation(gl.FUNC_ADD);
if (renderer.isMulticolor)
gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
else
gl.blendFunc(gl.ONE, gl.ONE);
gl.blendEquation(gl.FUNC_ADD);
gl.enable(gl.BLEND);
}
protected prepareAA(renderer: Renderer): void {
super.prepareAA(renderer);
this.setCoverDepthState(renderer);
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
this.clearForAA(renderer);
}
protected setCoverDepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
}
protected antialiasLinesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
lineProgram: PathfinderShaderProgram):
void {
if (renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.initLineVAOsForObject(renderer, objectIndex);
gl.useProgram(lineProgram.program);
const uniforms = lineProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex);
for (const direction of DIRECTIONS) {
const vao = this.lineVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const indexRanges = {
lower: renderer.meshData[meshIndex].edgeLowerLineIndexRanges,
upper: renderer.meshData[meshIndex].edgeUpperLineIndexRanges,
}[direction];
const count = calculateCountFromIndexRanges(pathRange, indexRanges);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected coverObjectIfNecessary(renderer: Renderer, objectIndex: number): void {
protected initVAOForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
@ -460,278 +419,45 @@ export class MCAAMonochromeStrategy extends XCAAStrategy {
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.initCoverVAOForObject(renderer, objectIndex);
const shaderProgram = this.edgeProgram(renderer);
const attributes = shaderProgram.attributes;
// Conservatively cover.
const coverProgram = renderContext.shaderPrograms.mcaaCover;
gl.useProgram(coverProgram.program);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
this.setAAUniforms(renderer, coverProgram.uniforms, objectIndex);
const bQuadRange = renderer.meshData[meshIndex].bQuadPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRange);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected initCurveVAOsForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
const curveProgram = this.curveProgram(renderer);
const attributes = curveProgram.attributes;
for (const direction of DIRECTIONS) {
const vao = this.curveVAOs[direction];
// FIXME(pcwalton): Refactor.
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const curveVertexPositionsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerCurveVertexPositions,
upper: renderer.meshes[meshIndex].edgeUpperCurveVertexPositions,
}[direction];
const curvePathIDsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerCurvePathIDs,
upper: renderer.meshes[meshIndex].edgeUpperCurvePathIDs,
}[direction];
const curveIndexRanges = {
lower: renderer.meshData[meshIndex].edgeLowerCurveIndexRanges,
upper: renderer.meshData[meshIndex].edgeUpperCurveIndexRanges,
}[direction];
const offset = calculateStartFromIndexRanges(pathRange, curveIndexRanges);
gl.useProgram(curveProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, curveVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset);
gl.vertexAttribPointer(attributes.aControlPointPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 2);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 6,
FLOAT32_SIZE * 6 * offset + FLOAT32_SIZE * 4);
gl.bindBuffer(gl.ARRAY_BUFFER, curvePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aControlPointPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected antialiasCurvesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
curveProgram: PathfinderShaderProgram):
void {
if (renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.initCurveVAOsForObject(renderer, objectIndex);
gl.useProgram(curveProgram.program);
const uniforms = curveProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex);
for (const direction of DIRECTIONS) {
const vao = this.curveVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
gl.uniform1i(uniforms.uWinding, direction === 'upper' ? 1 : 0);
const indexRanges = {
lower: renderer.meshData[meshIndex].edgeLowerCurveIndexRanges,
upper: renderer.meshData[meshIndex].edgeUpperCurveIndexRanges,
}[direction];
const count = calculateCountFromIndexRanges(pathRange, indexRanges);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected lineProgram(renderer: Renderer): PathfinderShaderProgram {
return renderer.renderContext.shaderPrograms.mcaaLine;
}
protected curveProgram(renderer: Renderer): PathfinderShaderProgram {
return renderer.renderContext.shaderPrograms.mcaaCurve;
}
private createLineVAOs(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const vaos: Partial<FastEdgeVAOs> = {};
for (const direction of DIRECTIONS)
vaos[direction] = renderContext.vertexArrayObjectExt.createVertexArrayOES();
this.lineVAOs = vaos as FastEdgeVAOs;
}
private initLineVAOsForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
const lineProgram = this.lineProgram(renderer);
const attributes = lineProgram.attributes;
for (const direction of DIRECTIONS) {
const vao = this.lineVAOs[direction];
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const lineVertexPositionsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerLineVertexPositions,
upper: renderer.meshes[meshIndex].edgeUpperLineVertexPositions,
}[direction];
const linePathIDsBuffer = {
lower: renderer.meshes[meshIndex].edgeLowerLinePathIDs,
upper: renderer.meshes[meshIndex].edgeUpperLinePathIDs,
}[direction];
const lineIndexRanges = {
lower: renderer.meshData[meshIndex].edgeLowerLineIndexRanges,
upper: renderer.meshData[meshIndex].edgeUpperLineIndexRanges,
}[direction];
const offset = calculateStartFromIndexRanges(pathRange, lineIndexRanges);
gl.useProgram(lineProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, lineVertexPositionsBuffer);
gl.vertexAttribPointer(attributes.aLeftPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
offset * FLOAT32_SIZE * 4);
gl.vertexAttribPointer(attributes.aRightPosition,
2,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
offset * FLOAT32_SIZE * 4 + FLOAT32_SIZE * 2);
gl.bindBuffer(gl.ARRAY_BUFFER, linePathIDsBuffer);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
offset * UINT16_SIZE);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aLeftPosition);
gl.enableVertexAttribArray(attributes.aRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aLeftPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aRightPosition,
1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private createCurveVAOs(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const vaos: Partial<FastEdgeVAOs> = {};
for (const direction of DIRECTIONS)
vaos[direction] = renderContext.vertexArrayObjectExt.createVertexArrayOES();
this.curveVAOs = vaos as FastEdgeVAOs;
}
get directRenderingMode(): DirectRenderingMode {
return 'none';
}
private initCoverVAOForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges);
const coverProgram = renderContext.shaderPrograms.mcaaCover;
const attributes = coverProgram.attributes;
gl.useProgram(coverProgram.program);
gl.useProgram(shaderProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxVertexPositions);
gl.vertexAttribPointer(attributes.aUpperLeftPosition,
2,
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions);
gl.vertexAttribPointer(attributes.aUpperEndpointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 4 * offset);
gl.vertexAttribPointer(attributes.aLowerRightPosition,
2,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset);
gl.vertexAttribPointer(attributes.aLowerEndpointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 4,
FLOAT32_SIZE * 4 * offset + FLOAT32_SIZE * 2);
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4);
gl.vertexAttribPointer(attributes.aControlPointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperEndpointPositions, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerEndpointPositions, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPositions, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs);
gl.vertexAttribPointer(attributes.aPathID,
1,
@ -739,49 +465,89 @@ export class MCAAMonochromeStrategy extends XCAAStrategy {
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aUpperLeftPosition);
gl.enableVertexAttribArray(attributes.aLowerRightPosition);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperLeftPosition, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerRightPosition, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aUpperEndpointPositions);
gl.enableVertexAttribArray(attributes.aLowerEndpointPositions);
gl.enableVertexAttribArray(attributes.aControlPointPositions);
gl.enableVertexAttribArray(attributes.aPathID);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private antialiasLinesOfObject(renderer: Renderer, objectIndex: number): void {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
const lineProgram = this.lineProgram(renderer);
renderContext.gl.useProgram(lineProgram.program);
// FIXME(pcwalton): Refactor.
this.antialiasLinesOfObjectWithProgram(renderer, objectIndex, lineProgram);
protected edgeProgram(renderer: Renderer): PathfinderShaderProgram {
return renderer.renderContext.shaderPrograms.mcaa;
}
private antialiasCurvesOfObject(renderer: Renderer, objectIndex: number): void {
protected antialiasEdgesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
shaderProgram: PathfinderShaderProgram):
void {
if (renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
this.setAAState(renderer);
const gl = renderContext.gl;
const curveProgram = this.curveProgram(renderer);
renderContext.gl.useProgram(curveProgram.program);
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.antialiasCurvesOfObjectWithProgram(renderer, objectIndex, curveProgram);
this.initVAOForObject(renderer, objectIndex);
gl.useProgram(shaderProgram.program);
const uniforms = shaderProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex);
// FIXME(pcwalton): Refactor.
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRanges);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
get directRenderingMode(): DirectRenderingMode {
return 'none';
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number):
void {
super.setAAUniforms(renderer, uniforms, objectIndex);
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
renderer.setPathColorsUniform(0, uniforms, 3);
gl.uniform1i(uniforms.uSnapToPixelGrid, renderer.isMulticolor ? 1 : 0);
}
}
export abstract class ECAAStrategy extends XCAAStrategy {
protected abstract get lineShaderProgramNames(): Array<keyof ShaderMap<void>>;
protected abstract get curveShaderProgramNames(): Array<keyof ShaderMap<void>>;
export class ECAAStrategy extends XCAAStrategy {
protected get lineShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaLine'];
}
protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaCurve', 'ecaaTransformedCurve'];
}
private lineVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
private curveVAOs: Partial<ShaderMap<WebGLVertexArrayObject>>;
protected get usesDilationTransforms(): boolean {
return false;
}
get directRenderingMode(): DirectRenderingMode {
return 'none';
}
@ -809,13 +575,19 @@ export abstract class ECAAStrategy extends XCAAStrategy {
this.curveShaderProgramNames[1]);
}
protected usesAAFramebuffer(): boolean {
return true;
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number):
void {
super.setAAUniforms(renderer, uniforms, objectIndex);
renderer.setEmboldenAmountUniform(objectIndex, uniforms);
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
protected getResolveProgram(renderer: Renderer): PathfinderShaderProgram {
const renderContext = renderer.renderContext;
if (this.subpixelAA !== 'none')
return renderContext.shaderPrograms.xcaaMonoSubpixelResolve;
return renderContext.shaderPrograms.xcaaMonoResolve;
@ -1089,212 +861,11 @@ export abstract class ECAAStrategy extends XCAAStrategy {
}
}
export class ECAAMonochromeStrategy extends ECAAStrategy {
protected get usesDilationTransforms(): boolean {
return false;
}
protected get usesAAFramebuffer(): boolean {
return true;
}
protected get lineShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaLine'];
}
protected get curveShaderProgramNames(): Array<keyof ShaderMap<void>> {
return ['ecaaCurve', 'ecaaTransformedCurve'];
}
}
export class MCAAMulticolorStrategy extends XCAAStrategy {
protected vao: WebGLVertexArrayObject;
protected get usesDilationTransforms(): boolean {
return true;
}
attachMeshes(renderer: Renderer): void {
super.attachMeshes(renderer);
const renderContext = renderer.renderContext;
this.vao = renderContext.vertexArrayObjectExt.createVertexArrayOES();
}
antialiasObject(renderer: Renderer, objectIndex: number): void {
super.antialiasObject(renderer, objectIndex);
const shaderProgram = this.edgeProgram(renderer);
this.antialiasEdgesOfObjectWithProgram(renderer, objectIndex, shaderProgram);
}
protected get usesAAFramebuffer(): boolean {
return false;
}
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram | null {
return null;
}
protected prepareAA(renderer: Renderer): void {
super.prepareAA(renderer);
this.clearForAA(renderer);
}
protected coverObjectIfNecessary(renderer: Renderer, objectIndex: number): void {}
protected antialiasEdgesOfObjectWithProgram(renderer: Renderer,
objectIndex: number,
shaderProgram: PathfinderShaderProgram):
void {
if (renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
this.initVAOForObject(renderer, objectIndex);
gl.useProgram(shaderProgram.program);
const uniforms = shaderProgram.uniforms;
this.setAAUniforms(renderer, uniforms, objectIndex);
// FIXME(pcwalton): Refactor.
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
this.setBlendModeForAA(renderer);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRanges);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, count);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clearDepth(0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.disable(gl.DEPTH_TEST);
}
protected clearForResolve(renderer: Renderer): void {}
protected setBlendModeForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
gl.blendEquation(gl.FUNC_ADD);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number):
void {
super.setAAUniforms(renderer, uniforms, objectIndex);
renderer.setPathColorsUniform(0, uniforms, 2);
}
protected initVAOForObject(renderer: Renderer, objectIndex: number): void {
if (renderer.meshes == null || renderer.meshData == null)
return;
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const pathRange = renderer.pathRangeForObject(objectIndex);
const meshIndex = renderer.meshIndexForObject(objectIndex);
const shaderProgram = this.edgeProgram(renderer);
const attributes = shaderProgram.attributes;
// FIXME(pcwalton): Refactor.
const vao = this.vao;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
const bQuadRanges = renderer.meshData[meshIndex].bQuadPathRanges;
const offset = calculateStartFromIndexRanges(pathRange, bQuadRanges);
gl.useProgram(shaderProgram.program);
gl.bindBuffer(gl.ARRAY_BUFFER, renderContext.quadPositionsBuffer);
gl.vertexAttribPointer(attributes.aQuadPosition, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].bQuadVertexPositions);
gl.vertexAttribPointer(attributes.aUpperEndpointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset);
gl.vertexAttribPointer(attributes.aLowerEndpointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 4);
gl.vertexAttribPointer(attributes.aControlPointPositions,
4,
gl.FLOAT,
false,
FLOAT32_SIZE * 12,
FLOAT32_SIZE * 12 * offset + FLOAT32_SIZE * 8);
gl.bindBuffer(gl.ARRAY_BUFFER, renderer.meshes[meshIndex].edgeBoundingBoxPathIDs);
gl.vertexAttribPointer(attributes.aPathID,
1,
gl.UNSIGNED_SHORT,
false,
0,
UINT16_SIZE * offset);
gl.enableVertexAttribArray(attributes.aQuadPosition);
gl.enableVertexAttribArray(attributes.aUpperEndpointPositions);
gl.enableVertexAttribArray(attributes.aLowerEndpointPositions);
gl.enableVertexAttribArray(attributes.aControlPointPositions);
gl.enableVertexAttribArray(attributes.aPathID);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aUpperEndpointPositions, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aLowerEndpointPositions, 1);
renderContext.instancedArraysExt
.vertexAttribDivisorANGLE(attributes.aControlPointPositions, 1);
renderContext.instancedArraysExt.vertexAttribDivisorANGLE(attributes.aPathID, 1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected edgeProgram(renderer: Renderer): PathfinderShaderProgram {
return renderer.renderContext.shaderPrograms.mcaaMulti;
}
get directRenderingMode(): DirectRenderingMode {
return 'none';
}
}
/// Switches between the mesh-based MCAA and ECAA depending on whether stem darkening is enabled.
///
/// FIXME(pcwalton): Share textures and FBOs between the two strategies.
export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
private mcaaStrategy: MCAAMonochromeStrategy;
private mcaaStrategy: MCAAStrategy;
private ecaaStrategy: ECAAStrategy;
get directRenderingMode(): DirectRenderingMode {
@ -1306,8 +877,8 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
}
constructor(level: number, subpixelAA: SubpixelAAType) {
this.mcaaStrategy = new MCAAMonochromeStrategy(level, subpixelAA);
this.ecaaStrategy = new ECAAMonochromeStrategy(level, subpixelAA);
this.mcaaStrategy = new MCAAStrategy(level, subpixelAA);
this.ecaaStrategy = new ECAAStrategy(level, subpixelAA);
}
init(renderer: Renderer): void {

View File

@ -87,6 +87,13 @@ impl BQuad {
}
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub struct BQuadVertexPositions {
pub upper_endpoint_positions: [Point2D<f32>; 2],
pub lower_endpoint_positions: [Point2D<f32>; 2],
pub control_point_positions: [Point2D<f32>; 2],
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u8)]
pub enum AntialiasingMode {

View File

@ -18,14 +18,14 @@ use std::ops::Range;
use std::u32;
use normal;
use {BQuad, BVertexLoopBlinnData};
use {BQuad, BQuadVertexPositions, BVertexLoopBlinnData};
#[derive(Debug, Clone)]
pub struct MeshLibrary {
pub path_ranges: Vec<PathRanges>,
pub b_quads: Vec<BQuad>,
// FIXME(pcwalton): Merge with `b_vertex_positions` below.
pub b_quad_vertex_positions: Vec<Point2D<f32>>,
pub b_quad_vertex_positions: Vec<BQuadVertexPositions>,
pub b_vertex_positions: Vec<Point2D<f32>>,
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
pub b_vertex_normals: Vec<f32>,
@ -94,12 +94,11 @@ impl MeshLibrary {
let lower_right_position =
&self.b_vertex_positions[b_quad.lower_right_vertex_index as usize];
self.b_quad_vertex_positions.extend_from_slice(&[
*upper_left_position,
*upper_right_position,
*lower_left_position,
*lower_right_position,
]);
let mut b_quad_vertex_positions = BQuadVertexPositions {
upper_endpoint_positions: [*upper_left_position, *upper_right_position],
lower_endpoint_positions: [*lower_left_position, *lower_right_position],
control_point_positions: [Point2D::zero(), Point2D::zero()],
};
let upper_left_bounding_box_position =
Point2D::new(upper_left_position.x,
@ -114,7 +113,6 @@ impl MeshLibrary {
});
if b_quad.upper_control_point_vertex_index == u32::MAX {
self.b_quad_vertex_positions.push(Point2D::zero());
self.edge_data.upper_line_vertex_positions.push(EdgeLineVertexPositions {
left: *upper_left_position,
right: *upper_right_position,
@ -122,7 +120,7 @@ impl MeshLibrary {
} else {
let upper_control_point_position =
&self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize];
self.b_quad_vertex_positions.push(*upper_control_point_position);
b_quad_vertex_positions.control_point_positions[0] = *upper_control_point_position;
self.edge_data.upper_curve_vertex_positions.push(EdgeCurveVertexPositions {
left: *upper_left_position,
control_point: *upper_control_point_position,
@ -131,7 +129,6 @@ impl MeshLibrary {
}
if b_quad.lower_control_point_vertex_index == u32::MAX {
self.b_quad_vertex_positions.push(Point2D::zero());
self.edge_data.lower_line_vertex_positions.push(EdgeLineVertexPositions {
left: *lower_left_position,
right: *lower_right_position,
@ -139,13 +136,15 @@ impl MeshLibrary {
} else {
let lower_control_point_position =
&self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize];
self.b_quad_vertex_positions.push(*lower_control_point_position);
b_quad_vertex_positions.control_point_positions[1] = *lower_control_point_position;
self.edge_data.lower_curve_vertex_positions.push(EdgeCurveVertexPositions {
left: *lower_left_position,
control_point: *lower_control_point_position,
right: *lower_right_position,
});
}
self.b_quad_vertex_positions.push(b_quad_vertex_positions);
}
/// Reverses interior indices so that they draw front-to-back.

View File

@ -145,17 +145,23 @@ vec2 computeMCAASnappedPosition(vec2 position,
vec4 localTransformST,
vec4 globalTransformST,
ivec2 framebufferSize,
float slope) {
float slope,
bool snapToPixelGrid) {
position = hintPosition(position, hints);
position = transformVertexPositionST(position, localTransformST);
position = transformVertexPositionST(position, globalTransformST);
position = convertClipToScreenSpace(position, framebufferSize);
float xNudge = fract(position.x);
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);
}

View File

@ -28,5 +28,5 @@ varying float vSign;
void main() {
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
float alpha = float(sign(side) == sign(vSign));
gl_FragColor = vec4(vColor.rgb, vColor.a * alpha);
gl_FragColor = alpha * vColor;
}

View File

@ -1,4 +1,4 @@
// pathfinder/shaders/gles2/xcaa-curve.fs.glsl
// pathfinder/shaders/gles2/ecaa-curve.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//

View File

@ -16,10 +16,10 @@
//! Use this shader only when *all* of the following are true:
//!
//! 1. You are only rendering monochrome paths such as text. (Otherwise,
//! consider `mcaa-multi`.)
//! consider MCAA.)
//!
//! 2. The paths are relatively small, so overdraw is not a concern.
//! (Otherwise, consider the MCAA shaders.)
//! (Otherwise, consider MCAA.)
//!
//! 3. Your transform is only a scale and/or translation, not a perspective,
//! rotation, or skew. (Otherwise, consider `ecaa-transformed-curve`.)

View File

@ -1,4 +1,4 @@
// pathfinder/shaders/gles2/xcaa-line.fs.glsl
// pathfinder/shaders/gles2/ecaa-line.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//

View File

@ -17,10 +17,10 @@
//! Use this shader only when *both* of the following are true:
//!
//! 1. You are only rendering monochrome paths such as text. (Otherwise,
//! consider `mcaa-multi`.)
//! consider MCAA.)
//!
//! 2. The paths are relatively small, so overdraw is not a concern.
//! (Otherwise, consider the MCAA shaders.)
//! (Otherwise, consider MCAA.)
precision highp float;

View File

@ -20,10 +20,10 @@
//! Use this shader only when *all* of the following are true:
//!
//! 1. You are only rendering monochrome paths such as text. (Otherwise,
//! consider `mcaa-multi`.)
//! consider MCAA.)
//!
//! 2. The paths are relatively small, so overdraw is not a concern.
//! (Otherwise, consider the MCAA shaders.)
//! (Otherwise, consider MCAA.)
//!
//! 3. Your transform contains perspective, rotation, or skew. (Otherwise,
//! consider `ecaa-curve`, which is faster and saves a pass.)

View File

@ -1,33 +0,0 @@
// pathfinder/shaders/gles2/mcaa-cover.fs.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.
//! Performs the conservative coverage step for *mesh coverage antialiasing*
//! (MCAA).
//!
//! This shader expects to render to the red channel of a floating point color
//! buffer. Half precision floating point should be sufficient.
//!
//! Use this shader only when *both* of the following are true:
//!
//! 1. You are only rendering monochrome paths such as text. (Otherwise,
//! consider `mcaa-multi`.)
//!
//! 2. Your transform is only a scale and/or translation, not a perspective,
//! rotation, or skew. (Otherwise, consider the ECAA shaders.)
precision highp float;
varying vec2 vHorizontalExtents;
void main() {
vec2 sides = gl_FragCoord.xx + vec2(-0.5, 0.5);
vec2 clampedSides = clamp(vHorizontalExtents, sides.x, sides.y);
gl_FragColor = vec4(vec3(clampedSides.y - clampedSides.x), 1.0);
}

View File

@ -1,63 +0,0 @@
// pathfinder/shaders/gles2/mcaa-cover.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.
//! Performs the conservative coverage step for *mesh coverage antialiasing*
//! (MCAA).
//!
//! This shader expects to render to the red channel of a floating point color
//! buffer. Half precision floating point should be sufficient.
//!
//! Use this shader only when *both* of the following are true:
//!
//! 1. You are only rendering monochrome paths such as text. (Otherwise,
//! consider `mcaa-multi`.)
//!
//! 2. Your transform is only a scale and/or translation, not a perspective,
//! rotation, or skew. (Otherwise, consider the ECAA shaders.)
precision highp float;
uniform vec4 uTransformST;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
attribute vec2 aQuadPosition;
attribute vec2 aUpperLeftPosition;
attribute vec2 aLowerRightPosition;
attribute float aPathID;
varying vec2 vHorizontalExtents;
void main() {
int pathID = int(aPathID);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
vec2 upperLeftPosition = computeMCAAPosition(aUpperLeftPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
vec2 lowerRightPosition = computeMCAAPosition(aLowerRightPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
vHorizontalExtents = vec2(upperLeftPosition.x, lowerRightPosition.x);
vec4 extents = vec4(upperLeftPosition.x, ceil(upperLeftPosition.y), lowerRightPosition);
vec2 position = computeXCAAClipSpaceQuadPosition(extents, aQuadPosition, uFramebufferSize);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
}

View File

@ -1,75 +0,0 @@
// pathfinder/shaders/gles2/mcaa-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.
//! Renders the curved edges of paths when performing *mesh coverage
//! antialiasing* (MCAA).
//!
//! This shader expects to render to the red channel of a floating point color
//! buffer. Half precision floating point should be sufficient.
//!
//! Use this shader only when *both* of the following are true:
//!
//! 1. You are only rendering monochrome paths such as text. (Otherwise,
//! consider `mcaa-multi`.)
//!
//! 2. Your transform is only a scale and/or translation, not a perspective,
//! rotation, or skew. (Otherwise, consider the ECAA shaders.)
precision highp float;
uniform vec4 uTransformST;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform bool uWinding;
attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aControlPointPosition;
attribute vec2 aRightPosition;
attribute float aPathID;
varying vec4 vEndpoints;
varying vec2 vControlPoint;
varying float vWinding;
void main() {
vec2 leftPosition = aLeftPosition;
vec2 controlPointPosition = aControlPointPosition;
vec2 rightPosition = aRightPosition;
int pathID = int(aPathID);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
// Transform the points, and compute the position of this vertex.
vec2 position;
if (computeMCAAQuadPosition(position,
leftPosition,
rightPosition,
aQuadPosition,
uFramebufferSize,
transformST,
uTransformST,
uHints)) {
controlPointPosition = computeMCAAPosition(controlPointPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
}
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vEndpoints = vec4(leftPosition, rightPosition);
vControlPoint = controlPointPosition;
vWinding = uWinding ? 1.0 : -1.0;
}

View File

@ -1,62 +0,0 @@
// pathfinder/shaders/gles2/mcaa-line.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.
//! Renders the straight line segments of paths when performing *mesh coverage
//! antialiasing* (MCAA).
//!
//! Use this shader only when *both* of the following are true:
//!
//! 1. You are only rendering monochrome paths such as text. (Otherwise,
//! consider `mcaa-multi`.)
//!
//! 2. Your transform is only a scale and/or translation, not a perspective,
//! rotation, or skew. (Otherwise, consider the ECAA shaders.)
precision highp float;
uniform vec4 uTransformST;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform bool uWinding;
attribute vec2 aQuadPosition;
attribute vec2 aLeftPosition;
attribute vec2 aRightPosition;
attribute float aPathID;
varying vec4 vEndpoints;
varying float vWinding;
void main() {
vec2 leftPosition = aLeftPosition;
vec2 rightPosition = aRightPosition;
int pathID = int(aPathID);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
// Transform the points, and compute the position of this vertex.
vec2 position;
computeMCAAQuadPosition(position,
leftPosition,
rightPosition,
aQuadPosition,
uFramebufferSize,
transformST,
uTransformST,
uHints);
float depth = convertPathIndexToViewportDepthValue(pathID);
gl_Position = vec4(position, depth, 1.0);
vEndpoints = vec4(leftPosition, rightPosition);
vWinding = uWinding ? 1.0 : -1.0;
}

View File

@ -1,126 +0,0 @@
// pathfinder/shaders/gles2/mcaa-multi.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.
//! Renders paths when performing multicolor *mesh coverage antialiasing*
//! (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.)
#define MAX_SLOPE 10.0
precision highp float;
uniform vec4 uTransformST;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathColors;
attribute vec2 aQuadPosition;
attribute vec4 aUpperEndpointPositions;
attribute vec4 aLowerEndpointPositions;
attribute vec4 aControlPointPositions;
attribute float aPathID;
varying vec4 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor;
void main() {
vec2 tlPosition = aUpperEndpointPositions.xy;
vec2 tcPosition = aControlPointPositions.xy;
vec2 trPosition = aUpperEndpointPositions.zw;
vec2 blPosition = aLowerEndpointPositions.xy;
vec2 bcPosition = aControlPointPositions.zw;
vec2 brPosition = aLowerEndpointPositions.zw;
vec2 quadPosition = aQuadPosition;
int pathID = int(aPathID);
vec4 transformST = fetchFloat4Data(uPathTransformST, pathID, uPathTransformSTDimensions);
vec4 color = fetchFloat4Data(uPathColors, pathID, uPathColorsDimensions);
vec2 topVector = trPosition - tlPosition, bottomVector = brPosition - blPosition;
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;
// Transform the points, and compute the position of this vertex.
tlPosition = computeMCAASnappedPosition(tlPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topSlope);
trPosition = computeMCAASnappedPosition(trPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topSlope);
tcPosition = computeMCAAPosition(tcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
blPosition = computeMCAASnappedPosition(blPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope);
brPosition = computeMCAASnappedPosition(brPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope);
bcPosition = computeMCAAPosition(bcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
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;
position.x = quadPosition.x < 0.5 ? tlPosition.x : trPosition.x;
if (quadPosition.y < 0.5)
position.y = floor(min(tlPosition.y, trPosition.y));
else
position.y = ceil(max(blPosition.y, brPosition.y));
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;
}

View File

@ -1,4 +1,4 @@
// pathfinder/shaders/gles2/mcaa-multi.fs.glsl
// pathfinder/shaders/gles2/mcaa.fs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
@ -69,5 +69,5 @@ void main() {
1.0);
// Compute area.
gl_FragColor = vec4(vColor.rgb, vColor.a * alpha);
gl_FragColor = alpha * vColor;
}

146
shaders/gles2/mcaa.vs.glsl Normal file
View File

@ -0,0 +1,146 @@
// pathfinder/shaders/gles2/mcaa.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.
//! Renders paths when performing *mesh coverage antialiasing* (MCAA). This
//! one shader handles both lines and curves.
//!
//! This shader expects to render to a standard RGB color buffer in the
//! multicolor case or a single-channel floating-point color buffer in the
//! monochrome case.
//!
//! Set state as follows depending on whether multiple overlapping multicolor
//! paths are present:
//!
//! * When paths of multiple colors are present, use
//! `glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE)` and
//! set `uSnapToPixelGrid` to 1.
//!
//! * Otherwise, if only one color of path is present, use
//! `glBlendFunc(GL_ONE, GL_ONE)` and set `uSnapToPixelGrid` to 0.
//!
//! Use this shader only when 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.)
#define MAX_SLOPE 10.0
precision highp float;
uniform vec4 uTransformST;
uniform vec4 uHints;
uniform ivec2 uFramebufferSize;
uniform ivec2 uPathTransformSTDimensions;
uniform sampler2D uPathTransformST;
uniform ivec2 uPathColorsDimensions;
uniform sampler2D uPathColors;
uniform bool uSnapToPixelGrid;
attribute vec2 aQuadPosition;
attribute vec4 aUpperEndpointPositions;
attribute vec4 aLowerEndpointPositions;
attribute vec4 aControlPointPositions;
attribute float aPathID;
varying vec4 vUpperEndpoints;
varying vec4 vLowerEndpoints;
varying vec4 vControlPoints;
varying vec4 vColor;
void main() {
vec2 tlPosition = aUpperEndpointPositions.xy;
vec2 tcPosition = aControlPointPositions.xy;
vec2 trPosition = aUpperEndpointPositions.zw;
vec2 blPosition = aLowerEndpointPositions.xy;
vec2 bcPosition = aControlPointPositions.zw;
vec2 brPosition = aLowerEndpointPositions.zw;
vec2 quadPosition = aQuadPosition;
int pathID = int(floor(aPathID));
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;
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;
// Transform the points, and compute the position of this vertex.
tlPosition = computeMCAASnappedPosition(tlPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topSlope,
uSnapToPixelGrid);
trPosition = computeMCAASnappedPosition(trPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
topSlope,
uSnapToPixelGrid);
tcPosition = computeMCAAPosition(tcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
blPosition = computeMCAASnappedPosition(blPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope,
uSnapToPixelGrid);
brPosition = computeMCAASnappedPosition(brPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize,
bottomSlope,
uSnapToPixelGrid);
bcPosition = computeMCAAPosition(bcPosition,
uHints,
transformST,
uTransformST,
uFramebufferSize);
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 (uSnapToPixelGrid)
position.x = quadPosition.x < 0.5 ? tlPosition.x : trPosition.x;
else
position.x = quadPosition.x < 0.5 ? floor(tlPosition.x) : ceil(trPosition.x);
if (quadPosition.y < 0.5)
position.y = floor(min(tlPosition.y, trPosition.y));
else
position.y = ceil(max(blPosition.y, brPosition.y));
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);
}
}

View File

@ -41,5 +41,6 @@ void main() {
lcdFilter(shadeL.x, shade0, shadeR.x, shadeR.y, shadeR.z));
vec3 color = mix(uBGColor.rgb, uFGColor.rgb, shades);
gl_FragColor = vec4(color, any(greaterThan(shades, vec3(0.0))) ? uFGColor.a : uBGColor.a);
float alpha = any(greaterThan(shades, vec3(0.0))) ? uFGColor.a : uBGColor.a;
gl_FragColor = alpha * vec4(color, 1.0);
}