Skip to content

Commit

Permalink
better symbol fading with texture lookups
Browse files Browse the repository at this point in the history
- this is simpler than prediction opacity based on current zooming speed
- this is smoother: Symbols don't flicker when changing zoom speed or zooming in
  and out rapidly.

fix #590
  • Loading branch information
ansis committed Mar 31, 2016
1 parent 3f86fbf commit 1df1466
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 109 deletions.
8 changes: 3 additions & 5 deletions js/render/draw_symbol.js
Expand Up @@ -143,11 +143,9 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref

gl.uniform1f(program.u_zoom, (painter.transform.zoom - zoomAdjust) * 10); // current zoom level

var f = painter.frameHistory.getFadeProperties(300);
gl.uniform1f(program.u_fadedist, f.fadedist * 10);
gl.uniform1f(program.u_minfadezoom, Math.floor(f.minfadezoom * 10));
gl.uniform1f(program.u_maxfadezoom, Math.floor(f.maxfadezoom * 10));
gl.uniform1f(program.u_fadezoom, (painter.transform.zoom + f.bump) * 10);
gl.activeTexture(gl.TEXTURE1);
painter.frameHistory.bind(gl);
gl.uniform1i(program.u_fadetexture, 1);

var group, offset, count, elementOffset;

Expand Down
100 changes: 52 additions & 48 deletions js/render/frame_history.js
Expand Up @@ -3,64 +3,68 @@
module.exports = FrameHistory;

function FrameHistory() {
this.frameHistory = [];
}
this.changeTimes = new Float64Array(256);
this.changeOpacities = new Uint8Array(256);
this.opacities = new Uint8ClampedArray(256);
this.array = new Uint8Array(this.opacities.buffer);

FrameHistory.prototype.getFadeProperties = function(duration) {
if (duration === undefined) duration = 300;
var currentTime = (new Date()).getTime();
this.fadeDuration = 300;
this.previousZoom = 0;
this.firstFrame = true;
}

// Remove frames until only one is outside the duration, or until there are only three
while (this.frameHistory.length > 3 && this.frameHistory[1].time + duration < currentTime) {
this.frameHistory.shift();
}
FrameHistory.prototype.record = function(zoom) {
var now = Date.now();

if (this.frameHistory[1].time + duration < currentTime) {
this.frameHistory[0].z = this.frameHistory[1].z;
if (this.firstFrame) {
now = 0;
this.firstFrame = false;
}

var frameLen = this.frameHistory.length;
if (frameLen < 3) console.warn('there should never be less than three frames in the history');

// Find the range of zoom levels we want to fade between
var startingZ = this.frameHistory[0].z,
lastFrame = this.frameHistory[frameLen - 1],
endingZ = lastFrame.z,
lowZ = Math.min(startingZ, endingZ),
highZ = Math.max(startingZ, endingZ);

// Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one duration
var zoomDiff = lastFrame.z - this.frameHistory[1].z,
timeDiff = lastFrame.time - this.frameHistory[1].time;
var fadedist = zoomDiff / (timeDiff / duration);
zoom = Math.floor(zoom * 10);

if (isNaN(fadedist)) console.warn('fadedist should never be NaN');
var z;
if (zoom < this.previousZoom) {
for (z = zoom + 1; z <= this.previousZoom; z++) {
this.changeTimes[z] = now;
this.changeOpacities[z] = this.opacities[z];
}
} else {
for (z = zoom; z > this.previousZoom; z--) {
this.changeTimes[z] = now;
this.changeOpacities[z] = this.opacities[z];
}
}

// At end of a zoom when the zoom stops changing continue pretending to zoom at that speed
// bump is how much farther it would have been if it had continued zooming at the same rate
var bump = (currentTime - lastFrame.time) / duration * fadedist;
for (z = 0; z < 256; z++) {
var timeSince = now - this.changeTimes[z];
var opacityChange = timeSince / this.fadeDuration * 255;
if (z <= zoom) {
this.opacities[z] = this.changeOpacities[z] + opacityChange;
} else {
this.opacities[z] = this.changeOpacities[z] - opacityChange;
}
}

return {
fadedist: fadedist,
minfadezoom: lowZ,
maxfadezoom: highZ,
bump: bump
};
this.changed = true;
this.previousZoom = zoom;
};

// Record frame history that will be used to calculate fading params
FrameHistory.prototype.record = function(zoom) {
var currentTime = (new Date()).getTime();

// first frame ever
if (!this.frameHistory.length) {
this.frameHistory.push({time: 0, z: zoom }, {time: 0, z: zoom });
}
FrameHistory.prototype.bind = function(gl) {
if (!this.texture) {
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
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_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, 256, 1, 0, gl.ALPHA, gl.UNSIGNED_BYTE, this.array);

if (this.frameHistory.length === 2 || this.frameHistory[this.frameHistory.length - 1].z !== zoom) {
this.frameHistory.push({
time: currentTime,
z: zoom
});
} else {
gl.bindTexture(gl.TEXTURE_2D, this.texture);
if (this.changed) {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 256, 1, gl.ALPHA, gl.UNSIGNED_BYTE, this.array);
this.changed = false;
}
}
};
4 changes: 2 additions & 2 deletions js/render/painter/use_program.js
Expand Up @@ -65,13 +65,13 @@ var definitions = {
fragmentSource: fs.readFileSync(path.join(__dirname, '../../../shaders/icon.fragment.glsl'), 'utf8'),
vertexSource: fs.readFileSync(path.join(__dirname, '../../../shaders/icon.vertex.glsl'), 'utf8'),
attributeNames: ['a_pos', 'a_offset', 'a_data1', 'a_data2'],
uniformNames: ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_zoom', 'u_fadedist', 'u_minfadezoom', 'u_maxfadezoom', 'u_fadezoom', 'u_opacity', 'u_skewed', 'u_extra']
uniformNames: ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_zoom', 'u_fadetexture', 'u_opacity', 'u_skewed', 'u_extra']
},
sdf: {
fragmentSource: fs.readFileSync(path.join(__dirname, '../../../shaders/sdf.fragment.glsl'), 'utf8'),
vertexSource: fs.readFileSync(path.join(__dirname, '../../../shaders/sdf.vertex.glsl'), 'utf8'),
attributeNames: ['a_pos', 'a_offset', 'a_data1', 'a_data2'],
uniformNames: ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_color', 'u_gamma', 'u_buffer', 'u_zoom', 'u_fadedist', 'u_minfadezoom', 'u_maxfadezoom', 'u_fadezoom', 'u_skewed', 'u_extra']
uniformNames: ['u_matrix', 'u_exmatrix', 'u_texture', 'u_texsize', 'u_color', 'u_gamma', 'u_buffer', 'u_zoom', 'u_fadetexture', 'u_skewed', 'u_extra']
},
collisionbox: {
fragmentSource: fs.readFileSync(path.join(__dirname, '../../../shaders/collisionbox.fragment.glsl'), 'utf8'),
Expand Down
7 changes: 5 additions & 2 deletions shaders/icon.fragment.glsl
@@ -1,10 +1,13 @@
precision mediump float;

uniform sampler2D u_texture;
uniform sampler2D u_fadetexture;
uniform lowp float u_opacity;

varying vec2 v_tex;
varying float v_alpha;
varying vec2 v_fade_tex;

void main() {
gl_FragColor = texture2D(u_texture, v_tex) * v_alpha;
lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * u_opacity;
gl_FragColor = texture2D(u_texture, v_tex) * alpha;
}
28 changes: 2 additions & 26 deletions shaders/icon.vertex.glsl
Expand Up @@ -11,18 +11,13 @@ attribute vec4 a_data2;
uniform mat4 u_matrix;
uniform mat4 u_exmatrix;
uniform mediump float u_zoom;
uniform mediump float u_fadedist;
uniform mediump float u_minfadezoom;
uniform mediump float u_maxfadezoom;
uniform mediump float u_fadezoom;
uniform lowp float u_opacity;
uniform bool u_skewed;
uniform float u_extra;

uniform vec2 u_texsize;

varying vec2 v_tex;
varying float v_alpha;
varying vec2 v_fade_tex;

void main() {
vec2 a_tex = a_data1.xy;
Expand All @@ -36,24 +31,6 @@ void main() {
// u_zoom is the current zoom level adjusted for the change in font size
mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom));

// fade out labels
mediump float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0);

if (u_fadedist >= 0.0) {
v_alpha = alpha;
} else {
v_alpha = 1.0 - alpha;
}
if (u_maxfadezoom < a_labelminzoom) {
v_alpha = 0.0;
}
if (u_minfadezoom >= a_labelminzoom) {
v_alpha = 1.0;
}

// if label has been faded out, clip it
z += step(v_alpha, 0.0);

if (u_skewed) {
vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, 0, 0);
gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1);
Expand All @@ -64,6 +41,5 @@ void main() {
}

v_tex = a_tex / u_texsize;

v_alpha *= u_opacity;
v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0);
}
8 changes: 5 additions & 3 deletions shaders/sdf.fragment.glsl
@@ -1,17 +1,19 @@
precision mediump float;

uniform sampler2D u_texture;
uniform sampler2D u_fadetexture;
uniform lowp vec4 u_color;
uniform lowp float u_buffer;
uniform lowp float u_gamma;

varying vec2 v_tex;
varying float v_alpha;
varying vec2 v_fade_tex;
varying float v_gamma_scale;

void main() {
lowp float gamma = u_gamma * v_gamma_scale;
lowp float dist = texture2D(u_texture, v_tex).a;
lowp float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * v_alpha;
lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a;
lowp float gamma = u_gamma * v_gamma_scale;
lowp float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * fade_alpha;
gl_FragColor = u_color * alpha;
}
25 changes: 2 additions & 23 deletions shaders/sdf.vertex.glsl
Expand Up @@ -12,17 +12,13 @@ uniform mat4 u_matrix;
uniform mat4 u_exmatrix;

uniform mediump float u_zoom;
uniform mediump float u_fadedist;
uniform mediump float u_minfadezoom;
uniform mediump float u_maxfadezoom;
uniform mediump float u_fadezoom;
uniform bool u_skewed;
uniform float u_extra;

uniform vec2 u_texsize;

varying vec2 v_tex;
varying float v_alpha;
varying vec2 v_fade_tex;
varying float v_gamma_scale;

void main() {
Expand All @@ -35,24 +31,6 @@ void main() {
// u_zoom is the current zoom level adjusted for the change in font size
mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom));

// fade out labels
mediump float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0);

if (u_fadedist >= 0.0) {
v_alpha = alpha;
} else {
v_alpha = 1.0 - alpha;
}
if (u_maxfadezoom < a_labelminzoom) {
v_alpha = 0.0;
}
if (u_minfadezoom >= a_labelminzoom) {
v_alpha = 1.0;
}

// if label has been faded out, clip it
z += step(v_alpha, 0.0);

if (u_skewed) {
vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, 0, 0);
gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1);
Expand All @@ -69,4 +47,5 @@ void main() {
v_gamma_scale = perspective_scale;

v_tex = a_tex / u_texsize;
v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0);
}

0 comments on commit 1df1466

Please sign in to comment.