Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Artifacts from linear filtering a floating point texture in the fragment shader

I'm using the following code taken from this tutorial to perform linear filtering on a floating point texture in my fragment shader in WebGL:

float fHeight = 512.0;
float fWidth = 1024.0;
float texelSizeX = 1.0/fWidth;
float texelSizeY = 1.0/fHeight;

float tex2DBiLinear( sampler2D textureSampler_i, vec2 texCoord_i )
{
    float p0q0 = texture2D(textureSampler_i, texCoord_i)[0];
    float p1q0 = texture2D(textureSampler_i, texCoord_i + vec2(texelSizeX, 0))[0];

    float p0q1 = texture2D(textureSampler_i, texCoord_i + vec2(0, texelSizeY))[0];
    float p1q1 = texture2D(textureSampler_i, texCoord_i + vec2(texelSizeX , texelSizeY))[0];

    float a = fract( texCoord_i.x * fWidth ); // Get Interpolation factor for X direction.
                    // Fraction near to valid data.

    float pInterp_q0 = mix( p0q0, p1q0, a ); // Interpolates top row in X direction.
    float pInterp_q1 = mix( p0q1, p1q1, a ); // Interpolates bottom row in X direction.

    float b = fract( texCoord_i.y * fHeight );// Get Interpolation factor for Y direction.
    return mix( pInterp_q0, pInterp_q1, b ); // Interpolate in Y direction.
}

On an Nvidia GPU this looks fine, but on two other computers with an Intel integrated GPU it looks like this:

enter image description here

enter image description here

enter image description here

enter image description here

There are lighter or darker lines appearing that shouldn't be there. They become visible if you zoom in, and tend to get more frequent the more you zoom. When zooming in very closely, they appear at the edge of every texel of the texture I'm filtering. I tried changing the precision statement in the fragment shader, but this didn't fix it.

The built-in linear filtering works on both GPUs, but I still need the manual filtering as a fallback for GPUs that don't support linear filtering on floating point textures with WebGL.

The Intel GPUs are from a desktop Core i5-4460 and a notebook with an Intel HD 5500 GPU. For all precisions of floating point values I get a rangeMin and rangeMax of 127 and a precision of 23 from getShaderPrecisionFormat.

Any idea on what causes these artifacts and how I can work around it?

Edit:

By experimenting a bit more I found that reducing the texel size variable in the fragment shader removes these artifacts:

float texelSizeX = 1.0/fWidth*0.998;
float texelSizeY = 1.0/fHeight*0.998;

Multiplying by 0.999 isn't enough, but multiplying the texel size by 0.998 removes the artifacts.

This is obviously not a satisfying fix, I still don't know what causes it and I probably caused artifacts on other GPUs or drivers now. So I'm still interested in figuring out what the actual issue is here.

like image 506
Mad Scientist Avatar asked Feb 04 '16 08:02

Mad Scientist


1 Answers

We had almost identical issue that ocurred at specific zoom of texture. We found out that positions where artifacts appers can be detected with this conditions:

vec2 imagePosCenterity = fract(uv * imageSize);
if (abs(imagePosCenterity.x-0.5) < 0.001 || abs(imagePosCenterity.y-0.5) < 0.001) {}

Where imageSize is width and height of the texture.

Our solution looks like this:

vec4 texture2DLinear( sampler2D texSampler, vec2 uv) {
    vec2 pixelOff = vec2(0.5,0.5)/imageSize;

    vec2 imagePosCenterity = fract(uv * imageSize);
    if (abs(imagePosCenterity.x-0.5) < 0.001 || abs(imagePosCenterity.y-0.5) < 0.001) {
        pixelOff = pixelOff-vec2(0.00001,0.00001);
    }

    vec4 tl = texture2D(texSampler, uv + vec2(-pixelOff.x,-pixelOff.y));
    vec4 tr = texture2D(texSampler, uv + vec2(pixelOff.x,-pixelOff.y));
    vec4 bl = texture2D(texSampler, uv + vec2(-pixelOff.x,pixelOff.y));
    vec4 br = texture2D(texSampler, uv + vec2(pixelOff.x,pixelOff.y));
    vec2 f = fract( (uv.xy-pixelOff) * imageSize );
    vec4 tA = mix( tl, tr, f.x );
    vec4 tB = mix( bl, br, f.x );
    return mix( tA, tB, f.y );
}

It is really dirty solution but it works. Changing texelSize as suggested above only moves artifacts to another positions. We are changing texelSize a little bit only on problematic positions.

Why we are using linear texture interpolation in GLSL shader? It is because we need to use 1 sample per pixel 16 bit per sample texture with broad set of compatibile devices. It is possible to do it only with OES_texture_half_float_linear extension. By our approach it is possible to solve it without using extension.

like image 105
Ivo Skalický Avatar answered Oct 30 '22 06:10

Ivo Skalický