Add partial support for clip paths in the SVG demo.

At the moment, this only works when antialiasing is off.
This commit is contained in:
Patrick Walton 2017-11-01 16:09:58 -07:00
parent 511e5956d8
commit 4cbc2a8800
14 changed files with 478 additions and 125 deletions

View File

@ -359,8 +359,8 @@ class ThreeDRenderer extends Renderer {
return this.destAllocatedSize;
}
protected get depthFunction(): GLenum {
return this.renderContext.gl.LESS;
get backgroundColor(): glmatrix.vec4 {
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
}
protected get pathIDsAreInstanced(): boolean {
@ -375,6 +375,10 @@ class ThreeDRenderer extends Renderer {
return glmatrix.vec2.clone([1.0, 1.0]);
}
protected get objectCount(): number {
return this.meshes.length;
}
private cubeVertexPositionBuffer: WebGLBuffer;
private cubeIndexBuffer: WebGLBuffer;
@ -431,9 +435,17 @@ class ThreeDRenderer extends Renderer {
this.renderContext.gl.uniform4f(uniforms.uHints, 0, 0, 0, 0);
}
protected clearColorForObject(objectIndex: number): glmatrix.vec4 | null {
return null;
}
protected drawSceneryIfNecessary(): void {
const gl = this.renderContext.gl;
// Set up the depth buffer for drawing the monument.
gl.clearDepth(1.0);
gl.clear(gl.DEPTH_BUFFER_BIT);
this.drawMonument();
// Clear to avoid Z-fighting.
@ -441,6 +453,10 @@ class ThreeDRenderer extends Renderer {
gl.clear(gl.DEPTH_BUFFER_BIT);
this.drawDistantGlyphs();
// Set up the depth buffer for direct rendering.
gl.clearDepth(0.0);
gl.clear(gl.DEPTH_BUFFER_BIT);
}
protected compositeIfNecessary(): void {}
@ -473,8 +489,12 @@ class ThreeDRenderer extends Renderer {
throw new PathfinderError("Unsupported antialiasing type!");
}
protected clearForDirectRendering(): void {
protected clearDestFramebuffer(): void {
const gl = this.renderContext.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.destFramebuffer);
gl.viewport(0, 0, this.destAllocatedSize[0], this.destAllocatedSize[1]);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clearDepth(1.0);
gl.depthMask(true);
@ -605,7 +625,7 @@ class ThreeDRenderer extends Renderer {
// Set state for the monument.
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(this.depthFunction);
gl.depthFunc(gl.LESS);
gl.depthMask(true);
gl.disable(gl.SCISSOR_TEST);
gl.disable(gl.BLEND);

View File

@ -10,7 +10,10 @@
import * as glmatrix from 'gl-matrix';
import {createFramebuffer, createFramebufferColorTexture} from './gl-utils';
import {createFramebufferDepthTexture} from './gl-utils';
import {Renderer} from './renderer';
import {unwrapNull} from './utils';
import {DemoView} from './view';
export type AntialiasingStrategyName = 'none' | 'ssaa' | 'xcaa';
@ -42,7 +45,12 @@ export abstract class AntialiasingStrategy {
// Called before direct rendering.
//
// Typically, this redirects direct rendering to a framebuffer of some sort.
abstract prepareForDirectRendering(renderer: Renderer): void;
abstract prepareForRendering(renderer: Renderer): void;
// Called before directly rendering a single object.
abstract prepareToDirectlyRenderObject(renderer: Renderer, objectIndex: number): void;
abstract finishDirectlyRenderingObject(renderer: Renderer, objectIndex: number): void;
// Called after direct rendering.
//
@ -58,9 +66,16 @@ export abstract class AntialiasingStrategy {
export class NoAAStrategy extends AntialiasingStrategy {
framebufferSize: glmatrix.vec2;
private renderTargetColorTextures: WebGLTexture[];
private renderTargetDepthTextures: WebGLTexture[];
private renderTargetFramebuffers: WebGLFramebuffer[];
constructor(level: number, subpixelAA: SubpixelAAType) {
super();
this.framebufferSize = glmatrix.vec2.create();
this.renderTargetColorTextures = [];
this.renderTargetDepthTextures = [];
this.renderTargetFramebuffers = [];
}
attachMeshes(renderer: Renderer) {}
@ -73,11 +88,61 @@ export class NoAAStrategy extends AntialiasingStrategy {
return glmatrix.mat4.create();
}
prepareForDirectRendering(renderer: Renderer) {
prepareForRendering(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, renderer.destFramebuffer);
renderContext.gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]);
renderContext.gl.disable(renderContext.gl.SCISSOR_TEST);
const gl = renderContext.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, renderer.destFramebuffer);
gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]);
gl.disable(gl.SCISSOR_TEST);
}
prepareToDirectlyRenderObject(renderer: Renderer, objectIndex: number): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
if (renderer.usesIntermediateRenderTargets &&
(renderer.renderTaskTypeForObject(objectIndex) === 'clip' ||
renderer.compositingOperationForObject(objectIndex) != null)) {
if (this.renderTargetColorTextures[objectIndex] == null) {
this.renderTargetColorTextures[objectIndex] =
createFramebufferColorTexture(gl,
this.framebufferSize,
renderContext.colorAlphaFormat);
}
if (this.renderTargetDepthTextures[objectIndex] == null) {
this.renderTargetDepthTextures[objectIndex] =
createFramebufferDepthTexture(gl, this.framebufferSize);
}
if (this.renderTargetFramebuffers[objectIndex] == null) {
this.renderTargetFramebuffers[objectIndex] =
createFramebuffer(gl,
this.renderTargetColorTextures[objectIndex],
this.renderTargetDepthTextures[objectIndex]);
}
gl.bindFramebuffer(gl.FRAMEBUFFER, this.renderTargetFramebuffers[objectIndex]);
} else {
gl.bindFramebuffer(gl.FRAMEBUFFER, renderer.destFramebuffer);
}
gl.viewport(0, 0, this.framebufferSize[0], this.framebufferSize[1]);
gl.disable(gl.SCISSOR_TEST);
}
finishDirectlyRenderingObject(renderer: Renderer, objectIndex: number): void {
if (!renderer.usesIntermediateRenderTargets)
return;
const compositingOperation = renderer.compositingOperationForObject(objectIndex);
if (compositingOperation == null)
return;
const gl = renderer.renderContext.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, renderer.destFramebuffer);
gl.viewport(0, 0, renderer.destAllocatedSize[0], renderer.destAllocatedSize[1]);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
compositingOperation.composite(renderer, objectIndex, this.renderTargetColorTextures);
}
antialias(renderer: Renderer) {}

View File

@ -309,10 +309,6 @@ class BenchmarkRenderer extends Renderer {
return glmatrix.vec2.clone([1.0, 1.0]);
}
protected get depthFunction(): number {
return this.renderContext.gl.GREATER;
}
protected get worldTransform() {
const canvas = this.renderContext.canvas;
@ -329,6 +325,10 @@ class BenchmarkRenderer extends Renderer {
return transform;
}
protected get objectCount(): number {
return this.meshes.length;
}
private _pixelsPerEm: number = 32.0;
private get pixelsPerUnit(): number {

View File

@ -203,6 +203,20 @@ const RANGE_TO_RANGE_BUFFER_TABLE: RangeToRangeBufferTable = {
segmentLineRanges: 'segmentLinePathIDs',
};
const RANGE_KEYS: Array<keyof PathRanges> = [
'bQuadPathRanges',
'bVertexPathRanges',
'coverInteriorIndexRanges',
'coverCurveIndexRanges',
'edgeBoundingBoxRanges',
'edgeUpperLineIndexRanges',
'edgeUpperCurveIndexRanges',
'edgeLowerLineIndexRanges',
'edgeLowerCurveIndexRanges',
'segmentCurveRanges',
'segmentLineRanges',
];
type BufferType = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER';
export interface Meshes<T> {
@ -375,7 +389,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
tempOriginalRanges[key] = this[key];
const newExpandedRanges = [];
for (const newPathID of pathIDs)
for (const pathIndex of pathIDs)
newExpandedRanges.push(new Range(0, 0));
tempExpandedRanges[key] = newExpandedRanges;
}
@ -544,7 +558,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
}
}
export class PathfinderMeshBuffers implements Meshes<WebGLBuffer> {
export class PathfinderMeshBuffers implements Meshes<WebGLBuffer>, PathRanges {
readonly bQuads: WebGLBuffer;
readonly bVertexPositions: WebGLBuffer;
readonly bVertexPathIDs: WebGLBuffer;
@ -569,6 +583,18 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer> {
readonly segmentLineNormals: WebGLBuffer;
readonly segmentCurveNormals: WebGLBuffer;
readonly bQuadPathRanges: Range[];
readonly bVertexPathRanges: Range[];
readonly coverInteriorIndexRanges: Range[];
readonly coverCurveIndexRanges: Range[];
readonly edgeBoundingBoxRanges: Range[];
readonly edgeUpperLineIndexRanges: Range[];
readonly edgeUpperCurveIndexRanges: Range[];
readonly edgeLowerLineIndexRanges: Range[];
readonly edgeLowerCurveIndexRanges: Range[];
readonly segmentCurveRanges: Range[];
readonly segmentLineRanges: Range[];
constructor(gl: WebGLRenderingContext, meshData: PathfinderMeshData) {
for (const bufferName of Object.keys(BUFFER_TYPES) as Array<keyof Meshes<void>>) {
const bufferType = gl[BUFFER_TYPES[bufferName]];
@ -577,6 +603,9 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer> {
gl.bufferData(bufferType, meshData[bufferName], gl.STATIC_DRAW);
this[bufferName] = buffer;
}
for (const rangeName of RANGE_KEYS)
this[rangeName] = meshData[rangeName];
}
}
@ -656,7 +685,7 @@ function copyIndices(destIndices: number[],
const lastDestIndex = destIndices.length;
destRanges[expandedPathID + 1] = new Range(firstExpandedIndex, lastDestIndex - firstDestIndex);
destRanges[expandedPathID - 1] = new Range(firstDestIndex, lastDestIndex);
}
function copySegments(segmentBufferNames: Array<keyof Meshes<void>>,

View File

@ -0,0 +1,59 @@
// pathfinder/client/src/render-task.ts
//
// Copyright © 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.
import {Renderer} from "./renderer";
import {Range} from "./utils";
export type RenderTaskType = 'color' | 'clip';
export class RenderTask {
type: RenderTaskType;
instanceIndices: Range;
compositingOperation: CompositingOperation | null;
constructor(type: RenderTaskType,
instanceIndices: Range,
compositingOperation?: CompositingOperation) {
this.type = type;
this.instanceIndices = instanceIndices;
this.compositingOperation = compositingOperation != null ? compositingOperation : null;
}
}
export type CompositingOperation = AlphaMaskCompositingOperation;
export class AlphaMaskCompositingOperation {
alphaFramebufferIndex: number;
constructor(alphaFramebufferIndex: number) {
this.alphaFramebufferIndex = alphaFramebufferIndex;
}
composite(renderer: Renderer, sourceTextureIndex: number, textures: WebGLTexture[]): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
const program = renderContext.shaderPrograms.compositeAlphaMask;
gl.useProgram(program.program);
renderContext.initQuadVAO(program.attributes);
// Composite to the current framebuffer.
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textures[sourceTextureIndex]);
gl.uniform1i(program.uniforms.uSource, 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, textures[this.alphaFramebufferIndex]);
gl.uniform1i(program.uniforms.uMask, 1);
renderer.setTransformAndTexScaleUniformsForDest(program.uniforms);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderContext.quadElementsBuffer);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
}
}

View File

@ -9,14 +9,16 @@
// except according to those terms.
import * as glmatrix from 'gl-matrix';
import * as _ from 'lodash';
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from './aa-strategy';
import {StemDarkeningMode, SubpixelAAType} from './aa-strategy';
import PathfinderBufferTexture from "./buffer-texture";
import {UniformMap} from './gl-utils';
import {PathfinderMeshBuffers, PathfinderMeshData} from "./meshes";
import {CompositingOperation, RenderTaskType} from './render-task';
import {ShaderMap} from './shader-loader';
import {FLOAT32_SIZE, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull} from './utils';
import {FLOAT32_SIZE, Range, UINT16_SIZE, UINT32_SIZE, unwrapNull, unwrapUndef} from './utils';
import {RenderContext, Timings} from "./view";
import {MCAAMulticolorStrategy} from './xcaa-strategy';
@ -48,6 +50,14 @@ export abstract class Renderer {
return null;
}
get backgroundColor(): glmatrix.vec4 {
return glmatrix.vec4.create();
}
get usesIntermediateRenderTargets(): boolean {
return false;
}
abstract get destFramebuffer(): WebGLFramebuffer | null;
abstract get destAllocatedSize(): glmatrix.vec2;
abstract get destUsedSize(): glmatrix.vec2;
@ -60,11 +70,7 @@ export abstract class Renderer {
return false;
}
protected get backgroundColor(): glmatrix.vec4 {
return glmatrix.vec4.create();
}
protected abstract get depthFunction(): GLenum;
protected abstract get objectCount(): number;
protected abstract get usedSizeFactor(): glmatrix.vec2;
protected abstract get worldTransform(): glmatrix.mat4;
@ -111,19 +117,17 @@ export abstract class Renderer {
renderContext.atlasRenderingTimerQuery);
}
// Antialias.
this.clearDestFramebuffer();
const antialiasingStrategy = unwrapNull(this.antialiasingStrategy);
antialiasingStrategy.antialias(this);
// Prepare for direct rendering.
antialiasingStrategy.prepareForDirectRendering(this);
// Clear.
this.clearForDirectRendering();
antialiasingStrategy.prepareForRendering(this);
// Draw "scenery" (used in the 3D view).
this.drawSceneryIfNecessary();
// Antialias.
antialiasingStrategy.antialias(this);
// Perform direct rendering (Loop-Blinn).
if (antialiasingStrategy.directRenderingMode !== 'none')
this.renderDirect();
@ -249,7 +253,20 @@ export abstract class Renderer {
setPathColorsUniform(objectIndex: number, uniforms: UniformMap, textureUnit: number): void {
const gl = this.renderContext.gl;
this.pathColorsBufferTextures[objectIndex].bind(gl, uniforms, textureUnit);
const meshIndex = this.meshIndexForObject(objectIndex);
this.pathColorsBufferTextures[meshIndex].bind(gl, uniforms, textureUnit);
}
renderTaskTypeForObject(objectIndex: number): RenderTaskType {
return 'color';
}
compositingOperationForObject(objectIndex: number): CompositingOperation | null {
return null;
}
protected clearColorForObject(objectIndex: number): glmatrix.vec4 | null {
return glmatrix.vec4.create();
}
protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
@ -266,12 +283,26 @@ export abstract class Renderer {
protected drawSceneryIfNecessary(): void {}
protected clearForDirectRendering(): void {
const renderingMode = unwrapNull(this.antialiasingStrategy).directRenderingMode;
protected clearDestFramebuffer(): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const clearColor = this.backgroundColor;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.destFramebuffer);
gl.viewport(0, 0, this.destAllocatedSize[0], this.destAllocatedSize[1]);
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
gl.clear(gl.COLOR_BUFFER_BIT);
}
protected clearForDirectRendering(objectIndex: number): void {
const renderingMode = unwrapNull(this.antialiasingStrategy).directRenderingMode;
const renderContext = this.renderContext;
const gl = renderContext.gl;
const clearColor = this.clearColorForObject(objectIndex);
if (clearColor == null)
return;
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
switch (renderingMode) {
@ -298,6 +329,15 @@ export abstract class Renderer {
return new Range(0, 1);
}
protected meshIndexForObject(objectIndex: number): number {
return objectIndex;
}
protected pathRangeForObject(objectIndex: number): Range {
const bVertexPathRanges = this.meshes[objectIndex].bVertexPathRanges;
return new Range(1, bVertexPathRanges.length + 1);
}
/// Called whenever new GPU timing statistics are available.
protected newTimingsReceived(): void {}
@ -307,16 +347,27 @@ export abstract class Renderer {
const antialiasingStrategy = unwrapNull(this.antialiasingStrategy);
const renderingMode = antialiasingStrategy.directRenderingMode;
const objectCount = this.objectCount;
for (let objectIndex = 0; objectIndex < this.meshes.length; objectIndex++) {
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
const instanceRange = this.instanceRangeForObject(objectIndex);
if (instanceRange.isEmpty)
continue;
const meshData = this.meshData[objectIndex];
const pathRange = this.pathRangeForObject(objectIndex);
const meshIndex = this.meshIndexForObject(objectIndex);
// Prepare for direct rendering.
antialiasingStrategy.prepareToDirectlyRenderObject(this, objectIndex);
// Clear.
this.clearForDirectRendering(objectIndex);
const meshes = this.meshes[meshIndex];
const meshData = this.meshData[meshIndex];
// Set up implicit cover state.
gl.depthFunc(this.depthFunction);
gl.depthFunc(gl.GREATER);
gl.depthMask(true);
gl.enable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
@ -336,21 +387,26 @@ export abstract class Renderer {
this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
this.setHintsUniform(directInteriorProgram.uniforms);
this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0);
this.pathTransformBufferTextures[objectIndex]
this.pathTransformBufferTextures[meshIndex]
.bind(gl, directInteriorProgram.uniforms, 1);
if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 2);
}
let indexCount = meshData.coverInteriorIndices.byteLength / UINT32_SIZE;
const coverInteriorRange = getMeshIndexRange(meshes.coverInteriorIndexRanges,
pathRange);
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
gl.drawElements(gl.TRIANGLES,
coverInteriorRange.length,
gl.UNSIGNED_INT,
coverInteriorRange.start * UINT32_SIZE);
} else {
renderContext.instancedArraysExt.drawElementsInstancedANGLE(gl.TRIANGLES,
indexCount,
gl.UNSIGNED_INT,
0,
instanceRange.length);
renderContext.instancedArraysExt
.drawElementsInstancedANGLE(gl.TRIANGLES,
coverInteriorRange.length,
gl.UNSIGNED_INT,
0,
instanceRange.length);
}
// Set up direct curve state.
@ -381,23 +437,29 @@ export abstract class Renderer {
this.setFramebufferSizeUniform(directCurveProgram.uniforms);
this.setHintsUniform(directCurveProgram.uniforms);
this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0);
this.pathTransformBufferTextures[objectIndex].bind(gl, directCurveProgram.uniforms, 1);
this.pathTransformBufferTextures[meshIndex].bind(gl, directCurveProgram.uniforms, 1);
if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directCurveProgram.uniforms, 2);
}
indexCount = meshData.coverCurveIndices.byteLength / UINT32_SIZE;
const coverCurveRange = getMeshIndexRange(meshes.coverCurveIndexRanges, pathRange);
if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
gl.drawElements(gl.TRIANGLES,
coverCurveRange.length,
gl.UNSIGNED_INT,
coverCurveRange.start * UINT32_SIZE);
} else {
renderContext.instancedArraysExt.drawElementsInstancedANGLE(gl.TRIANGLES,
indexCount,
coverCurveRange.length,
gl.UNSIGNED_INT,
0,
instanceRange.length);
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
// Finish direct rendering. Right now, this performs compositing if necessary.
antialiasingStrategy.finishDirectlyRenderingObject(this, objectIndex);
}
}
@ -444,7 +506,9 @@ export abstract class Renderer {
private initImplicitCoverCurveVAO(objectIndex: number, instanceRange: Range): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const meshes = this.meshes[objectIndex];
const meshIndex = this.meshIndexForObject(objectIndex);
const meshes = this.meshes[meshIndex];
const directCurveProgramName = this.directCurveProgramName();
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
@ -490,7 +554,9 @@ export abstract class Renderer {
private initImplicitCoverInteriorVAO(objectIndex: number, instanceRange: Range): void {
const renderContext = this.renderContext;
const gl = renderContext.gl;
const meshes = this.meshes[objectIndex];
const meshIndex = this.meshIndexForObject(objectIndex);
const meshes = this.meshes[meshIndex];
const directInteriorProgramName = this.directInteriorProgramName();
const directInteriorProgram = renderContext.shaderPrograms[directInteriorProgramName];
@ -543,3 +609,22 @@ export abstract class Renderer {
this.renderContext.gl.uniformMatrix4fv(uniforms.uTransform, false, transform);
}
}
function getMeshIndexRange(indexRanges: Range[], pathRange: Range): Range {
if (indexRanges.length === 0)
return new Range(0, 0);
let firstIndex;
if (pathRange.start - 1 >= indexRanges.length)
firstIndex = unwrapUndef(_.last(indexRanges)).end;
else
firstIndex = indexRanges[pathRange.start - 1].start;
let lastIndex;
if (pathRange.end - 1 >= indexRanges.length)
lastIndex = unwrapUndef(_.last(indexRanges)).end;
else
lastIndex = indexRanges[pathRange.end - 1].start;
return new Range(firstIndex, lastIndex);
}

View File

@ -13,6 +13,7 @@ import {expectNotNull, PathfinderError, unwrapNull} from './utils';
export interface ShaderMap<T> {
blit: T;
compositeAlphaMask: T;
demo3DDistantGlyph: T;
demo3DMonument: T;
directCurve: T;
@ -43,6 +44,7 @@ const COMMON_SHADER_URL: string = '/glsl/gles2/common.inc.glsl';
export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
'blit',
'compositeAlphaMask',
'directCurve',
'directInterior',
'direct3DCurve',
@ -69,6 +71,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
fragment: "/glsl/gles2/blit.fs.glsl",
vertex: "/glsl/gles2/blit.vs.glsl",
},
compositeAlphaMask: {
fragment: "/glsl/gles2/composite-alpha-mask.fs.glsl",
vertex: "/glsl/gles2/composite-alpha-mask.vs.glsl",
},
demo3DDistantGlyph: {
fragment: "/glsl/gles2/demo-3d-distant-glyph.fs.glsl",
vertex: "/glsl/gles2/demo-3d-distant-glyph.vs.glsl",

View File

@ -80,7 +80,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
return transform;
}
prepareForDirectRendering(renderer: Renderer) {
prepareForRendering(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -90,6 +90,18 @@ export default class SSAAStrategy extends AntialiasingStrategy {
gl.viewport(0, 0, framebufferSize[0], framebufferSize[1]);
gl.scissor(0, 0, usedSize[0], usedSize[1]);
gl.enable(gl.SCISSOR_TEST);
const clearColor = renderer.backgroundColor;
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
gl.clear(gl.COLOR_BUFFER_BIT);
}
prepareToDirectlyRenderObject(renderer: Renderer, objectIndex: number): void {
// TODO(pcwalton)
}
finishDirectlyRenderingObject(renderer: Renderer, objectIndex: number): void {
// TODO(pcwalton)
}
antialias(renderer: Renderer) {}

View File

@ -18,11 +18,12 @@ import PathfinderBufferTexture from "./buffer-texture";
import {OrthographicCamera} from "./camera";
import {UniformMap} from './gl-utils';
import {PathfinderMeshData} from "./meshes";
import {CompositingOperation, RenderTaskType} from './render-task';
import {Renderer} from './renderer';
import {ShaderMap, ShaderProgramSource} from './shader-loader';
import SSAAStrategy from "./ssaa-strategy";
import {BUILTIN_SVG_URI, SVGLoader} from './svg-loader';
import {panic, unwrapNull} from './utils';
import {panic, Range, unwrapNull} from './utils';
import {DemoView, Timings} from './view';
import {MCAAMulticolorStrategy, XCAAStrategy} from "./xcaa-strategy";
@ -127,10 +128,19 @@ class SVGDemoRenderer extends Renderer {
return this.destAllocatedSize;
}
protected get backgroundColor(): glmatrix.vec4 {
get usesIntermediateRenderTargets(): boolean {
return true;
}
get backgroundColor(): glmatrix.vec4 {
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
}
protected get objectCount(): number {
const loader = this.renderContext.appController.loader;
return loader.renderTasks.length;
}
constructor(renderContext: SVGDemoView) {
super(renderContext);
@ -159,8 +169,14 @@ class SVGDemoRenderer extends Renderer {
this.camera.zoomToFit();
}
protected get depthFunction(): GLenum {
return this.renderContext.gl.GREATER;
renderTaskTypeForObject(objectIndex: number): RenderTaskType {
const loader = this.renderContext.appController.loader;
return loader.renderTasks[objectIndex].type;
}
compositingOperationForObject(objectIndex: number): CompositingOperation | null {
const loader = this.renderContext.appController.loader;
return loader.renderTasks[objectIndex].compositingOperation;
}
protected get usedSizeFactor(): glmatrix.vec2 {
@ -193,6 +209,15 @@ class SVGDemoRenderer extends Renderer {
return 'directInterior';
}
protected meshIndexForObject(objectIndex: number): number {
return 0;
}
protected pathRangeForObject(objectIndex: number): Range {
const loader = this.renderContext.appController.loader;
return loader.renderTasks[objectIndex].instanceIndices;
}
protected newTimingsReceived(): void {
this.renderContext.appController.newTimingsReceived(this.lastTimings);
}

View File

@ -14,6 +14,7 @@ import * as _ from 'lodash';
import 'path-data-polyfill.js';
import {parseServerTiming, PathfinderMeshData} from "./meshes";
import {AlphaMaskCompositingOperation, RenderTask, RenderTaskType} from './render-task';
import {panic, Range, unwrapNull, unwrapUndef} from "./utils";
export const BUILTIN_SVG_URI: string = "/svg/demo";
@ -65,26 +66,12 @@ export class SVGStroke extends SVGPath {
}
}
export type SVGRenderTaskType = 'color' | 'clip';
export class SVGRenderTask {
type: SVGRenderTaskType;
instanceIndices: Range;
clipIndex: number | null;
constructor(type: SVGRenderTaskType, instanceIndices: Range, clipIndex?: number) {
this.type = type;
this.instanceIndices = instanceIndices;
this.clipIndex = clipIndex != null ? clipIndex : null;
}
}
interface ClipPathIDTable {
[id: string]: number;
}
export class SVGLoader {
renderTasks: SVGRenderTask[];
renderTasks: RenderTask[];
pathInstances: SVGPath[];
scale: number;
bounds: glmatrix.vec4;
@ -202,7 +189,8 @@ export class SVGLoader {
!this.clipPathIDs.hasOwnProperty(matches[1])) {
hasClip = false;
} else {
currentRenderTask.clipIndex = this.clipPathIDs[matches[1]];
currentRenderTask.compositingOperation =
new AlphaMaskCompositingOperation(this.clipPathIDs[matches[1]]);
}
}
@ -231,7 +219,7 @@ export class SVGLoader {
const currentRenderTask = unwrapUndef(_.last(this.renderTasks));
this.pathInstances.push(pathInstance);
currentRenderTask.instanceIndices.end = Math.max(currentRenderTask.instanceIndices.end,
this.pathInstances.length);
this.pathInstances.length + 1);
}
private popTopRenderTaskIfEmpty(): void {
@ -240,10 +228,10 @@ export class SVGLoader {
this.renderTasks.pop();
}
private pushNewRenderTask(taskType: SVGRenderTaskType): void {
private pushNewRenderTask(taskType: RenderTaskType): void {
this.popTopRenderTaskIfEmpty();
const emptyRange = new Range(this.pathInstances.length, this.pathInstances.length);
this.renderTasks.push(new SVGRenderTask(taskType, emptyRange));
const emptyRange = new Range(this.pathInstances.length + 1, this.pathInstances.length + 1);
this.renderTasks.push(new RenderTask(taskType, emptyRange));
}
}

View File

@ -109,10 +109,6 @@ export abstract class TextRenderer extends Renderer {
return glmatrix.vec2.create();
}
protected get depthFunction(): number {
return this.renderContext.gl.GREATER;
}
protected get usedSizeFactor(): glmatrix.vec2 {
const usedSize = glmatrix.vec2.create();
glmatrix.vec2.div(usedSize, this.renderContext.atlas.usedSize, ATLAS_SIZE);
@ -123,6 +119,10 @@ export abstract class TextRenderer extends Renderer {
return this.renderContext.glyphStore.glyphIDs.length * SUBPIXEL_GRANULARITY;
}
protected get objectCount(): number {
return this.meshes.length;
}
private stemDarkening: StemDarkeningMode;
private subpixelAA: SubpixelAAType;

View File

@ -60,16 +60,16 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.destFramebufferSize = glmatrix.vec2.create();
}
init(renderer: Renderer) {
init(renderer: Renderer): void {
super.init(renderer);
}
attachMeshes(renderer: Renderer) {
attachMeshes(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.createResolveVAO(renderer);
}
setFramebufferSize(renderer: Renderer) {
setFramebufferSize(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.destFramebufferSize = glmatrix.vec2.clone(renderer.destAllocatedSize);
@ -82,7 +82,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, null);
}
prepareForDirectRendering(renderer: Renderer) {
prepareForRendering(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -102,7 +102,16 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
antialias(renderer: Renderer) {
prepareToDirectlyRenderObject(renderer: Renderer, objectIndex: number): void {
// TODO(pcwalton)
this.prepareForRendering(renderer);
}
finishDirectlyRenderingObject(renderer: Renderer, objectIndex: number): void {
// TODO(pcwalton)
}
antialias(renderer: Renderer): void {
// Perform early preparations.
this.createPathBoundsBufferTexture(renderer);
@ -116,7 +125,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.clearForAA(renderer);
}
resolve(renderer: Renderer) {
resolve(renderer: Renderer): void {
// Resolve the antialiasing.
this.resolveAA(renderer);
}
@ -125,7 +134,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
return glmatrix.mat4.create();
}
protected initDirectFramebufferIfNecessary(renderer: Renderer) {
protected initDirectFramebufferIfNecessary(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -156,7 +165,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
renderContext.gl.enable(renderContext.gl.SCISSOR_TEST);
}
protected setAAState(renderer: Renderer) {
protected setAAState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const usedSize = this.supersampledUsedSize(renderer);
renderContext.gl.bindFramebuffer(renderContext.gl.FRAMEBUFFER, this.aaFramebuffer);
@ -170,7 +179,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.setAADepthState(renderer);
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap) {
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -183,7 +192,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
renderer.setHintsUniform(uniforms);
}
protected resolveAA(renderer: Renderer) {
protected resolveAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -238,7 +247,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
protected abstract setAADepthState(renderer: Renderer): void;
protected abstract clearForResolve(renderer: Renderer): void;
private initAAAlphaFramebuffer(renderer: Renderer) {
private initAAAlphaFramebuffer(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.aaAlphaTexture = unwrapNull(renderContext.gl.createTexture());
renderContext.gl.activeTexture(renderContext.gl.TEXTURE0);
@ -262,7 +271,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.aaDepthTexture);
}
private createPathBoundsBufferTexture(renderer: Renderer) {
private createPathBoundsBufferTexture(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const pathBounds = renderer.pathBoundingRects(0);
this.pathBoundsBufferTexture = new PathfinderBufferTexture(renderContext.gl,
@ -270,7 +279,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.pathBoundsBufferTexture.upload(renderContext.gl, pathBounds);
}
private createResolveVAO(renderer: Renderer) {
private createResolveVAO(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.resolveVAO = renderContext.vertexArrayObjectExt.createVertexArrayOES();
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.resolveVAO);
@ -296,7 +305,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
private lineVAOs: FastEdgeVAOs;
private curveVAOs: FastEdgeVAOs;
attachMeshes(renderer: Renderer) {
attachMeshes(renderer: Renderer): void {
super.attachMeshes(renderer);
this.createCoverVAO(renderer);
@ -304,7 +313,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
this.createCurveVAOs(renderer);
}
antialias(renderer: Renderer) {
antialias(renderer: Renderer): void {
super.antialias(renderer);
// Conservatively cover.
@ -391,7 +400,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private createCoverVAO(renderer: Renderer) {
private createCoverVAO(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.coverVAO = renderContext.vertexArrayObjectExt.createVertexArrayOES();
@ -445,7 +454,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private createLineVAOs(renderer: Renderer) {
private createLineVAOs(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const vaos: Partial<FastEdgeVAOs> = {};
const lineProgram = renderContext.shaderPrograms.mcaaLine;
@ -517,7 +526,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
this.lineVAOs = vaos as FastEdgeVAOs;
}
private createCurveVAOs(renderer: Renderer) {
private createCurveVAOs(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const vaos: Partial<FastEdgeVAOs> = {};
const curveProgram = renderContext.shaderPrograms.mcaaCurve;
@ -612,14 +621,14 @@ export abstract class MCAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private setBlendModeForAA(renderer: Renderer, direction: 'upper' | 'lower') {
private setBlendModeForAA(renderer: Renderer, direction: 'upper' | 'lower'): void {
const renderContext = renderer.renderContext;
renderContext.gl.blendEquation(renderContext.gl.FUNC_ADD);
renderContext.gl.blendFunc(renderContext.gl.ONE, renderContext.gl.ONE);
renderContext.gl.enable(renderContext.gl.BLEND);
}
private antialiasLines(renderer: Renderer) {
private antialiasLines(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
@ -630,7 +639,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
this.antialiasLinesWithProgram(renderer, lineProgram);
}
private antialiasCurves(renderer: Renderer) {
private antialiasCurves(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
@ -641,7 +650,6 @@ export abstract class MCAAStrategy extends XCAAStrategy {
this.antialiasCurvesWithProgram(renderer, curveProgram);
}
}
export class ECAAStrategy extends XCAAStrategy {
@ -652,14 +660,14 @@ export class ECAAStrategy extends XCAAStrategy {
return 'none';
}
attachMeshes(renderer: Renderer) {
attachMeshes(renderer: Renderer): void {
super.attachMeshes(renderer);
this.createLineVAO(renderer);
this.createCurveVAO(renderer);
}
antialias(renderer: Renderer) {
antialias(renderer: Renderer): void {
super.antialias(renderer);
// Antialias.
@ -667,7 +675,7 @@ export class ECAAStrategy extends XCAAStrategy {
this.antialiasCurves(renderer);
}
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap) {
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap): void {
super.setAAUniforms(renderer, uniforms);
const renderContext = renderer.renderContext;
@ -681,9 +689,9 @@ export class ECAAStrategy extends XCAAStrategy {
return renderContext.shaderPrograms.xcaaMonoResolve;
}
protected maskEdgesIfNecessary(renderer: Renderer) {}
protected maskEdgesIfNecessary(renderer: Renderer): void {}
protected clearForAA(renderer: Renderer) {
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0);
renderContext.gl.clearDepth(0.0);
@ -691,25 +699,25 @@ export class ECAAStrategy extends XCAAStrategy {
renderContext.gl.DEPTH_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer) {
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
}
protected clearForResolve(renderer: Renderer) {
protected clearForResolve(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0);
renderContext.gl.clear(renderContext.gl.COLOR_BUFFER_BIT);
}
private setBlendModeForAA(renderer: Renderer) {
private setBlendModeForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.blendEquation(renderContext.gl.FUNC_ADD);
renderContext.gl.blendFunc(renderContext.gl.ONE, renderContext.gl.ONE);
renderContext.gl.enable(renderContext.gl.BLEND);
}
private createLineVAO(renderer: Renderer) {
private createLineVAO(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const lineProgram = renderContext.shaderPrograms.ecaaLine;
@ -786,7 +794,7 @@ export class ECAAStrategy extends XCAAStrategy {
this.lineVAO = vao;
}
private createCurveVAO(renderer: Renderer) {
private createCurveVAO(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const curveProgram = renderContext.shaderPrograms.ecaaCurve;
@ -864,7 +872,7 @@ export class ECAAStrategy extends XCAAStrategy {
this.curveVAO = vao;
}
private antialiasLines(renderer: Renderer) {
private antialiasLines(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
@ -890,7 +898,7 @@ export class ECAAStrategy extends XCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
private antialiasCurves(renderer: Renderer) {
private antialiasCurves(renderer: Renderer): void {
const renderContext = renderer.renderContext;
this.setAAState(renderer);
@ -924,9 +932,9 @@ export class MCAAMonochromeStrategy extends MCAAStrategy {
return renderContext.shaderPrograms.xcaaMonoResolve;
}
protected maskEdgesIfNecessary(renderer: Renderer) {}
protected maskEdgesIfNecessary(renderer: Renderer): void {}
protected clearForAA(renderer: Renderer) {
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0);
renderContext.gl.clearDepth(0.0);
@ -934,12 +942,12 @@ export class MCAAMonochromeStrategy extends MCAAStrategy {
renderContext.gl.DEPTH_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer) {
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.disable(renderContext.gl.DEPTH_TEST);
}
protected clearForResolve(renderer: Renderer) {
protected clearForResolve(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.clearColor(0.0, 0.0, 0.0, 0.0);
renderContext.gl.clear(renderContext.gl.COLOR_BUFFER_BIT);
@ -985,8 +993,16 @@ export class AdaptiveMonochromeXCAAStrategy implements AntialiasingStrategy {
return this.mcaaStrategy.transform;
}
prepareForDirectRendering(renderer: Renderer): void {
this.getAppropriateStrategy(renderer).prepareForDirectRendering(renderer);
prepareForRendering(renderer: Renderer): void {
this.getAppropriateStrategy(renderer).prepareForRendering(renderer);
}
prepareToDirectlyRenderObject(renderer: Renderer, objectIndex: number): void {
this.getAppropriateStrategy(renderer).prepareToDirectlyRenderObject(renderer, objectIndex);
}
finishDirectlyRenderingObject(renderer: Renderer, objectIndex: number): void {
this.getAppropriateStrategy(renderer).finishDirectlyRenderingObject(renderer, objectIndex);
}
antialias(renderer: Renderer): void {
@ -1018,9 +1034,9 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
return renderContext.shaderPrograms.xcaaMultiResolve;
}
protected initDirectFramebufferIfNecessary(renderer: Renderer) {}
protected initDirectFramebufferIfNecessary(renderer: Renderer): void {}
protected maskEdgesIfNecessary(renderer: Renderer) {
protected maskEdgesIfNecessary(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -1053,14 +1069,14 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
renderContext.vertexArrayObjectExt.bindVertexArrayOES(null);
}
protected setCoverDepthState(renderer: Renderer) {
protected setCoverDepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
renderContext.gl.depthMask(false);
renderContext.gl.depthFunc(renderContext.gl.EQUAL);
renderContext.gl.enable(renderContext.gl.DEPTH_TEST);
}
protected clearForAA(renderer: Renderer) {
protected clearForAA(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -1068,7 +1084,7 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
gl.clear(gl.COLOR_BUFFER_BIT);
}
protected setAADepthState(renderer: Renderer) {
protected setAADepthState(renderer: Renderer): void {
const renderContext = renderer.renderContext;
const gl = renderContext.gl;
@ -1089,7 +1105,7 @@ export class MCAAMulticolorStrategy extends MCAAStrategy {
gl.enable(gl.BLEND);
}
protected clearForResolve(renderer: Renderer) {}
protected clearForResolve(renderer: Renderer): void {}
protected setAdditionalStateForResolveIfNecessary(renderer: Renderer,
resolveProgram: PathfinderShaderProgram,

View File

@ -0,0 +1,22 @@
// pathfinder/shaders/gles2/composite-alpha-mask.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.
precision mediump float;
uniform sampler2D uSource;
uniform sampler2D uMask;
varying vec2 vTexCoord;
void main() {
vec4 color = texture2D(uSource, vTexCoord);
float alpha = texture2D(uMask, vTexCoord).a;
gl_FragColor = vec4(color.rgb, color.a * alpha);
}

View File

@ -0,0 +1,26 @@
// pathfinder/shaders/gles2/composite-alpha-mask.vs.glsl
//
// Copyright (c) 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision mediump float;
/// A 3D transform to apply to the scene.
uniform mat4 uTransform;
/// The 2D vertex position.
attribute vec2 aPosition;
/// The texture coordinate.
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = uTransform * vec4(aPosition, 0.0, 1.0);
vTexCoord = aTexCoord;
}