Support stem darkening in SSAA mode.

The normals are incorrect right now in some cases, but it looks OK-ish.
This commit is contained in:
Patrick Walton 2017-11-09 16:20:15 -08:00
parent 35e59b1a05
commit 8e7eb6ca60
9 changed files with 110 additions and 29 deletions

1
.gitignore vendored
View File

@ -23,3 +23,4 @@ Cargo.lock
# VSCode # VSCode
.vscode .vscode
*.code-workspace

View File

@ -403,7 +403,9 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer>, MeshDataCounts,
const expandedPathID = newPathIndex + 1; const expandedPathID = newPathIndex + 1;
const originalPathID = pathIDs[newPathIndex]; const originalPathID = pathIDs[newPathIndex];
const bVertexCopyResult = copyVertices(['bVertexPositions', 'bVertexLoopBlinnData'], const bVertexCopyResult = copyVertices(['bVertexPositions',
'bVertexLoopBlinnData',
'bVertexNormals'],
'bVertexPathRanges', 'bVertexPathRanges',
expandedArrays, expandedArrays,
expandedRanges, expandedRanges,

View File

@ -285,6 +285,12 @@ export abstract class Renderer {
this.pathColorsBufferTextures[meshIndex].bind(gl, uniforms, textureUnit); this.pathColorsBufferTextures[meshIndex].bind(gl, uniforms, textureUnit);
} }
setEmboldenAmountUniform(objectIndex: number, uniforms: UniformMap): void {
const gl = this.renderContext.gl;
const emboldenAmount = this.emboldenAmount;
gl.uniform2f(uniforms.uEmboldenAmount, emboldenAmount[0], emboldenAmount[1]);
}
renderTaskTypeForObject(objectIndex: number): RenderTaskType { renderTaskTypeForObject(objectIndex: number): RenderTaskType {
return 'color'; return 'color';
} }
@ -420,14 +426,14 @@ export abstract class Renderer {
this.setFramebufferSizeUniform(directInteriorProgram.uniforms); this.setFramebufferSizeUniform(directInteriorProgram.uniforms);
this.setHintsUniform(directInteriorProgram.uniforms); this.setHintsUniform(directInteriorProgram.uniforms);
this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0); this.setPathColorsUniform(objectIndex, directInteriorProgram.uniforms, 0);
this.setEmboldenAmountUniform(objectIndex, directInteriorProgram.uniforms);
this.pathTransformBufferTextures[meshIndex] this.pathTransformBufferTextures[meshIndex]
.bind(gl, directInteriorProgram.uniforms, 1); .bind(gl, directInteriorProgram.uniforms, 1);
if (renderingMode === 'color-depth') { if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy; const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 2); strategy.bindEdgeDepthTexture(gl, directInteriorProgram.uniforms, 2);
} }
const coverInteriorRange = getMeshIndexRange(meshes.coverInteriorIndexRanges, const coverInteriorRange = getMeshIndexRange(meshes.coverInteriorIndexRanges, pathRange);
pathRange);
if (!this.pathIDsAreInstanced) { if (!this.pathIDsAreInstanced) {
gl.drawElements(gl.TRIANGLES, gl.drawElements(gl.TRIANGLES,
coverInteriorRange.length, coverInteriorRange.length,
@ -458,10 +464,8 @@ export abstract class Renderer {
// TODO(pcwalton): Cache these. // TODO(pcwalton): Cache these.
const directCurveProgramName = this.directCurveProgramName(); const directCurveProgramName = this.directCurveProgramName();
const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName]; const directCurveProgram = renderContext.shaderPrograms[directCurveProgramName];
if (this.implicitCoverCurveVAO == null) { if (this.implicitCoverCurveVAO == null)
this.implicitCoverCurveVAO = renderContext.vertexArrayObjectExt this.implicitCoverCurveVAO = renderContext.vertexArrayObjectExt.createVertexArrayOES();
.createVertexArrayOES();
}
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverCurveVAO); renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.implicitCoverCurveVAO);
this.initImplicitCoverCurveVAO(objectIndex, instanceRange); this.initImplicitCoverCurveVAO(objectIndex, instanceRange);
@ -470,6 +474,7 @@ export abstract class Renderer {
this.setFramebufferSizeUniform(directCurveProgram.uniforms); this.setFramebufferSizeUniform(directCurveProgram.uniforms);
this.setHintsUniform(directCurveProgram.uniforms); this.setHintsUniform(directCurveProgram.uniforms);
this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0); this.setPathColorsUniform(objectIndex, directCurveProgram.uniforms, 0);
this.setEmboldenAmountUniform(objectIndex, directCurveProgram.uniforms);
this.pathTransformBufferTextures[meshIndex].bind(gl, directCurveProgram.uniforms, 1); this.pathTransformBufferTextures[meshIndex].bind(gl, directCurveProgram.uniforms, 1);
if (renderingMode === 'color-depth') { if (renderingMode === 'color-depth') {
const strategy = antialiasingStrategy as MCAAMulticolorStrategy; const strategy = antialiasingStrategy as MCAAMulticolorStrategy;
@ -592,10 +597,18 @@ export abstract class Renderer {
false, false,
B_LOOP_BLINN_DATA_SIZE, B_LOOP_BLINN_DATA_SIZE,
B_LOOP_BLINN_DATA_SIGN_OFFSET); B_LOOP_BLINN_DATA_SIGN_OFFSET);
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexNormals);
gl.vertexAttribPointer(directCurveProgram.attributes.aNormalAngle,
1,
gl.FLOAT,
false,
FLOAT32_SIZE,
0);
gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition); gl.enableVertexAttribArray(directCurveProgram.attributes.aPosition);
gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord); gl.enableVertexAttribArray(directCurveProgram.attributes.aTexCoord);
gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID); gl.enableVertexAttribArray(directCurveProgram.attributes.aPathID);
gl.enableVertexAttribArray(directCurveProgram.attributes.aSign); gl.enableVertexAttribArray(directCurveProgram.attributes.aSign);
gl.enableVertexAttribArray(directCurveProgram.attributes.aNormalAngle);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverCurveIndices);
} }
@ -632,8 +645,17 @@ export abstract class Renderer {
.vertexAttribDivisorANGLE(directInteriorProgram.attributes.aPathID, 1); .vertexAttribDivisorANGLE(directInteriorProgram.attributes.aPathID, 1);
} }
gl.bindBuffer(gl.ARRAY_BUFFER, meshes.bVertexNormals);
gl.vertexAttribPointer(directInteriorProgram.attributes.aNormalAngle,
1,
gl.FLOAT,
false,
FLOAT32_SIZE,
0);
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition); gl.enableVertexAttribArray(directInteriorProgram.attributes.aPosition);
gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID); gl.enableVertexAttribArray(directInteriorProgram.attributes.aPathID);
gl.enableVertexAttribArray(directInteriorProgram.attributes.aNormalAngle);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshes.coverInteriorIndices);
} }

View File

@ -221,7 +221,7 @@ export abstract class XCAAStrategy extends AntialiasingStrategy {
this.setAADepthState(renderer); this.setAADepthState(renderer);
} }
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap): void { protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): void {
const renderContext = renderer.renderContext; const renderContext = renderer.renderContext;
const gl = renderContext.gl; const gl = renderContext.gl;
@ -395,7 +395,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
gl.useProgram(lineProgram.program); gl.useProgram(lineProgram.program);
const uniforms = lineProgram.uniforms; const uniforms = lineProgram.uniforms;
this.setAAUniforms(renderer, uniforms); this.setAAUniforms(renderer, uniforms, objectIndex);
for (const direction of DIRECTIONS) { for (const direction of DIRECTIONS) {
const vao = this.lineVAOs[direction]; const vao = this.lineVAOs[direction];
@ -431,7 +431,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
gl.useProgram(curveProgram.program); gl.useProgram(curveProgram.program);
const uniforms = curveProgram.uniforms; const uniforms = curveProgram.uniforms;
this.setAAUniforms(renderer, uniforms); this.setAAUniforms(renderer, uniforms, objectIndex);
for (const direction of DIRECTIONS) { for (const direction of DIRECTIONS) {
const vao = this.curveVAOs[direction]; const vao = this.curveVAOs[direction];
@ -687,7 +687,7 @@ export abstract class MCAAStrategy extends XCAAStrategy {
const coverProgram = renderContext.shaderPrograms.mcaaCover; const coverProgram = renderContext.shaderPrograms.mcaaCover;
gl.useProgram(coverProgram.program); gl.useProgram(coverProgram.program);
renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO); renderContext.vertexArrayObjectExt.bindVertexArrayOES(this.coverVAO);
this.setAAUniforms(renderer, coverProgram.uniforms); this.setAAUniforms(renderer, coverProgram.uniforms, objectIndex);
const bQuadRange = renderer.meshData[meshIndex].bQuadPathRanges; const bQuadRange = renderer.meshData[meshIndex].bQuadPathRanges;
const count = calculateCountFromIndexRanges(pathRange, bQuadRange); const count = calculateCountFromIndexRanges(pathRange, bQuadRange);
@ -751,12 +751,9 @@ export class ECAAStrategy extends XCAAStrategy {
this.antialiasCurvesOfObject(renderer, objectIndex); this.antialiasCurvesOfObject(renderer, objectIndex);
} }
protected setAAUniforms(renderer: Renderer, uniforms: UniformMap): void { protected setAAUniforms(renderer: Renderer, uniforms: UniformMap, objectIndex: number): void {
super.setAAUniforms(renderer, uniforms); super.setAAUniforms(renderer, uniforms, objectIndex);
renderer.setEmboldenAmountUniform(objectIndex, uniforms);
const renderContext = renderer.renderContext;
const emboldenAmount = renderer.emboldenAmount;
renderContext.gl.uniform2f(uniforms.uEmboldenAmount, emboldenAmount[0], emboldenAmount[1]);
} }
protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram { protected getResolveProgram(renderContext: RenderContext): PathfinderShaderProgram {
@ -929,7 +926,7 @@ export class ECAAStrategy extends XCAAStrategy {
gl.useProgram(lineProgram.program); gl.useProgram(lineProgram.program);
const uniforms = lineProgram.uniforms; const uniforms = lineProgram.uniforms;
this.setAAUniforms(renderer, uniforms); this.setAAUniforms(renderer, uniforms, objectIndex);
const vao = this.lineVAO; const vao = this.lineVAO;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);
@ -957,7 +954,7 @@ export class ECAAStrategy extends XCAAStrategy {
gl.useProgram(curveProgram.program); gl.useProgram(curveProgram.program);
const uniforms = curveProgram.uniforms; const uniforms = curveProgram.uniforms;
this.setAAUniforms(renderer, uniforms); this.setAAUniforms(renderer, uniforms, objectIndex);
const vao = this.curveVAO; const vao = this.curveVAO;
renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao); renderContext.vertexArrayObjectExt.bindVertexArrayOES(vao);

View File

@ -177,10 +177,24 @@ impl<'a> Partitioner<'a> {
let next_active_edge_index = self.find_point_between_active_edges(endpoint_index); let next_active_edge_index = self.find_point_between_active_edges(endpoint_index);
let endpoint = &self.endpoints[endpoint_index as usize]; let endpoint = &self.endpoints[endpoint_index as usize];
self.emit_b_quads_around_active_edge(next_active_edge_index, endpoint.position.x); let emission_result = self.emit_b_quads_around_active_edge(next_active_edge_index,
endpoint.position.x);
self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index); self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index);
// Add supporting interior triangles if necessary.
match emission_result {
BQuadEmissionResult::BQuadEmittedAbove | BQuadEmissionResult::BQuadEmittedAround => {
self.add_supporting_interior_triangle(next_active_edge_index,
next_active_edge_index - 1,
next_active_edge_index + 2);
self.add_supporting_interior_triangle(next_active_edge_index + 1,
next_active_edge_index - 1,
next_active_edge_index + 2);
}
_ => {}
}
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index); let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
let next_endpoint_index = self.next_endpoint_of(endpoint_index); let next_endpoint_index = self.next_endpoint_of(endpoint_index);
let new_point = self.create_point_from_endpoint(next_endpoint_index); let new_point = self.create_point_from_endpoint(next_endpoint_index);
@ -277,8 +291,28 @@ impl<'a> Partitioner<'a> {
// TODO(pcwalton): Collapse the two duplicate endpoints that this will create together if // TODO(pcwalton): Collapse the two duplicate endpoints that this will create together if
// possible (i.e. if they have the same parity). // possible (i.e. if they have the same parity).
self.emit_b_quads_around_active_edge(active_edge_indices[0], endpoint.position.x); let b_quad_emission_results = [
self.emit_b_quads_around_active_edge(active_edge_indices[1], endpoint.position.x); self.emit_b_quads_around_active_edge(active_edge_indices[0], endpoint.position.x),
self.emit_b_quads_around_active_edge(active_edge_indices[1], endpoint.position.x),
];
// Add supporting interior triangles if necessary.
match b_quad_emission_results[0] {
BQuadEmissionResult::BQuadEmittedAbove | BQuadEmissionResult::BQuadEmittedAround => {
self.add_supporting_interior_triangle(active_edge_indices[0],
active_edge_indices[0] - 1,
active_edge_indices[0] + 2)
}
_ => {}
}
match b_quad_emission_results[1] {
BQuadEmissionResult::BQuadEmittedBelow | BQuadEmissionResult::BQuadEmittedAround => {
self.add_supporting_interior_triangle(active_edge_indices[1],
active_edge_indices[1] - 2,
active_edge_indices[1] + 1)
}
_ => {}
}
self.heap.pop(); self.heap.pop();
@ -837,6 +871,17 @@ impl<'a> Partitioner<'a> {
self.subdivide_active_edge_again_at_t(subdivision, t, bottom) self.subdivide_active_edge_again_at_t(subdivision, t, bottom)
} }
fn add_supporting_interior_triangle(&mut self,
active_edge_index: u32,
upper_active_edge_index: u32,
lower_active_edge_index: u32) {
self.library.cover_indices.interior_indices.extend([
self.active_edges[active_edge_index as usize].left_vertex_index,
self.active_edges[upper_active_edge_index as usize].left_vertex_index,
self.active_edges[lower_active_edge_index as usize].left_vertex_index,
].into_iter());
}
fn already_visited_point(&self, point: &Point) -> bool { fn already_visited_point(&self, point: &Point) -> bool {
// FIXME(pcwalton): This makes the visited vector too big. // FIXME(pcwalton): This makes the visited vector too big.
let index = point.endpoint_index as usize; let index = point.endpoint_index as usize;
@ -1093,6 +1138,9 @@ impl<'a> Partitioner<'a> {
} }
} }
// FIXME(pcwalton): This creates incorrect normals for vertical lines. I think we should
// probably calculate normals for the path vertices first and then lerp them to calculate these
// B-vertex normals. That would be simpler, faster, and more correct, I suspect.
fn update_vertex_normals_for_new_b_quad(&mut self, b_quad: &BQuad) { fn update_vertex_normals_for_new_b_quad(&mut self, b_quad: &BQuad) {
self.update_vertex_normal_for_b_quad_edge(b_quad.upper_left_vertex_index, self.update_vertex_normal_for_b_quad_edge(b_quad.upper_left_vertex_index,
b_quad.upper_control_point_vertex_index, b_quad.upper_control_point_vertex_index,

View File

@ -114,6 +114,10 @@ int convertWindowDepthValueToPathIndex(float depthValue) {
return int(pathIndex); return int(pathIndex);
} }
vec2 dilatePosition(vec2 position, float normalAngle, vec2 amount) {
return position + vec2(cos(normalAngle), -sin(normalAngle)) * amount;
}
bool computeMCAAQuadPosition(out vec2 outPosition, bool computeMCAAQuadPosition(out vec2 outPosition,
inout vec2 leftPosition, inout vec2 leftPosition,
inout vec2 rightPosition, inout vec2 rightPosition,
@ -164,8 +168,8 @@ bool computeECAAQuadPosition(out vec2 outPosition,
float leftNormalAngle, float leftNormalAngle,
float rightNormalAngle, float rightNormalAngle,
vec2 emboldenAmount) { vec2 emboldenAmount) {
leftPosition += vec2(cos(leftNormalAngle), -sin(leftNormalAngle)) * emboldenAmount; leftPosition = dilatePosition(leftPosition, leftNormalAngle, emboldenAmount);
rightPosition += vec2(cos(rightNormalAngle), -sin(rightNormalAngle)) * emboldenAmount; rightPosition = dilatePosition(rightPosition, rightNormalAngle, emboldenAmount);
leftPosition = hintPosition(leftPosition, hints); leftPosition = hintPosition(leftPosition, hints);
rightPosition = hintPosition(rightPosition, hints); rightPosition = hintPosition(rightPosition, hints);

View File

@ -12,6 +12,7 @@ precision highp float;
uniform mat4 uTransform; uniform mat4 uTransform;
uniform vec4 uHints; uniform vec4 uHints;
uniform vec2 uEmboldenAmount;
uniform ivec2 uPathColorsDimensions; uniform ivec2 uPathColorsDimensions;
uniform ivec2 uPathTransformDimensions; uniform ivec2 uPathTransformDimensions;
uniform sampler2D uPathColors; uniform sampler2D uPathColors;
@ -21,6 +22,7 @@ attribute vec2 aPosition;
attribute vec2 aTexCoord; attribute vec2 aTexCoord;
attribute float aPathID; attribute float aPathID;
attribute float aSign; attribute float aSign;
attribute float aNormalAngle;
varying vec4 vColor; varying vec4 vColor;
varying vec2 vTexCoord; varying vec2 vTexCoord;
@ -31,7 +33,8 @@ void main() {
vec4 pathTransform = fetchFloat4Data(uPathTransform, pathID, uPathTransformDimensions); vec4 pathTransform = fetchFloat4Data(uPathTransform, pathID, uPathTransformDimensions);
vec2 position = hintPosition(aPosition, uHints); vec2 position = dilatePosition(aPosition, aNormalAngle, uEmboldenAmount);
position = hintPosition(position, uHints);
position = transformVertexPositionST(position, pathTransform); position = transformVertexPositionST(position, pathTransform);
position = transformVertexPosition(position, uTransform); position = transformVertexPosition(position, uTransform);

View File

@ -12,6 +12,7 @@ precision highp float;
uniform mat4 uTransform; uniform mat4 uTransform;
uniform vec4 uHints; uniform vec4 uHints;
uniform vec2 uEmboldenAmount;
uniform ivec2 uPathColorsDimensions; uniform ivec2 uPathColorsDimensions;
uniform ivec2 uPathTransformDimensions; uniform ivec2 uPathTransformDimensions;
uniform sampler2D uPathColors; uniform sampler2D uPathColors;
@ -19,6 +20,7 @@ uniform sampler2D uPathTransform;
attribute vec2 aPosition; attribute vec2 aPosition;
attribute float aPathID; attribute float aPathID;
attribute float aNormalAngle;
varying vec4 vColor; varying vec4 vColor;
@ -27,7 +29,8 @@ void main() {
vec4 pathTransform = fetchFloat4Data(uPathTransform, pathID, uPathTransformDimensions); vec4 pathTransform = fetchFloat4Data(uPathTransform, pathID, uPathTransformDimensions);
vec2 position = hintPosition(aPosition, uHints); vec2 position = dilatePosition(aPosition, aNormalAngle, uEmboldenAmount);
position = hintPosition(position, uHints);
position = transformVertexPositionST(position, pathTransform); position = transformVertexPositionST(position, pathTransform);
position = transformVertexPosition(position, uTransform); position = transformVertexPosition(position, uTransform);

View File

@ -58,9 +58,10 @@ void main() {
leftNormalAngle, leftNormalAngle,
rightNormalAngle, rightNormalAngle,
uEmboldenAmount)) { uEmboldenAmount)) {
controlPointPosition += vec2(cos(controlPointNormalAngle), controlPointPosition = dilatePosition(controlPointPosition,
-sin(controlPointNormalAngle)) * uEmboldenAmount; controlPointNormalAngle,
controlPointPosition = hintPosition(aControlPointPosition, uHints); uEmboldenAmount);
controlPointPosition = hintPosition(controlPointPosition, uHints);
controlPointPosition = transformVertexPositionST(controlPointPosition, transform); controlPointPosition = transformVertexPositionST(controlPointPosition, transform);
controlPointPosition = transformVertexPositionST(controlPointPosition, uTransformST); controlPointPosition = transformVertexPositionST(controlPointPosition, uTransformST);
controlPointPosition = convertClipToScreenSpace(controlPointPosition, uFramebufferSize); controlPointPosition = convertClipToScreenSpace(controlPointPosition, uFramebufferSize);