在片段着色器中线性过滤浮点纹理的伪像

时间:2016-02-04 08:54:43

标签: opengl-es webgl fragment-shader

我使用以下来自this tutorial的代码对WebGL中的片段着色器中的浮点纹理执行线性过滤:

inserted

在Nvidia GPU上看起来很不错,但在另外两台配备Intel集成GPU的电脑上看起来像这样:

enter image description here

enter image description here

enter image description here

enter image description here

出现较浅或较暗的线条,不应该存在。如果放大,它们会变得可见,并且缩放得越多,它们就越频繁。当非常接近地放大时,它们出现在我过滤的纹理的每个纹素的边缘。我尝试更改片段着色器中的精度语句,但这并没有解决它。

内置线性滤波适用于两个GPU,但我仍然需要手动过滤作为不支持使用WebGL对浮点纹理进行线性过滤的GPU的后备。

英特尔GPU来自台式机Core i5-4460和配备Intel HD 5500 GPU的笔记本电脑。对于浮点值的所有精度,我得到的rangeMin和rangeMax为127,精度为23 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. }

有关导致这些文物的原因以及我如何解决它的任何想法?

修改

通过多尝试一下,我发现在片段着色器中减少纹素大小变量会删除这些伪像:

getShaderPrecisionFormat

乘以0.999还不够,但将纹素大小乘以0.998会消除伪影。

这显然不是一个令人满意的解决方案,我仍然不知道是什么导致它,我现在可能在其他GPU或驱动程序上造成了伪影。所以我仍然有兴趣弄清楚实际问题是什么。

2 个答案:

答案 0 :(得分:1)

我不清楚代码正在尝试执行什么操作。它不会复制GPU的双线性,因为那将使用以texcoord为中心的像素。

换句话说,已实施

vec4 c = tex2DBiLinear(someSampler, someTexcoord);

不是等同于LINEAR

vec4 c = texture2D(someSampler, someTexcoord);

texture2D查看像素someTexcoord +/- texelSize * .5,其中tex2DBiLinear查看像素someTexcoordsomeTexcoord + texelSize

您没有提供足够的代码来回购问题。我猜测源纹理的大小是512x1024,但是由于您没有发布该代码,所以我不知道您的源纹理是否与定义的大小匹配。您也没有发布目标的大小。您发布的顶部图片是471x488。那是您的目标尺寸吗?您也没有发布有关所使用的纹理坐标以及对其进行操作的代码的代码。

猜测您的来源是512x1024,目标是471x488,我无法回复您的问题。

const fs = `
precision highp float;

uniform sampler2D tex;
varying vec2 v_texcoord;

float tex2DBiLinear( sampler2D textureSampler_i, vec2 texCoord_i )
{
float fHeight = 1024.0;
float fWidth = 512.0;
float texelSizeX = 1.0/fWidth;
float texelSizeY = 1.0/fHeight;

    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.
}

void main() {
  gl_FragColor = vec4(tex2DBiLinear(tex, v_texcoord), 0, 0, 1);
}
`;

const vs = `
attribute vec4 position;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main() {
  gl_Position = position;
  v_texcoord = texcoord;
}
`;

const gl = document.querySelector('canvas').getContext('webgl');
// compile shaders, link programs, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position: {
    numComponents: 2,
    data: [
      -1, -1,
       1, -1,
      -1,  1,
       1,  1,
    ],
  },
  texcoord: [
    0, 0,
    1, 0,
    0, 1,
    1, 1,
  ],
  indices: [
    0, 1, 2,
    2, 1, 3,
  ],
});


const ctx = document.createElement('canvas').getContext('2d');
ctx.canvas.width = 512;
ctx.canvas.height = 1024;
const gradient = ctx.createRadialGradient(256, 512, 0, 256, 512, 700);

gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'cyan');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 512, 1024);

const tex = twgl.createTexture(gl, {
  src: ctx.canvas,
  minMag: gl.NEAREST,
  wrap: gl.CLAMP_TO_EDGE,
  auto: false,
});

gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas width="471" height="488"></canvas>

如果您认为该问题与浮点纹理有关,我也无法在此回购

const fs = `
precision highp float;

uniform sampler2D tex;
varying vec2 v_texcoord;

float tex2DBiLinear( sampler2D textureSampler_i, vec2 texCoord_i )
{
float fHeight = 1024.0;
float fWidth = 512.0;
float texelSizeX = 1.0/fWidth;
float texelSizeY = 1.0/fHeight;

    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.
}

void main() {
  gl_FragColor = vec4(tex2DBiLinear(tex, v_texcoord), 0, 0, 1);
}
`;

const vs = `
attribute vec4 position;
attribute vec2 texcoord;
varying vec2 v_texcoord;
void main() {
  gl_Position = position;
  v_texcoord = texcoord;
}
`;

const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('OES_texture_float');
if (!ext) { alert('need OES_texture_float'); }
// compile shaders, link programs, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position: {
    numComponents: 2,
    data: [
      -1, -1,
       1, -1,
      -1,  1,
       1,  1,
    ],
  },
  texcoord: [
    0, 0,
    1, 0,
    0, 1,
    1, 1,
  ],
  indices: [
    0, 1, 2,
    2, 1, 3,
  ],
});


const ctx = document.createElement('canvas').getContext('2d');
ctx.canvas.width = 512;
ctx.canvas.height = 1024;
const gradient = ctx.createRadialGradient(256, 512, 0, 256, 512, 700);

gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'cyan');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 512, 1024);

const tex = twgl.createTexture(gl, {
  src: ctx.canvas,
  type: gl.FLOAT,
  minMag: gl.NEAREST,
  wrap: gl.CLAMP_TO_EDGE,
  auto: false,
});

gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
const e = gl.getExtension('WEBGL_debug_renderer_info');
if (e) {
  console.log(gl.getParameter(e.UNMASKED_VENDOR_WEBGL));
  console.log(gl.getParameter(e.UNMASKED_RENDERER_WEBGL));
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas width="471" height="488"></canvas>

如果任何一个值都关闭。如果您的源纹理大小与fWidthfHeigth不匹配,或者您的纹理坐标不同或以某种方式进行了调整,那么我当然可以回购。如果这些都不相同,那么我可以想象问题。

在Intel Iris Pro和Intel HD Graphics 630上进行了测试。也在iPhone6 +上进行了测试。请注意,您需要确保片段着色器在precision highp float中运行,但是该设置可能只会影响移动GPU。

答案 1 :(得分:0)

我们遇到了几乎相同的问题,这些问题在特定的纹理缩放时发生。我们发现在这种情况下可以检测到伪影的位置:

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

其中 imageSize 是纹理的宽度和高度。

我们的解决方案如下所示:

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 );
}

这确实是一个肮脏的解决方案,但它有效。按照上面的建议更改 texelSize 只会将工件移动到另一个位置。我们只在有问题的位置上稍微改变了 texelSize。

为什么我们在 GLSL 着色器中使用线性纹理插值?这是因为我们需要使用每个像素 1 个样本 16 位每个样本纹理与广泛的兼容设备。只能使用 OES_texture_half_float_linear extension 来实现。通过我们的方法,可以在不使用扩展的情况下解决它。