【问题标题】:What is this technique, if not bilinear filtering?如果不是双线性过滤,这种技术是什么?
【发布时间】:2017-01-15 17:11:16
【问题描述】:

我正在尝试使用下面的代码复制 Unity3D 的自动双线性过滤算法:

fixed4 GetBilinearFilteredColor(float2 texcoord)
{
    fixed4 s1 = SampleSpriteTexture(texcoord + float2(0.0, _MainTex_TexelSize.y));
    fixed4 s2 = SampleSpriteTexture(texcoord + float2(_MainTex_TexelSize.x, 0.0));
    fixed4 s3 = SampleSpriteTexture(texcoord + float2(_MainTex_TexelSize.x, _MainTex_TexelSize.y));
    fixed4 s4 = SampleSpriteTexture(texcoord);

    float2 TexturePosition = float2(texcoord)* _MainTex_TexelSize.z;

    float fu = frac(TexturePosition.x);
    float fv = frac(TexturePosition.y);

    float4 tmp1 = lerp(s4, s2, fu);
    float4 tmp2 = lerp(s1, s3, fu);

    return lerp(tmp1, tmp2, fv);
}

fixed4 frag(v2f IN) : SV_Target
{
    fixed4 c = GetBilinearFilteredColor(IN.texcoord) * IN.color;
    c.rgb *= c.a;
    return c;
}

我认为我使用了正确的算法,因为这是我见过的唯一一种双线性算法。但我尝试使用统一的相同纹理复制:

  • 1º 纹理:在点过滤中并使用自定义双线性着色器(由默认精灵着色器制成)。
  • 2º 纹理:在双线性过滤器中使用默认精灵着色器

结果如下:


您可以看到它们是不同的,而且我的自定义着色器中存在一些位移,这使得精灵在 Z 轴上旋转时会偏离中心。

知道我做错了什么吗? 知道 Unity3D 有什么不同吗? 是否有其他算法适合 Unity3D 默认过滤?

解决方案

更新了完整的代码解决方案,其中包含 Nico 的代码,供在此处搜索的其他人使用:

fixed4 GetBilinearFilteredColor(float2 texcoord)
{
    fixed4 s1 = SampleSpriteTexture(texcoord + float2(0.0, _MainTex_TexelSize.y));
    fixed4 s2 = SampleSpriteTexture(texcoord + float2(_MainTex_TexelSize.x, 0.0));
    fixed4 s3 = SampleSpriteTexture(texcoord + float2(_MainTex_TexelSize.x, _MainTex_TexelSize.y));
    fixed4 s4 = SampleSpriteTexture(texcoord);

    float2 TexturePosition = float2(texcoord)* _MainTex_TexelSize.z;

    float fu = frac(TexturePosition.x);
    float fv = frac(TexturePosition.y);

    float4 tmp1 = lerp(s4, s2, fu);
    float4 tmp2 = lerp(s1, s3, fu);

    return lerp(tmp1, tmp2, fv);
}

fixed4 frag(v2f IN) : SV_Target
{
    fixed4 c = GetBilinearFilteredColor(IN.texcoord - 0.498 * _MainTex_TexelSize.xy) * IN.color;
    c.rgb *= c.a;
    return c;
}

以及带有结果的图像测试:

为什么不准确减去 0.5?

如果您对其进行测试,您会看到一些边缘情况,它会跳转到 (pixel - 1)。

【问题讨论】:

    标签: opengl unity3d shader fragment-shader


    【解决方案1】:

    让我们仔细看看您实际在做什么。我会坚持使用一维案例,因为它更容易可视化。

    你有一个像素数组和一个纹理位置。我假设,_MainTex_TexelSize.z 以某种方式设置,以便它提供像素坐标。这就是你得到的(方框代表像素,方框中的数字是像素数和像素空间坐标下方的数字):

    通过您的采样(假设最近点采样),您将获得像素 2 和 3。但是,您会发现 lerp 的插值坐标实际上是错误的。您将传递纹理位置的小数部分(即0.8),但它应该是0.3= 0.8 - 0.5)。这背后的原因很简单:如果你落在一个像素的中心,你想使用像素值。如果你正好落在两个像素之间的中间,你想使用两个像素值的平均值(即插值0.5)。现在,您基本上向左偏移了半个像素。

    当你解决了第一个问题时,还有第二个问题:

    在这种情况下,您实际上想要在像素 1 和 2 之间进行混合。但是因为您在采样中总是向右移动,所以您将在 2 和 3 之间进行混合。同样,插值错误。

    解决方案应该很简单:在对纹理坐标进行任何操作之前,从纹理坐标中减去一半像素宽度,这可能只是以下内容(假设您的变量包含我认为的内容):

    fixed4 c = GetBilinearFilteredColor(IN.texcoord - 0.5 * _MainTex_TexelSize.xy) * IN.color;
    

    结果不同的另一个原因可能是 Unity 实际上使用了不同的过滤器,例如双三次(但我不知道)。此外,mipmap 的使用可能会影响结果。

    【讨论】:

      猜你喜欢
      • 2014-07-24
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多