Get edge detection working in the demo
This commit is contained in:
parent
b10807d217
commit
4b62ec8fa7
|
@ -9,6 +9,9 @@
|
||||||
<script type="text/javascript" src="js/bootstrap/bootstrap.js"></script>
|
<script type="text/javascript" src="js/bootstrap/bootstrap.js"></script>
|
||||||
<script type="text/javascript" src="js/pathfinder.js"></script>
|
<script type="text/javascript" src="js/pathfinder.js"></script>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background: lightslategray;
|
||||||
|
}
|
||||||
.pf-bottom-control {
|
.pf-bottom-control {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 1em;
|
bottom: 1em;
|
||||||
|
|
|
@ -14,10 +14,10 @@ const UINT32_SIZE: number = 4;
|
||||||
|
|
||||||
const B_POSITION_SIZE: number = 8;
|
const B_POSITION_SIZE: number = 8;
|
||||||
|
|
||||||
const B_VERTEX_QUAD_SIZE: number = 8;
|
const B_VERTEX_SIZE: number = 8;
|
||||||
const B_VERTEX_QUAD_PATH_ID_OFFSET: number = 0;
|
const B_VERTEX_PATH_ID_OFFSET: number = 0;
|
||||||
const B_VERTEX_QUAD_TEX_COORD_OFFSET: number = 4;
|
const B_VERTEX_TEX_COORD_OFFSET: number = 4;
|
||||||
const B_VERTEX_QUAD_SIGN_OFFSET: number = 6;
|
const B_VERTEX_SIGN_OFFSET: number = 6;
|
||||||
|
|
||||||
const IDENTITY: Matrix4D = [
|
const IDENTITY: Matrix4D = [
|
||||||
1.0, 0.0, 0.0, 0.0,
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
@ -43,6 +43,10 @@ const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
|
||||||
vertex: "/glsl/gles2/ecaa-edge-detect.vs.glsl",
|
vertex: "/glsl/gles2/ecaa-edge-detect.vs.glsl",
|
||||||
fragment: "/glsl/gles2/ecaa-edge-detect.fs.glsl",
|
fragment: "/glsl/gles2/ecaa-edge-detect.fs.glsl",
|
||||||
},
|
},
|
||||||
|
ecaaResolve: {
|
||||||
|
vertex: "/glsl/gles2/ecaa-resolve.vs.glsl",
|
||||||
|
fragment: "/glsl/gles2/ecaa-resolve.fs.glsl",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
interface UnlinkedShaderProgram {
|
interface UnlinkedShaderProgram {
|
||||||
|
@ -72,6 +76,7 @@ interface ShaderMap<T> {
|
||||||
directCurve: T;
|
directCurve: T;
|
||||||
directInterior: T;
|
directInterior: T;
|
||||||
ecaaEdgeDetect: T;
|
ecaaEdgeDetect: T;
|
||||||
|
ecaaResolve: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UniformMap {
|
interface UniformMap {
|
||||||
|
@ -150,6 +155,44 @@ class PathfinderError extends Error {
|
||||||
|
|
||||||
// GL utilities
|
// GL utilities
|
||||||
|
|
||||||
|
function createFramebufferColorTexture(gl: WebGLRenderingContext, size: Size2D): WebGLTexture {
|
||||||
|
// Firefox seems to have a bug whereby textures don't get marked as initialized when cleared
|
||||||
|
// if they're anything other than the first attachment of an FBO. To work around this, supply
|
||||||
|
// zero data explicitly when initializing the texture.
|
||||||
|
|
||||||
|
const texture = unwrapNull(gl.createTexture());
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
size.width,
|
||||||
|
size.height,
|
||||||
|
0,
|
||||||
|
gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE,
|
||||||
|
new Uint8Array(size.width * size.height * 4));
|
||||||
|
setTextureParameters(gl, gl.NEAREST);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFramebufferDepthTexture(gl: WebGLRenderingContext, size: Size2D): WebGLTexture {
|
||||||
|
const texture = unwrapNull(gl.createTexture());
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
gl.DEPTH_COMPONENT,
|
||||||
|
size.width,
|
||||||
|
size.height,
|
||||||
|
0,
|
||||||
|
gl.DEPTH_COMPONENT,
|
||||||
|
gl.UNSIGNED_INT,
|
||||||
|
null);
|
||||||
|
setTextureParameters(gl, gl.NEAREST);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
function setTextureParameters(gl: WebGLRenderingContext, filter: number) {
|
function setTextureParameters(gl: WebGLRenderingContext, filter: number) {
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||||
|
@ -157,6 +200,47 @@ function setTextureParameters(gl: WebGLRenderingContext, filter: number) {
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createFramebuffer(gl: WebGLRenderingContext,
|
||||||
|
drawBuffersExt: any,
|
||||||
|
colorAttachments: WebGLTexture[],
|
||||||
|
depthAttachment: WebGLTexture | null):
|
||||||
|
WebGLFramebuffer {
|
||||||
|
const framebuffer = unwrapNull(gl.createFramebuffer());
|
||||||
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
||||||
|
|
||||||
|
const colorAttachmentCount = colorAttachments.length;
|
||||||
|
for (let colorAttachmentIndex = 0;
|
||||||
|
colorAttachmentIndex < colorAttachmentCount;
|
||||||
|
colorAttachmentIndex++) {
|
||||||
|
gl.framebufferTexture2D(gl.FRAMEBUFFER,
|
||||||
|
drawBuffersExt[`COLOR_ATTACHMENT${colorAttachmentIndex}_WEBGL`],
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
colorAttachments[colorAttachmentIndex],
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depthAttachment != null) {
|
||||||
|
gl.framebufferTexture2D(gl.FRAMEBUFFER,
|
||||||
|
gl.DEPTH_ATTACHMENT,
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
depthAttachment,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE,
|
||||||
|
"Framebuffer was incomplete!");
|
||||||
|
return framebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initQuadVAO(view: PathfinderView, attributes: any) {
|
||||||
|
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadPositionsBuffer);
|
||||||
|
view.gl.vertexAttribPointer(attributes.aPosition, 2, view.gl.FLOAT, false, 0, 0);
|
||||||
|
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadTexCoordsBuffer);
|
||||||
|
view.gl.vertexAttribPointer(attributes.aTexCoord, 2, view.gl.FLOAT, false, 0, 0);
|
||||||
|
view.gl.enableVertexAttribArray(attributes.aPosition);
|
||||||
|
view.gl.enableVertexAttribArray(attributes.aTexCoord);
|
||||||
|
}
|
||||||
|
|
||||||
interface Meshes<T> {
|
interface Meshes<T> {
|
||||||
readonly bQuads: T;
|
readonly bQuads: T;
|
||||||
readonly bVertexPositions: T;
|
readonly bVertexPositions: T;
|
||||||
|
@ -446,12 +530,6 @@ class PathfinderView {
|
||||||
// Prepare for direct rendering.
|
// Prepare for direct rendering.
|
||||||
this.antialiasingStrategy.prepare(this);
|
this.antialiasingStrategy.prepare(this);
|
||||||
|
|
||||||
// Clear.
|
|
||||||
this.gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
|
||||||
this.gl.clearDepth(0.0);
|
|
||||||
this.gl.depthMask(true);
|
|
||||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
// Perform direct rendering (Loop-Blinn).
|
// Perform direct rendering (Loop-Blinn).
|
||||||
this.renderDirect(shaderPrograms);
|
this.renderDirect(shaderPrograms);
|
||||||
|
|
||||||
|
@ -464,10 +542,11 @@ class PathfinderView {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDirect(shaderPrograms: ShaderMap<PathfinderShaderProgram>) {
|
renderDirect(shaderPrograms: ShaderMap<PathfinderShaderProgram>) {
|
||||||
// Set up the depth buffer.
|
// Set up implicit cover state.
|
||||||
this.gl.depthFunc(this.gl.GREATER);
|
this.gl.depthFunc(this.gl.GREATER);
|
||||||
this.gl.depthMask(true);
|
this.gl.depthMask(true);
|
||||||
this.gl.enable(this.gl.DEPTH_TEST);
|
this.gl.enable(this.gl.DEPTH_TEST);
|
||||||
|
this.gl.disable(this.gl.BLEND);
|
||||||
|
|
||||||
// Set up the implicit cover interior VAO.
|
// Set up the implicit cover interior VAO.
|
||||||
const directInteriorProgram = shaderPrograms.directInterior;
|
const directInteriorProgram = shaderPrograms.directInterior;
|
||||||
|
@ -484,10 +563,10 @@ class PathfinderView {
|
||||||
1,
|
1,
|
||||||
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
|
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
|
||||||
false,
|
false,
|
||||||
B_VERTEX_QUAD_SIZE,
|
B_VERTEX_SIZE,
|
||||||
B_VERTEX_QUAD_PATH_ID_OFFSET);
|
B_VERTEX_PATH_ID_OFFSET);
|
||||||
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
|
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
|
||||||
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathDepth);
|
this.gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
|
||||||
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverInteriorIndices);
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverInteriorIndices);
|
||||||
|
|
||||||
// Draw direct interior parts.
|
// Draw direct interior parts.
|
||||||
|
@ -505,10 +584,13 @@ class PathfinderView {
|
||||||
this.gl.BUFFER_SIZE) / UINT32_SIZE;
|
this.gl.BUFFER_SIZE) / UINT32_SIZE;
|
||||||
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
|
this.gl.drawElements(this.gl.TRIANGLES, indexCount, this.gl.UNSIGNED_INT, 0);
|
||||||
|
|
||||||
// Disable depth writing.
|
// Set up direct curve state.
|
||||||
this.gl.depthMask(false);
|
this.gl.depthMask(false);
|
||||||
|
this.gl.enable(this.gl.BLEND);
|
||||||
|
this.gl.blendEquation(this.gl.FUNC_ADD);
|
||||||
|
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
// Set up the implicit cover curve VAO.
|
// Set up the direct curve VAO.
|
||||||
const directCurveProgram = shaderPrograms.directCurve;
|
const directCurveProgram = shaderPrograms.directCurve;
|
||||||
this.gl.useProgram(directCurveProgram.program);
|
this.gl.useProgram(directCurveProgram.program);
|
||||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.meshes.bVertexPositions);
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.meshes.bVertexPositions);
|
||||||
|
@ -523,23 +605,23 @@ class PathfinderView {
|
||||||
2,
|
2,
|
||||||
this.gl.UNSIGNED_BYTE,
|
this.gl.UNSIGNED_BYTE,
|
||||||
false,
|
false,
|
||||||
B_VERTEX_QUAD_SIZE,
|
B_VERTEX_SIZE,
|
||||||
B_VERTEX_QUAD_TEX_COORD_OFFSET);
|
B_VERTEX_TEX_COORD_OFFSET);
|
||||||
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
|
this.gl.vertexAttribPointer(directCurveProgram.attributes.aPathID,
|
||||||
1,
|
1,
|
||||||
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
|
this.gl.UNSIGNED_SHORT, // FIXME(pcwalton)
|
||||||
false,
|
false,
|
||||||
B_VERTEX_QUAD_SIZE,
|
B_VERTEX_SIZE,
|
||||||
B_VERTEX_QUAD_PATH_ID_OFFSET);
|
B_VERTEX_PATH_ID_OFFSET);
|
||||||
this.gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
|
this.gl.vertexAttribPointer(directCurveProgram.attributes.aSign,
|
||||||
1,
|
1,
|
||||||
this.gl.BYTE,
|
this.gl.BYTE,
|
||||||
false,
|
false,
|
||||||
B_VERTEX_QUAD_SIZE,
|
B_VERTEX_SIZE,
|
||||||
B_VERTEX_QUAD_SIGN_OFFSET);
|
B_VERTEX_SIGN_OFFSET);
|
||||||
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
|
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
|
||||||
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
|
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
|
||||||
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathDepth);
|
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
|
||||||
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
|
this.gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
|
||||||
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverCurveIndices);
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.meshes.coverCurveIndices);
|
||||||
|
|
||||||
|
@ -639,6 +721,12 @@ class NoAAStrategy implements AntialiasingStrategy {
|
||||||
|
|
||||||
prepare(view: PathfinderView) {
|
prepare(view: PathfinderView) {
|
||||||
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
|
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
|
||||||
|
|
||||||
|
// Clear.
|
||||||
|
view.gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
||||||
|
view.gl.clearDepth(0.0);
|
||||||
|
view.gl.depthMask(true);
|
||||||
|
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {}
|
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {}
|
||||||
|
@ -661,6 +749,7 @@ class SSAAStrategy implements AntialiasingStrategy {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.supersampledColorTexture = unwrapNull(view.gl.createTexture());
|
this.supersampledColorTexture = unwrapNull(view.gl.createTexture());
|
||||||
|
view.gl.activeTexture(view.gl.TEXTURE0);
|
||||||
view.gl.bindTexture(view.gl.TEXTURE_2D, this.supersampledColorTexture);
|
view.gl.bindTexture(view.gl.TEXTURE_2D, this.supersampledColorTexture);
|
||||||
view.gl.texImage2D(view.gl.TEXTURE_2D,
|
view.gl.texImage2D(view.gl.TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
|
@ -673,33 +762,13 @@ class SSAAStrategy implements AntialiasingStrategy {
|
||||||
null);
|
null);
|
||||||
setTextureParameters(view.gl, view.gl.LINEAR);
|
setTextureParameters(view.gl, view.gl.LINEAR);
|
||||||
|
|
||||||
this.supersampledDepthTexture = unwrapNull(view.gl.createTexture());
|
this.supersampledDepthTexture =
|
||||||
view.gl.bindTexture(view.gl.TEXTURE_2D, this.supersampledDepthTexture);
|
createFramebufferDepthTexture(view.gl, this.supersampledFramebufferSize);
|
||||||
view.gl.texImage2D(view.gl.TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
view.gl.DEPTH_COMPONENT,
|
|
||||||
this.supersampledFramebufferSize.width,
|
|
||||||
this.supersampledFramebufferSize.height,
|
|
||||||
0,
|
|
||||||
view.gl.DEPTH_COMPONENT,
|
|
||||||
view.gl.UNSIGNED_INT,
|
|
||||||
null);
|
|
||||||
setTextureParameters(view.gl, view.gl.NEAREST);
|
|
||||||
|
|
||||||
this.supersampledFramebuffer = unwrapNull(view.gl.createFramebuffer());
|
this.supersampledFramebuffer = createFramebuffer(view.gl,
|
||||||
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer);
|
view.drawBuffersExt,
|
||||||
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
|
[this.supersampledColorTexture],
|
||||||
view.gl.COLOR_ATTACHMENT0,
|
this.supersampledDepthTexture);
|
||||||
view.gl.TEXTURE_2D,
|
|
||||||
this.supersampledColorTexture,
|
|
||||||
0);
|
|
||||||
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
|
|
||||||
view.gl.DEPTH_ATTACHMENT,
|
|
||||||
view.gl.TEXTURE_2D,
|
|
||||||
this.supersampledDepthTexture,
|
|
||||||
0);
|
|
||||||
assert(view.gl.checkFramebufferStatus(view.gl.FRAMEBUFFER) == view.gl.FRAMEBUFFER_COMPLETE,
|
|
||||||
"The SSAA framebuffer was incomplete!");
|
|
||||||
|
|
||||||
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
|
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
|
||||||
}
|
}
|
||||||
|
@ -708,6 +777,12 @@ class SSAAStrategy implements AntialiasingStrategy {
|
||||||
const size = this.supersampledFramebufferSize;
|
const size = this.supersampledFramebufferSize;
|
||||||
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer);
|
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.supersampledFramebuffer);
|
||||||
view.gl.viewport(0, 0, size.width, size.height);
|
view.gl.viewport(0, 0, size.width, size.height);
|
||||||
|
|
||||||
|
// Clear.
|
||||||
|
view.gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
||||||
|
view.gl.clearDepth(0.0);
|
||||||
|
view.gl.depthMask(true);
|
||||||
|
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {
|
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {
|
||||||
|
@ -718,22 +793,7 @@ class SSAAStrategy implements AntialiasingStrategy {
|
||||||
// Set up the blit program VAO.
|
// Set up the blit program VAO.
|
||||||
const blitProgram = shaders.blit;
|
const blitProgram = shaders.blit;
|
||||||
view.gl.useProgram(blitProgram.program);
|
view.gl.useProgram(blitProgram.program);
|
||||||
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadPositionsBuffer);
|
initQuadVAO(view, blitProgram.attributes);
|
||||||
view.gl.vertexAttribPointer(blitProgram.attributes.aPosition,
|
|
||||||
2,
|
|
||||||
view.gl.FLOAT,
|
|
||||||
false,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
view.gl.bindBuffer(view.gl.ARRAY_BUFFER, view.quadTexCoordsBuffer);
|
|
||||||
view.gl.vertexAttribPointer(blitProgram.attributes.aTexCoord,
|
|
||||||
2,
|
|
||||||
view.gl.FLOAT,
|
|
||||||
false,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
view.gl.enableVertexAttribArray(blitProgram.attributes.aPosition);
|
|
||||||
view.gl.enableVertexAttribArray(blitProgram.attributes.aTexCoord);
|
|
||||||
|
|
||||||
// Resolve framebuffer.
|
// Resolve framebuffer.
|
||||||
view.gl.activeTexture(view.gl.TEXTURE0);
|
view.gl.activeTexture(view.gl.TEXTURE0);
|
||||||
|
@ -757,82 +817,129 @@ class ECAAStrategy implements AntialiasingStrategy {
|
||||||
|
|
||||||
init(view: PathfinderView, framebufferSize: Size2D) {
|
init(view: PathfinderView, framebufferSize: Size2D) {
|
||||||
this.framebufferSize = framebufferSize;
|
this.framebufferSize = framebufferSize;
|
||||||
|
this.initDirectFramebuffer(view);
|
||||||
this.directColorTexture = unwrapNull(view.gl.createTexture());
|
this.initEdgeDetectFramebuffer(view);
|
||||||
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directColorTexture);
|
|
||||||
view.gl.texImage2D(view.gl.TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
view.gl.RGBA,
|
|
||||||
framebufferSize.width,
|
|
||||||
framebufferSize.height,
|
|
||||||
0,
|
|
||||||
view.gl.RGBA,
|
|
||||||
view.gl.UNSIGNED_BYTE,
|
|
||||||
null);
|
|
||||||
setTextureParameters(view.gl, view.gl.NEAREST);
|
|
||||||
|
|
||||||
this.directPathIDTexture = unwrapNull(view.gl.createTexture());
|
|
||||||
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directPathIDTexture);
|
|
||||||
view.gl.texImage2D(view.gl.TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
view.gl.RGBA, // really should be RG
|
|
||||||
framebufferSize.width,
|
|
||||||
framebufferSize.height,
|
|
||||||
0,
|
|
||||||
view.gl.RGBA,
|
|
||||||
view.gl.UNSIGNED_BYTE,
|
|
||||||
null);
|
|
||||||
setTextureParameters(view.gl, view.gl.NEAREST);
|
|
||||||
|
|
||||||
this.directDepthTexture = unwrapNull(view.gl.createTexture());
|
|
||||||
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directDepthTexture);
|
|
||||||
view.gl.texImage2D(view.gl.TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
view.gl.DEPTH_COMPONENT,
|
|
||||||
framebufferSize.width,
|
|
||||||
framebufferSize.height,
|
|
||||||
0,
|
|
||||||
view.gl.DEPTH_COMPONENT,
|
|
||||||
view.gl.UNSIGNED_INT,
|
|
||||||
null);
|
|
||||||
setTextureParameters(view.gl, view.gl.NEAREST);
|
|
||||||
|
|
||||||
this.directFramebuffer = unwrapNull(view.gl.createFramebuffer());
|
|
||||||
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer);
|
|
||||||
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
|
|
||||||
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
|
|
||||||
view.gl.TEXTURE_2D,
|
|
||||||
this.directColorTexture,
|
|
||||||
0);
|
|
||||||
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
|
|
||||||
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
|
|
||||||
view.gl.TEXTURE_2D,
|
|
||||||
this.directPathIDTexture,
|
|
||||||
0);
|
|
||||||
view.gl.framebufferTexture2D(view.gl.FRAMEBUFFER,
|
|
||||||
view.gl.DEPTH_ATTACHMENT,
|
|
||||||
view.gl.TEXTURE_2D,
|
|
||||||
this.directDepthTexture,
|
|
||||||
0);
|
|
||||||
assert(view.gl.checkFramebufferStatus(view.gl.FRAMEBUFFER) == view.gl.FRAMEBUFFER_COMPLETE,
|
|
||||||
"The direct framebuffer was incomplete!");
|
|
||||||
|
|
||||||
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
|
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initDirectFramebuffer(view: PathfinderView) {
|
||||||
|
this.directColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
|
||||||
|
this.directPathIDTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
|
||||||
|
this.directDepthTexture = createFramebufferDepthTexture(view.gl, this.framebufferSize);
|
||||||
|
this.directFramebuffer =
|
||||||
|
createFramebuffer(view.gl,
|
||||||
|
view.drawBuffersExt,
|
||||||
|
[this.directColorTexture, this.directPathIDTexture],
|
||||||
|
this.directDepthTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
initEdgeDetectFramebuffer(view: PathfinderView) {
|
||||||
|
this.bgColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
|
||||||
|
this.fgColorTexture = createFramebufferColorTexture(view.gl, this.framebufferSize);
|
||||||
|
this.aaDepthTexture = createFramebufferDepthTexture(view.gl, this.framebufferSize);
|
||||||
|
this.edgeDetectFramebuffer = createFramebuffer(view.gl,
|
||||||
|
view.drawBuffersExt,
|
||||||
|
[this.bgColorTexture, this.fgColorTexture],
|
||||||
|
this.aaDepthTexture);
|
||||||
|
}
|
||||||
|
|
||||||
prepare(view: PathfinderView) {
|
prepare(view: PathfinderView) {
|
||||||
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer);
|
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.directFramebuffer);
|
||||||
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
|
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
|
||||||
|
|
||||||
|
// Clear out the color and depth textures.
|
||||||
|
view.drawBuffersExt.drawBuffersWEBGL([
|
||||||
|
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
|
||||||
|
view.drawBuffersExt.NONE,
|
||||||
|
]);
|
||||||
|
view.gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
||||||
|
view.gl.clearDepth(0.0);
|
||||||
|
view.gl.depthMask(true);
|
||||||
|
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Clear out the path ID texture.
|
||||||
|
view.drawBuffersExt.drawBuffersWEBGL([
|
||||||
|
view.drawBuffersExt.NONE,
|
||||||
|
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
|
||||||
|
]);
|
||||||
|
view.gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
view.gl.clear(view.gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Render to both textures.
|
||||||
|
view.drawBuffersExt.drawBuffersWEBGL([
|
||||||
|
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
|
||||||
|
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {
|
resolve(view: PathfinderView, shaders: ShaderMap<PathfinderShaderProgram>) {
|
||||||
// TODO(pcwalton)
|
// Set state for edge detection.
|
||||||
|
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, this.edgeDetectFramebuffer);
|
||||||
|
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
|
||||||
|
|
||||||
|
view.drawBuffersExt.drawBuffersWEBGL([
|
||||||
|
view.drawBuffersExt.COLOR_ATTACHMENT0_WEBGL,
|
||||||
|
view.drawBuffersExt.COLOR_ATTACHMENT1_WEBGL,
|
||||||
|
]);
|
||||||
|
|
||||||
|
view.gl.depthMask(true);
|
||||||
|
view.gl.depthFunc(view.gl.ALWAYS);
|
||||||
|
view.gl.enable(view.gl.DEPTH_TEST);
|
||||||
|
view.gl.disable(view.gl.BLEND);
|
||||||
|
|
||||||
|
view.gl.clearDepth(0.0);
|
||||||
|
view.gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
view.gl.clear(view.gl.COLOR_BUFFER_BIT | view.gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Set up the edge detection VAO.
|
||||||
|
const edgeDetectProgram = shaders.ecaaEdgeDetect;
|
||||||
|
view.gl.useProgram(edgeDetectProgram.program);
|
||||||
|
initQuadVAO(view, edgeDetectProgram.attributes);
|
||||||
|
|
||||||
|
// Perform edge detection.
|
||||||
|
view.gl.uniform2i(edgeDetectProgram.uniforms.uFramebufferSize,
|
||||||
|
this.framebufferSize.width,
|
||||||
|
this.framebufferSize.height);
|
||||||
|
view.gl.activeTexture(view.gl.TEXTURE0);
|
||||||
|
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directColorTexture);
|
||||||
|
view.gl.uniform1i(edgeDetectProgram.uniforms.uColor, 0);
|
||||||
|
view.gl.activeTexture(view.gl.TEXTURE1);
|
||||||
|
view.gl.bindTexture(view.gl.TEXTURE_2D, this.directPathIDTexture);
|
||||||
|
view.gl.uniform1i(edgeDetectProgram.uniforms.uPathID, 1);
|
||||||
|
view.gl.drawArrays(view.gl.TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
// Set state for ECAA resolve.
|
||||||
|
view.gl.bindFramebuffer(view.gl.FRAMEBUFFER, null);
|
||||||
|
view.gl.viewport(0, 0, this.framebufferSize.width, this.framebufferSize.height);
|
||||||
|
view.gl.disable(view.gl.DEPTH_TEST);
|
||||||
|
view.drawBuffersExt.drawBuffersWEBGL([view.gl.BACK]);
|
||||||
|
|
||||||
|
// Set up the ECAA resolve VAO.
|
||||||
|
const resolveProgram = shaders.ecaaResolve;
|
||||||
|
view.gl.useProgram(resolveProgram.program);
|
||||||
|
initQuadVAO(view, resolveProgram.attributes);
|
||||||
|
|
||||||
|
// Perform ECAA resolution.
|
||||||
|
view.gl.uniform2i(resolveProgram.uniforms.uFramebufferSize,
|
||||||
|
this.framebufferSize.width,
|
||||||
|
this.framebufferSize.height);
|
||||||
|
view.gl.activeTexture(view.gl.TEXTURE0);
|
||||||
|
view.gl.bindTexture(view.gl.TEXTURE_2D, this.bgColorTexture);
|
||||||
|
view.gl.uniform1i(resolveProgram.uniforms.uBGColor, 0);
|
||||||
|
view.gl.activeTexture(view.gl.TEXTURE1);
|
||||||
|
view.gl.bindTexture(view.gl.TEXTURE_2D, this.fgColorTexture);
|
||||||
|
view.gl.uniform1i(resolveProgram.uniforms.uFGColor, 1);
|
||||||
|
view.gl.drawArrays(view.gl.TRIANGLE_STRIP, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
directColorTexture: WebGLTexture;
|
directColorTexture: WebGLTexture;
|
||||||
directPathIDTexture: WebGLTexture;
|
directPathIDTexture: WebGLTexture;
|
||||||
directDepthTexture: WebGLTexture;
|
directDepthTexture: WebGLTexture;
|
||||||
directFramebuffer: WebGLFramebuffer;
|
directFramebuffer: WebGLFramebuffer;
|
||||||
|
bgColorTexture: WebGLTexture;
|
||||||
|
fgColorTexture: WebGLTexture;
|
||||||
|
aaDepthTexture: WebGLTexture;
|
||||||
|
edgeDetectFramebuffer: WebGLFramebuffer;
|
||||||
framebufferSize: Size2D;
|
framebufferSize: Size2D;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ fn partition_font(request: Json<PartitionFontRequest>)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
partitioner.partition(path_index as u32,
|
partitioner.partition((path_index + 1) as u32,
|
||||||
decoded_outline_indices.subpath_indices.start as u32,
|
decoded_outline_indices.subpath_indices.start as u32,
|
||||||
decoded_outline_indices.subpath_indices.end as u32);
|
decoded_outline_indices.subpath_indices.end as u32);
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ vec2 convertScreenToClipSpace(vec2 position, ivec2 framebufferSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float convertPathIndexToDepthValue(int pathIndex) {
|
float convertPathIndexToDepthValue(int pathIndex) {
|
||||||
return float(pathIndex + 1) / float(MAX_PATHS);
|
return mix(-1.0, 1.0, float(pathIndex) / float(MAX_PATHS));
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 fetchFloat4Data(sampler2D dataTexture, int index, ivec2 dimensions) {
|
vec4 fetchFloat4Data(sampler2D dataTexture, int index, ivec2 dimensions) {
|
||||||
|
@ -42,3 +42,8 @@ vec4 fetchFloat4NormIndexedData(sampler2D dataTexture, float normIndex, ivec2 di
|
||||||
vec2 packPathID(int pathID) {
|
vec2 packPathID(int pathID) {
|
||||||
return vec2(imod(pathID, 256), pathID / 256) / 255.0;
|
return vec2(imod(pathID, 256), pathID / 256) / 255.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int unpackPathID(vec2 packedPathID) {
|
||||||
|
ivec2 pathIDBytes = ivec2(floor(packedPathID * 255.0));
|
||||||
|
return pathIDBytes.y * 256 + pathIDBytes.x;
|
||||||
|
}
|
||||||
|
|
|
@ -17,5 +17,5 @@ void main() {
|
||||||
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
|
float side = vTexCoord.x * vTexCoord.x - vTexCoord.y;
|
||||||
float alpha = float(sign(side) == sign(vSign));
|
float alpha = float(sign(side) == sign(vSign));
|
||||||
gl_FragData[0] = vec4(vColor.rgb, vColor.a * alpha);
|
gl_FragData[0] = vec4(vColor.rgb, vColor.a * alpha);
|
||||||
gl_FragData[1] = vec4(vPathID, 1.0, 1.0);
|
gl_FragData[1] = vec4(vPathID, 0.0, alpha);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,15 @@ precision highp float;
|
||||||
|
|
||||||
uniform ivec2 uFramebufferSize;
|
uniform ivec2 uFramebufferSize;
|
||||||
uniform sampler2D uColor;
|
uniform sampler2D uColor;
|
||||||
uniform sampler2D uDepth;
|
uniform sampler2D uPathID;
|
||||||
|
|
||||||
varying vec2 vTexCoord;
|
varying vec2 vTexCoord;
|
||||||
|
|
||||||
vec3 checkFG(vec3 fgPosition, vec3 queryPosition) {
|
void checkFG(out vec2 fgPosition, out int fgPathID, vec2 queryPosition, int queryPathID) {
|
||||||
return queryPosition.z > fgPosition.z ? queryPosition : fgPosition;
|
if (queryPathID > fgPathID) {
|
||||||
|
fgPosition = queryPosition;
|
||||||
|
fgPathID = queryPathID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -22,47 +25,48 @@ void main() {
|
||||||
vec2 onePixel = 1.0 / vec2(uFramebufferSize);
|
vec2 onePixel = 1.0 / vec2(uFramebufferSize);
|
||||||
vec2 positionL = position + vec2(-onePixel.x, 0.0);
|
vec2 positionL = position + vec2(-onePixel.x, 0.0);
|
||||||
vec2 positionR = position + vec2( onePixel.x, 0.0);
|
vec2 positionR = position + vec2( onePixel.x, 0.0);
|
||||||
vec2 positionB = position + vec2(0.0, -onePixel.x);
|
vec2 positionB = position + vec2(0.0, -onePixel.y);
|
||||||
vec2 positionT = position + vec2(0.0, onePixel.x);
|
vec2 positionT = position + vec2(0.0, onePixel.y);
|
||||||
|
|
||||||
// Determine the topmost path.
|
// Determine the topmost path.
|
||||||
float centerDepth = texture2D(uDepth, position).r;
|
int centerPathID = unpackPathID(texture2D(uPathID, position).rg);
|
||||||
vec4 neighborDepths = vec4(texture2D(uDepth, positionL).r,
|
ivec4 neighborPathIDs = ivec4(unpackPathID(texture2D(uPathID, positionL).rg),
|
||||||
texture2D(uDepth, positionR).r,
|
unpackPathID(texture2D(uPathID, positionR).rg),
|
||||||
texture2D(uDepth, positionT).r,
|
unpackPathID(texture2D(uPathID, positionB).rg),
|
||||||
texture2D(uDepth, positionB).r);
|
unpackPathID(texture2D(uPathID, positionT).rg));
|
||||||
|
|
||||||
// Determine the position of the foreground color.
|
// Determine the position of the foreground color.
|
||||||
vec3 fgPosition = vec3(position, centerDepth);
|
vec2 fgPosition = position;
|
||||||
fgPosition = checkFG(fgPosition, vec3(positionL, neighborDepths.x));
|
int fgPathID = centerPathID;
|
||||||
fgPosition = checkFG(fgPosition, vec3(positionR, neighborDepths.y));
|
checkFG(fgPosition, fgPathID, positionL, neighborPathIDs.x);
|
||||||
fgPosition = checkFG(fgPosition, vec3(positionT, neighborDepths.z));
|
checkFG(fgPosition, fgPathID, positionR, neighborPathIDs.y);
|
||||||
fgPosition = checkFG(fgPosition, vec3(positionB, neighborDepths.w));
|
checkFG(fgPosition, fgPathID, positionB, neighborPathIDs.z);
|
||||||
|
checkFG(fgPosition, fgPathID, positionT, neighborPathIDs.w);
|
||||||
|
|
||||||
// Determine the position of the background color.
|
// Determine the position of the background color.
|
||||||
vec2 bgPosition;
|
vec2 bgPosition;
|
||||||
if (fgPosition.z != centerDepth)
|
if (fgPathID != centerPathID)
|
||||||
bgPosition = fgPosition.xy;
|
bgPosition = position;
|
||||||
else if (fgPosition.z != neighborDepths.x)
|
else if (fgPathID != neighborPathIDs.x)
|
||||||
bgPosition = positionL;
|
bgPosition = positionL;
|
||||||
else if (fgPosition.z != neighborDepths.y)
|
else if (fgPathID != neighborPathIDs.y)
|
||||||
bgPosition = positionR;
|
bgPosition = positionR;
|
||||||
else if (fgPosition.z != neighborDepths.z)
|
else if (fgPathID != neighborPathIDs.z)
|
||||||
bgPosition = positionT;
|
|
||||||
else
|
|
||||||
bgPosition = positionB;
|
bgPosition = positionB;
|
||||||
|
else
|
||||||
|
bgPosition = positionT;
|
||||||
|
|
||||||
// Determine the foreground and background colors.
|
// Determine the foreground and background colors.
|
||||||
vec4 fgColor = texture2D(uColor, fgPosition.st);
|
vec4 fgColor = texture2D(uColor, fgPosition);
|
||||||
vec4 bgColor = texture2D(uColor, bgPosition);
|
vec4 bgColor = texture2D(uColor, bgPosition);
|
||||||
|
|
||||||
// Determine the depth.
|
// Determine the depth.
|
||||||
//
|
//
|
||||||
// If all colors are the same, avoid touching this pixel in any further passes.
|
// If all colors are the same, avoid touching this pixel in any further passes.
|
||||||
float outDepth = fgColor == bgColor ? 0.0 : fgPosition.z;
|
float outDepth = fgColor == bgColor ? -1.0 : convertPathIndexToDepthValue(fgPathID);
|
||||||
|
|
||||||
// Output results.
|
// Output results.
|
||||||
gl_FragData[0] = fgColor;
|
gl_FragData[0] = bgColor;
|
||||||
gl_FragData[1] = bgColor;
|
gl_FragData[1] = fgColor;
|
||||||
gl_FragDepthEXT = outDepth;
|
gl_FragDepthEXT = outDepth;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// pathfinder/shaders/gles2/ecaa-resolve.fs.glsl
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Mozilla Foundation
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
uniform ivec2 uFramebufferSize;
|
||||||
|
uniform sampler2D uBGColor;
|
||||||
|
uniform sampler2D uFGColor;
|
||||||
|
|
||||||
|
varying vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 bgColor = texture2D(uBGColor, vTexCoord);
|
||||||
|
vec4 fgColor = texture2D(uFGColor, vTexCoord);
|
||||||
|
gl_FragColor = bgColor != fgColor ? vec4(1.0, 0.0, 0.0, 1.0) : bgColor;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// pathfinder/shaders/gles2/ecaa-resolve.vs.glsl
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Mozilla Foundation
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
attribute vec2 aPosition;
|
||||||
|
attribute vec2 aTexCoord;
|
||||||
|
|
||||||
|
varying vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(aPosition, 0.0, 1.0);
|
||||||
|
vTexCoord = aTexCoord;
|
||||||
|
}
|
Loading…
Reference in New Issue