Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SurfaceShape picking is unpredictable #27

Closed
ComBatVision opened this issue Apr 18, 2019 · 1 comment · Fixed by #30
Closed

SurfaceShape picking is unpredictable #27

ComBatVision opened this issue Apr 18, 2019 · 1 comment · Fixed by #30
Assignees
Labels

Comments

@ComBatVision
Copy link
Member

Picking of any SurfaceShapes is unpredictable, because JS canvas still does not allow to switch off anti-aliasing for strokes during rendering of pick frame.
This issue makes picking of SurfaceShapes unusable (see animation below).

  1. Create several SurfacePolylines close to each other so that they will be located on the same terrain tile.
  2. Try to pick one of them on mouse move.
  3. You will see that it picks random shape located on this tile.
    54945856-7a020f00-4f3f-11e9-8266-25d8852dc801

This problem appears because anti-aliasing not only make contour pixels transparent, but also make color value average between shape color and background. This average color is equal to pick color of some other shape in tile.

54986976-58933880-4fbc-11e9-9671-5188d9b4cc32

To fix this issue you need to do three steps:

  1. Disable anti-aliasing for path stroke on canvas to avoid appearance of semi-transparent pixels with wrong average color on the edge of the shape.

As I have researched - there is no possibility to disable canvas anti-aliasing for shapes. There is an option only for images. That's why we have two possibilities: ignore pick color with alpha != 1 during pick procedure or remove semi-transparent pixels which may contain wrong color from resulted tile. First approach is faster, but require changes in several places and may have some side effect on other renderables selection except surface shapes. That's why I propose to go with second approach - remove semi transparent pixels in SurfaceShapeTile.prototype.updateTexture.

                shape.renderToTexture(dc, ctx2D, xScale, yScale, xOffset, yOffset);
            }

            // Remove semi-transparent pixels which may contain wrong pick color
            if (dc.pickingMode) {
              const imageData = ctx2D.getImageData(0, 0, canvas.width, canvas.height);
              for (let i = 3, n = canvas.width * canvas.height * 4; i < n; i += 4) {
                if (imageData.data[i] < 255) {
                  imageData.data[i - 3] = 0;
                  imageData.data[i - 2] = 0;
                  imageData.data[i - 1] = 0;
                  imageData.data[i] = 0;
                }
              }
              ctx2D.putImageData(imageData, 0, 0);
            }

            this.gpuCacheKey = this.getCacheKey();
  1. We need to use NEAREST value for TEXTURE_MIN_FILTER and TEXTURE_MAG_FILTER in pickingMode the same way as in WorldWindAndroid, to avoid appearance of average color pixels during texture filtering in WebGL.
        Texture.prototype.applyTexParameters = function (dc) {
            var gl = dc.currentGlContext;

            // Configure the OpenGL texture minification function. Use nearest in pickingMode or linear by default.
            var textureMinFilter = dc.pickingMode ? gl.NEAREST : this.texParameters[gl.TEXTURE_MIN_FILTER] || gl.LINEAR;
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, textureMinFilter);

            // Configure the OpenGL texture magnification function. Use nearest in pickingMode or linear by default.
            var textureMagFilter = dc.pickingMode ? gl.NEAREST : this.texParameters[gl.TEXTURE_MAG_FILTER] || gl.LINEAR;
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, textureMagFilter);

            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.texParameters[gl.TEXTURE_WRAP_S] || gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.texParameters[gl.TEXTURE_WRAP_T] || gl.CLAMP_TO_EDGE);

            // Try to enable the anisotropic texture filtering only if we have a linear magnification filter.
            // This can't be enabled all the time because Windows seems to ignore the TEXTURE_MAG_FILTER parameter when
            // this extension is enabled.
            if (textureMagFilter === gl.LINEAR) {
                // Setup 4x anisotropic texture filtering when this feature is available.
                if (this.anisotropicFilterExt) {
                    gl.texParameteri(gl.TEXTURE_2D, this.anisotropicFilterExt.TEXTURE_MAX_ANISOTROPY_EXT, 4);
                }
            }
        };
  1. Remove texture parameters setting from Texture constructor, as it now located in applyTextParameters.
            gl.bindTexture(gl.TEXTURE_2D, textureId);

            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
            gl.texImage2D(gl.TEXTURE_2D, 0,
                gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);

            if (isPowerOfTwo) {
                gl.generateMipmap(gl.TEXTURE_2D);
            }

            this.textureId = textureId;

            /**
             * The time at which this texture was created.
             * @type {Date}
             */
            this.creationTime = new Date();

            // Internal use only. Intentionally not documented.
            this.texParameters = {};
            this.texParameters[gl.TEXTURE_MIN_FILTER] = isPowerOfTwo ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR;
            this.texParameters[gl.TEXTURE_WRAP_S] = wrapMode;
            this.texParameters[gl.TEXTURE_WRAP_T] = wrapMode;

Second and third part is a 100% MUST, but first part of my fix is little bit performance ineffective. So if anybody knows better way to avoid wrong color pixels during shape anti-alising or knows how to disable anti-alising, please, let me know.

@emxsys
Copy link
Member

emxsys commented Apr 23, 2019

@Sufaev Great detective work and write-up on this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants