From c52a09639ceeeb809cd837360993df9c45b45420 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 12 Dec 2016 17:04:49 -0800 Subject: [PATCH 1/4] fix text blurriness at different FOVs This replaces a hardcoded shader approximation with a more correct, FOV-dependent one. `gl_Position.w - 0.5` is replaced with `gl_Position.w / tan(fov)` The `/ tan(fov)` is handled by multiplying `1 / tan(fov)` into `u_gamma` outside the shader. I think `- 0.5` was just chosen as something that looked right for the default fov. --- js/render/draw_symbol.js | 2 +- shaders/symbol_sdf.vertex.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index e07db38e68e..7459b5ec6b6 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -178,7 +178,7 @@ function drawTileSymbols(program, painter, layer, tile, buffers, isText, isSDF, } if (isSDF) { - const gammaScale = fontScale * (pitchWithMap ? Math.cos(tr._pitch) : 1); + const gammaScale = fontScale * (pitchWithMap ? Math.cos(tr._pitch) : 1) * tr.altitude; if (haloWidth) { // Draw halo underneath the text. gl.uniform1f(program.u_gamma, (haloBlur * blurOffset / sdfPx + gamma) / gammaScale); diff --git a/shaders/symbol_sdf.vertex.glsl b/shaders/symbol_sdf.vertex.glsl index 223b79161fc..f14aa6768a9 100644 --- a/shaders/symbol_sdf.vertex.glsl +++ b/shaders/symbol_sdf.vertex.glsl @@ -75,7 +75,7 @@ void main() { gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); } - v_gamma_scale = (gl_Position.w - 0.5); + v_gamma_scale = gl_Position.w; v_tex = a_tex / u_texsize; v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0); From eb6c6596c6a7a61363d30356674e0002153b1d19 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 12 Dec 2016 15:36:25 -0800 Subject: [PATCH 2/4] replace `altitude` with `fov` `altitude` was a terribly-named variable that was used to indirectly control the fov. This should eliminate some confusion. `altitude` was equivalent to `cameraToCenterDistance / height` --- js/geo/transform.js | 39 +++++++++++--------- js/render/draw_circle.js | 4 +- js/render/draw_symbol.js | 4 +- package.json | 2 +- test/js/geo/transform.test.js | 3 ++ test/js/source/source_cache.test.js | 12 +++--- test/js/ui/map.test.js | 11 +++--- test/node_modules/mapbox-gl-js-test/fixed.js | 2 +- 8 files changed, 43 insertions(+), 34 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index 55f925b3fd5..f238d3bc701 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -32,7 +32,7 @@ class Transform { this._center = new LngLat(0, 0); this.zoom = 0; this.angle = 0; - this._altitude = 1.5; + this._fov = 0.6435011087932844; this._pitch = 0; this._unmodified = true; } @@ -89,14 +89,14 @@ class Transform { this._calcMatrices(); } - get altitude() { - return this._altitude; + get fov() { + return this._fov / Math.PI * 180; } - set altitude(altitude) { - const a = Math.max(0.75, altitude); - if (this._altitude === a) return; + set fov(fov) { + fov = Math.max(0.01, Math.min(60, fov)); + if (this._fov === fov) return; this._unmodified = false; - this._altitude = a; + this._fov = fov / 180 * Math.PI; this._calcMatrices(); } @@ -396,25 +396,28 @@ class Transform { _calcMatrices() { if (!this.height) return; - // Find the distance from the center point to the center top in altitude units using law of sines. - const halfFov = Math.atan(0.5 / this.altitude); - const topHalfSurfaceDistance = Math.sin(halfFov) * this.altitude / Math.sin(Math.PI / 2 - this._pitch - halfFov); + this.cameraToCenterDistance = 0.5 / Math.tan(this._fov / 2) * this.height; + + // Find the distance from the center point [width/2, height/2] to the + // center top point [width/2, 0] in Z units, using the law of sines. + // 1 Z unit is equivalent to 1 horizontal px at the center of the map + // (the distance between[width/2, height/2] and [width/2 + 1, height/2]) + const halfFov = this._fov / 2; + const groundAngle = Math.PI / 2 + this._pitch; + const topHalfSurfaceDistance = Math.sin(halfFov) * this.cameraToCenterDistance / Math.sin(Math.PI - groundAngle - halfFov); // Calculate z value of the farthest fragment that should be rendered. - const farZ = Math.cos(Math.PI / 2 - this._pitch) * topHalfSurfaceDistance + this.altitude; + const farZ = Math.cos(Math.PI / 2 - this._pitch) * topHalfSurfaceDistance + this.cameraToCenterDistance; // matrix for conversion from location to GL coordinates (-1 .. 1) let m = new Float64Array(16); - mat4.perspective(m, 2 * Math.atan((this.height / 2) / this.altitude), this.width / this.height, 0.1, farZ); - mat4.translate(m, m, [0, 0, -this.altitude]); + mat4.perspective(m, this._fov, this.width / this.height, 0.1, farZ); // a hack around https://github.com/mapbox/mapbox-gl-js/issues/2270 m[14] = Math.min(m[14], m[15]); - // After the rotateX, z values are in pixel units. Convert them to - // altitude units. 1 altitude unit = the screen height. - mat4.scale(m, m, [1, -1, 1 / this.height]); - + mat4.scale(m, m, [1, -1, 1]); + mat4.translate(m, m, [0, 0, -this.cameraToCenterDistance]); mat4.rotateX(m, m, this._pitch); mat4.rotateZ(m, m, this.angle); mat4.translate(m, m, [-this.x, -this.y, 0]); @@ -445,7 +448,7 @@ class Transform { // calculate how much longer the real world distance is at the top of the screen // than at the middle of the screen. - const topEdgeLength = Math.sqrt(this.height * this.height / 4 * (1 + this.altitude * this.altitude)); + const topEdgeLength = Math.sqrt((this.height * this.height + this.cameraToCenterDistance * this.cameraToCenterDistance) / 4); this.lineStretch = (this.height / 2 * Math.tan(this._pitch)) / topEdgeLength; } } diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index e3960376ccb..76cf8b04c59 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -32,8 +32,8 @@ function drawCircles(painter, sourceCache, layer, coords) { if (layer.paint['circle-pitch-scale'] === 'map') { gl.uniform1i(program.u_scale_with_map, true); gl.uniform2f(program.u_extrude_scale, - painter.transform.pixelsToGLUnits[0] * painter.transform.altitude, - painter.transform.pixelsToGLUnits[1] * painter.transform.altitude); + painter.transform.pixelsToGLUnits[0] * painter.transform.cameraToCenterDistance, + painter.transform.pixelsToGLUnits[1] * painter.transform.cameraToCenterDistance); } else { gl.uniform1i(program.u_scale_with_map, false); gl.uniform2fv(program.u_extrude_scale, painter.transform.pixelsToGLUnits); diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 7459b5ec6b6..7c100153cca 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -173,12 +173,12 @@ function drawTileSymbols(program, painter, layer, tile, buffers, isText, isSDF, const s = pixelsToTileUnits(tile, fontScale, tr.zoom); gl.uniform2f(program.u_extrude_scale, s, s); } else { - const s = tr.altitude * fontScale; + const s = tr.cameraToCenterDistance * fontScale; gl.uniform2f(program.u_extrude_scale, tr.pixelsToGLUnits[0] * s, tr.pixelsToGLUnits[1] * s); } if (isSDF) { - const gammaScale = fontScale * (pitchWithMap ? Math.cos(tr._pitch) : 1) * tr.altitude; + const gammaScale = fontScale * (pitchWithMap ? Math.cos(tr._pitch) : 1) * tr.cameraToCenterDistance; if (haloWidth) { // Draw halo underneath the text. gl.uniform1f(program.u_gamma, (haloBlur * blurOffset / sdfPx + gamma) / gammaScale); diff --git a/package.json b/package.json index b1521245ac8..38c97d5b6bc 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "in-publish": "^2.0.0", "jsdom": "^9.4.2", "lodash.template": "^4.4.0", - "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#52acb6e9e4b75b7e765ef087a0e539739e9846c1", + "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#f13a9710d754fe7357d04175ba960a298aa43a2c", "minifyify": "^7.0.1", "npm-run-all": "^3.0.0", "nyc": "^8.3.0", diff --git a/test/js/geo/transform.test.js b/test/js/geo/transform.test.js index cabfc6fcf99..7b1ae13acac 100644 --- a/test/js/geo/transform.test.js +++ b/test/js/geo/transform.test.js @@ -113,6 +113,9 @@ test('transform', (t) => { const transform = new Transform(); transform.resize(200, 200); + // make slightly off center so that sort order is not subject to precision issues + transform.center = { lng: -0.01, lat: 0.01 }; + transform.zoom = 0; t.deepEqual(transform.coveringTiles(options), []); diff --git a/test/js/source/source_cache.test.js b/test/js/source/source_cache.test.js index 53b646fbf2c..ec37e1cd9e9 100644 --- a/test/js/source/source_cache.test.js +++ b/test/js/source/source_cache.test.js @@ -443,7 +443,9 @@ test('SourceCache#update', (t) => { const transform = new Transform(); transform.resize(511, 511); transform.zoom = 16; - transform.center = new LngLat(0, 0); + + // use slightly offset center so that sort order is better defined + transform.center = new LngLat(-0.001, 0.001); const sourceCache = createSourceCache({ @@ -459,8 +461,8 @@ test('SourceCache#update', (t) => { t.deepEqual(sourceCache.getRenderableIds(), [ new TileCoord(16, 8191, 8191, 0).id, new TileCoord(16, 8192, 8191, 0).id, - new TileCoord(16, 8192, 8192, 0).id, - new TileCoord(16, 8191, 8192, 0).id + new TileCoord(16, 8191, 8192, 0).id, + new TileCoord(16, 8192, 8192, 0).id ]); transform.zoom = 15; @@ -469,8 +471,8 @@ test('SourceCache#update', (t) => { t.deepEqual(sourceCache.getRenderableIds(), [ new TileCoord(16, 8191, 8191, 0).id, new TileCoord(16, 8192, 8191, 0).id, - new TileCoord(16, 8192, 8192, 0).id, - new TileCoord(16, 8191, 8192, 0).id + new TileCoord(16, 8191, 8192, 0).id, + new TileCoord(16, 8192, 8192, 0).id ]); t.end(); }); diff --git a/test/js/ui/map.test.js b/test/js/ui/map.test.js index 36492bdc4b8..d65ef1d71b7 100755 --- a/test/js/ui/map.test.js +++ b/test/js/ui/map.test.js @@ -9,6 +9,7 @@ const LngLat = require('../../../js/geo/lng_lat'); const fixed = require('mapbox-gl-js-test/fixed'); const fixedNum = fixed.Num; const fixedLngLat = fixed.LngLat; +const fixedCoord = fixed.Coord; function createMap(options, callback) { const container = window.document.createElement('div'); @@ -505,7 +506,7 @@ test('Map', (t) => { }); function toFixed(bounds) { - const n = 10; + const n = 9; return [ [bounds[0][0].toFixed(n), bounds[0][1].toFixed(n)], [bounds[1][0].toFixed(n), bounds[1][1].toFixed(n)] @@ -659,7 +660,7 @@ test('Map', (t) => { t.test('#unproject', (t) => { const map = createMap(); - t.deepEqual(map.unproject([100, 100]), { lng: 0, lat: 0 }); + t.deepEqual(fixedLngLat(map.unproject([100, 100])), { lng: 0, lat: 0 }); t.end(); }); @@ -689,7 +690,7 @@ test('Map', (t) => { const output = map.queryRenderedFeatures(map.project(new LngLat(0, 0))); const args = map.style.queryRenderedFeatures.getCall(0).args; - t.deepEqual(args[0], [{ column: 0.5, row: 0.5, zoom: 0 }]); // query geometry + t.deepEqual(args[0].map(c => fixedCoord(c)), [{ column: 0.5, row: 0.5, zoom: 0 }]); // query geometry t.deepEqual(args[1], {}); // params t.deepEqual(args[2], 0); // bearing t.deepEqual(args[3], 0); // zoom @@ -738,8 +739,8 @@ test('Map', (t) => { map.queryRenderedFeatures(map.project(new LngLat(360, 0))); - const coords = map.style.queryRenderedFeatures.getCall(0).args[0]; - t.equal(parseFloat(coords[0].column.toFixed(4)), 1.5); + const coords = map.style.queryRenderedFeatures.getCall(0).args[0].map(c => fixedCoord(c)); + t.equal(coords[0].column, 1.5); t.equal(coords[0].row, 0.5); t.equal(coords[0].zoom, 0); diff --git a/test/node_modules/mapbox-gl-js-test/fixed.js b/test/node_modules/mapbox-gl-js-test/fixed.js index bbf9aad5066..1f2eff9620a 100644 --- a/test/node_modules/mapbox-gl-js-test/fixed.js +++ b/test/node_modules/mapbox-gl-js-test/fixed.js @@ -12,7 +12,7 @@ function fixedNum(n, precision) { } function fixedLngLat(l, precision) { - if (precision === undefined) precision = 10; + if (precision === undefined) precision = 9; return { lng: fixedNum(l.lng, precision), lat: fixedNum(l.lat, precision) From 0b5520fa5ab2a4659d80dcffa8b035a0d84fe1ca Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 12 Dec 2016 20:23:46 -0800 Subject: [PATCH 3/4] fix matrix z range and remove hack This should fix the issue behind #2270 and remove the need for the hack added in #3740. --- js/geo/transform.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index f238d3bc701..49f8b0f428f 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -406,15 +406,14 @@ class Transform { const groundAngle = Math.PI / 2 + this._pitch; const topHalfSurfaceDistance = Math.sin(halfFov) * this.cameraToCenterDistance / Math.sin(Math.PI - groundAngle - halfFov); - // Calculate z value of the farthest fragment that should be rendered. - const farZ = Math.cos(Math.PI / 2 - this._pitch) * topHalfSurfaceDistance + this.cameraToCenterDistance; + // Calculate z distance of the farthest fragment that should be rendered. + const furthestDistance = Math.cos(Math.PI / 2 - this._pitch) * topHalfSurfaceDistance + this.cameraToCenterDistance; + // Add a bit extra to avoid precision problems when a fragment's distance is exactly `furthestDistance` + const farZ = furthestDistance * 1.01; // matrix for conversion from location to GL coordinates (-1 .. 1) let m = new Float64Array(16); - mat4.perspective(m, this._fov, this.width / this.height, 0.1, farZ); - - // a hack around https://github.com/mapbox/mapbox-gl-js/issues/2270 - m[14] = Math.min(m[14], m[15]); + mat4.perspective(m, this._fov, this.width / this.height, 1, farZ); mat4.scale(m, m, [1, -1, 1]); mat4.translate(m, m, [0, 0, -this.cameraToCenterDistance]); From ef5582dd3bc5c15a3112e875ed66494dab8e9d0b Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 13 Dec 2016 10:56:17 -0800 Subject: [PATCH 4/4] clearer pitched line antialiasing Project the extrusion and compare it's projected pixel length with the actual pixel length and adjust antialiasing accordingly. The previous approach calculated the adjustment much more indirectly and had no intuitive explanation. --- js/geo/transform.js | 10 ---------- js/render/draw_line.js | 5 ++--- shaders/line.vertex.glsl | 22 +++++++++------------- shaders/line_pattern.vertex.glsl | 24 ++++++++++-------------- shaders/line_sdf.vertex.glsl | 26 +++++++++++--------------- 5 files changed, 32 insertions(+), 55 deletions(-) diff --git a/js/geo/transform.js b/js/geo/transform.js index 49f8b0f428f..be59870a9e4 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -439,16 +439,6 @@ class Transform { if (!m) throw new Error("failed to invert matrix"); this.pixelMatrixInverse = m; - // line antialiasing matrix - m = mat2.create(); - mat2.scale(m, m, [1, Math.cos(this._pitch)]); - mat2.rotate(m, m, this.angle); - this.lineAntialiasingMatrix = m; - - // calculate how much longer the real world distance is at the top of the screen - // than at the middle of the screen. - const topEdgeLength = Math.sqrt((this.height * this.height + this.cameraToCenterDistance * this.cameraToCenterDistance) / 4); - this.lineStretch = (this.height / 2 * Math.tan(this._pitch)) / topEdgeLength; } } diff --git a/js/render/draw_line.js b/js/render/draw_line.js index a813750fd6e..c8599e7c3cc 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -80,6 +80,8 @@ function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, gl.uniform2f(program.u_pattern_size_a, imagePosA.size[0] * image.fromScale / tileRatio, imagePosB.size[1]); gl.uniform2f(program.u_pattern_size_b, imagePosB.size[0] * image.toScale / tileRatio, imagePosB.size[1]); } + + gl.uniform2f(program.u_gl_units_to_pixels, 1 / painter.transform.pixelsToGLUnits[0], 1 / painter.transform.pixelsToGLUnits[1]); } if (programChanged) { @@ -105,9 +107,6 @@ function drawLineTile(program, painter, tile, buffers, layer, coord, layerData, gl.uniform1f(program.u_fade, image.t); } gl.uniform1f(program.u_width, layer.paint['line-width']); - - gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, painter.transform.lineAntialiasingMatrix); - gl.uniform1f(program.u_extra, painter.transform.lineStretch); } painter.enableTileClippingMask(coord); diff --git a/shaders/line.vertex.glsl b/shaders/line.vertex.glsl index 263c57759e5..10347beda3e 100644 --- a/shaders/line.vertex.glsl +++ b/shaders/line.vertex.glsl @@ -17,9 +17,8 @@ attribute vec4 a_data; uniform mat4 u_matrix; uniform mediump float u_ratio; -uniform mediump float u_extra; -uniform mat2 u_antialiasingmatrix; uniform mediump float u_width; +uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; varying vec2 v_width2; @@ -71,19 +70,16 @@ void main() { mediump float t = 1.0 - abs(u); mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); - // Remove the texture normal bit of the position before scaling it with the - // model/view matrix. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + (offset2 + dist) / u_ratio, 0.0, 1.0); + // Remove the texture normal bit to get the position + vec2 pos = floor(a_pos * 0.5); - // position of y on the screen - float y = gl_Position.y / gl_Position.w; + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude; - // how much features are squished in the y direction by the tilt - float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude); - - // how much features are squished in all directions by the perspectiveness - float perspective_scale = 1.0 / (1.0 - min(y * u_extra, 0.9)); + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; v_width2 = vec2(outset, inset); - v_gamma_scale = perspective_scale * squish_scale; } diff --git a/shaders/line_pattern.vertex.glsl b/shaders/line_pattern.vertex.glsl index e0429ce83b3..6d85a0b6b90 100644 --- a/shaders/line_pattern.vertex.glsl +++ b/shaders/line_pattern.vertex.glsl @@ -20,8 +20,7 @@ attribute vec4 a_data; uniform mat4 u_matrix; uniform mediump float u_ratio; uniform mediump float u_width; -uniform mediump float u_extra; -uniform mat2 u_antialiasingmatrix; +uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; varying vec2 v_width2; @@ -72,20 +71,17 @@ void main() { mediump float t = 1.0 - abs(u); mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); - // Remove the texture normal bit of the position before scaling it with the - // model/view matrix. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + (offset2 + dist) / u_ratio, 0.0, 1.0); - v_linesofar = a_linesofar; - - // position of y on the screen - float y = gl_Position.y / gl_Position.w; + // Remove the texture normal bit to get the position + vec2 pos = floor(a_pos * 0.5); - // how much features are squished in the y direction by the tilt - float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude); + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude; - // how much features are squished in all directions by the perspectiveness - float perspective_scale = 1.0 / (1.0 - min(y * u_extra, 0.9)); + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; + v_linesofar = a_linesofar; v_width2 = vec2(outset, inset); - v_gamma_scale = perspective_scale * squish_scale; } diff --git a/shaders/line_sdf.vertex.glsl b/shaders/line_sdf.vertex.glsl index 8f64174cc7f..ce76a1526d9 100644 --- a/shaders/line_sdf.vertex.glsl +++ b/shaders/line_sdf.vertex.glsl @@ -23,8 +23,7 @@ uniform vec2 u_patternscale_a; uniform float u_tex_y_a; uniform vec2 u_patternscale_b; uniform float u_tex_y_b; -uniform float u_extra; -uniform mat2 u_antialiasingmatrix; +uniform vec2 u_gl_units_to_pixels; uniform mediump float u_width; varying vec2 v_normal; @@ -79,22 +78,19 @@ void main() { mediump float t = 1.0 - abs(u); mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); - // Remove the texture normal bit of the position before scaling it with the - // model/view matrix. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + (offset2 + dist) / u_ratio, 0.0, 1.0); + // Remove the texture normal bit to get the position + vec2 pos = floor(a_pos * 0.5); - v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a); - v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b); - - // position of y on the screen - float y = gl_Position.y / gl_Position.w; + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude; - // how much features are squished in the y direction by the tilt - float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude); + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; - // how much features are squished in all directions by the perspectiveness - float perspective_scale = 1.0 / (1.0 - min(y * u_extra, 0.9)); + v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a); + v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b); v_width2 = vec2(outset, inset); - v_gamma_scale = perspective_scale * squish_scale; }