From 23a30e6716e91b80d9669877805fc7e7d14c554c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 29 Apr 2013 18:02:19 -0400 Subject: [PATCH 1/4] Reproject imagery in the vertex shader instead of FS. This avoids fragment shader precision problems in mobile devices. --- Source/Scene/ImageryLayer.js | 22 +++++++++++++++++++--- Source/Shaders/ReprojectWebMercatorFS.glsl | 20 +------------------- Source/Shaders/ReprojectWebMercatorVS.glsl | 10 +++++++++- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index 17e76a2720ed..b2396d44c3f2 100755 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -23,6 +23,7 @@ define([ './TileProviderError', './ImageryState', './TileImagery', + './TerrainProvider', './TexturePool', '../ThirdParty/when', '../Shaders/ReprojectWebMercatorFS', @@ -51,6 +52,7 @@ define([ TileProviderError, ImageryState, TileImagery, + TerrainProvider, TexturePool, when, ReprojectWebMercatorFS, @@ -778,14 +780,28 @@ define([ reproject.framebuffer = context.createFramebuffer(); reproject.framebuffer.destroyAttachments = false; + var positions = []; + for (var j = 0; j < 256; ++j) { + var y = j / 255.0; + for (var i = 0; i < 256; ++i) { + var x = i / 255.0; + positions.push(x); + positions.push(y); + } + } + var reprojectMesh = { attributes : { position : { componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 2, - values : [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0] + values : positions } - } + }, + indexLists : [{ + primitiveType : PrimitiveType.TRIANGLES, + values : TerrainProvider.getRegularGridIndices(256, 256) + }] }; var reprojectAttribInds = { @@ -870,7 +886,7 @@ define([ framebuffer : reproject.framebuffer, shaderProgram : reproject.shaderProgram, renderState : reproject.renderState, - primitiveType : PrimitiveType.TRIANGLE_FAN, + primitiveType : PrimitiveType.TRIANGLES, vertexArray : reproject.vertexArray, uniformMap : uniformMap }); diff --git a/Source/Shaders/ReprojectWebMercatorFS.glsl b/Source/Shaders/ReprojectWebMercatorFS.glsl index b24a7b185992..58852482c392 100644 --- a/Source/Shaders/ReprojectWebMercatorFS.glsl +++ b/Source/Shaders/ReprojectWebMercatorFS.glsl @@ -1,26 +1,8 @@ uniform sampler2D u_texture; -uniform float u_northLatitude; -uniform float u_southLatitude; -uniform float u_southMercatorYHigh; -uniform float u_southMercatorYLow; -uniform float u_oneOverMercatorHeight; - varying vec2 v_textureCoordinates; void main() { - // The clamp below works around an apparent bug in Chrome Canary v23.0.1241.0 - // where the fragment shader sees textures coordinates < 0.0 and > 1.0 for the - // fragments on the edges of tiles even though the vertex shader is outputting - // coordinates strictly in the 0-1 range. - vec2 geographicUV = clamp(v_textureCoordinates, 0.0, 1.0); - vec2 webMercatorUV = geographicUV; - - float currentLatitude = mix(u_southLatitude, u_northLatitude, geographicUV.y); - float fraction = czm_latitudeToWebMercatorFraction(currentLatitude, u_southMercatorYLow, u_southMercatorYHigh, u_oneOverMercatorHeight); - - webMercatorUV = vec2(geographicUV.x, fraction); - - gl_FragColor = texture2D(u_texture, webMercatorUV); + gl_FragColor = texture2D(u_texture, v_textureCoordinates); } diff --git a/Source/Shaders/ReprojectWebMercatorVS.glsl b/Source/Shaders/ReprojectWebMercatorVS.glsl index 6f4e7ba87fc6..2dcc2fef40cc 100644 --- a/Source/Shaders/ReprojectWebMercatorVS.glsl +++ b/Source/Shaders/ReprojectWebMercatorVS.glsl @@ -2,10 +2,18 @@ attribute vec4 position; uniform vec2 u_textureDimensions; +uniform float u_northLatitude; +uniform float u_southLatitude; +uniform float u_southMercatorYHigh; +uniform float u_southMercatorYLow; +uniform float u_oneOverMercatorHeight; + varying vec2 v_textureCoordinates; void main() { - v_textureCoordinates = position.xy; + float currentLatitude = mix(u_southLatitude, u_northLatitude, position.y); + float fraction = czm_latitudeToWebMercatorFraction(currentLatitude, u_southMercatorYLow, u_southMercatorYHigh, u_oneOverMercatorHeight); + v_textureCoordinates = vec2(position.x, fraction); gl_Position = czm_viewportOrthographic * (position * vec4(u_textureDimensions, 1.0, 1.0)); } From a7838bd8e05db6bcb3227c346b608682c9594818 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 30 Apr 2013 12:05:10 -0400 Subject: [PATCH 2/4] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 3b144e1e2126..f8f8d24e39d7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -44,6 +44,7 @@ Beta Releases * Improved rendering performance by minimizing WebGL state calls. * Fixed an error in Web Worker creation when loading Cesium.js from a different origin. * Fixed `EllipsoidPrimitive` picking and picking objects with materials that have transparent parts. +* Fixed imagery smearing artifacts on mobile devices and other devices without high-precision fragment shaders. ### b15 - 2013-04-01 From c849941eccb5390412e50a77e3b35b3baa284055 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 30 Apr 2013 14:06:59 -0400 Subject: [PATCH 3/4] Slight optimization, and comment explaining approach. --- Source/Scene/ImageryLayer.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index b2396d44c3f2..518c95aa9c84 100755 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -780,13 +780,22 @@ define([ reproject.framebuffer = context.createFramebuffer(); reproject.framebuffer.destroyAttachments = false; - var positions = []; + // We need a vertex array with close to one vertex per output texel because we're doing + // the reprojection by computing texture coordinates in the vertex shader. + // If we computed Web Mercator texture coordinate per-fragment instead, we could get away with only + // four vertices. Problem is: fragment shaders have limited precision on many mobile devices, + // leading to all kinds of smearing artifacts. Current browsers (Chrome 26 for example) + // do not correctly report the available fragment shader precision, so we can't have different + // paths for devices with or without high precision fragment shaders, even if we want to. + + var positions = new Array(256 * 256 * 2); + var index = 0; for (var j = 0; j < 256; ++j) { var y = j / 255.0; for (var i = 0; i < 256; ++i) { var x = i / 255.0; - positions.push(x); - positions.push(y); + positions[++index] = x; + positions[++index] = y; } } From aa49715b143d29eb56a29e3c8d4889c25cb70998 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 30 Apr 2013 15:02:59 -0400 Subject: [PATCH 4/4] Fix my failure to increment properly. --- Source/Scene/ImageryLayer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index 518c95aa9c84..7a02e5342eb7 100755 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -794,8 +794,8 @@ define([ var y = j / 255.0; for (var i = 0; i < 256; ++i) { var x = i / 255.0; - positions[++index] = x; - positions[++index] = y; + positions[index++] = x; + positions[index++] = y; } }