跳转到内容

双线性过滤:修订间差异

维基百科,自由的百科全书
删除的内容 添加的内容
Sterrys留言 | 贡献
无编辑摘要
 
西瓜玩偶留言 | 贡献
修改方程使得方程与文字描述相符。
 
(未显示11个用户的24个中间版本)
第1行: 第1行:
{{noteTA|G1=IT|time=2010-02-28T12:33:28+00:00}}
{{translation}}
[[File:Image-resample-sample.png|right|frame|经过[[缩放]]的一小部分[[位图]]图像,左图使用了[[最近邻插值]]过滤,中间的图用了双线性过滤,右图是用了立方过滤。[[立方插值]]过滤类似于双线性插值]]

'''双线性滤'''('''''Bilinear filtering''''')是进行缩放显示的时候进行纹理平滑的一种[[纹理滤]]方法。
[[Image:Pixel_interpolation.png|right|frame|A [[zoom|zoomed]] small portion of a [[bitmap]] of a [[cat]], using [[nearest neighbor interpolation|nearest neighbor]] filtering ''(left)'' and bicubic filtering ''(right)''. [[Bicubic interpolation|Bicubic filtering]] is similar to bilinear filtering but with a different interpolation function.]]
在大多数情况下,纹理在屏幕上显示的时候都不会同保存的纹理一模一样,没有任何失。正因为这样,所以一些像素要使用[[纹素]]之间的点进行表示,在这里我们假设纹素都是位于各个单元中心或者左上或者其它位置的点。双线性滤器利用像素所表示点周围四个最近的点(纹素点)之间进行[[双线性插值]]。
'''双线性滤'''是进行缩放显示的时候进行纹理平滑的一种[[纹理滤]]方法。
在大多数情况下,纹理在屏幕上显示的时候都不会同保存的纹理一模一样,没有任何失。正因为这样,所以一些像素要使用[[纹素]]之间的点进行表示,在这里我们假设纹素都是位于各个单元中心或者左上或者其它位置的点。双线性滤器利用这些点在像素所表示点周围四个最近的点之间进行[[双线性插值]]。


==公式==
==公式==
在下面这些方程中,u<sub>k</sub> 与 v<sub>k</sub> 是纹理坐标,y<sub>k</sub> 是点 k 处的颜色值。不带下标的值表示像素点,带有下标 0、1、2、3 的值表示从左上到下包围像素的纹素点 这就是[[linear interpolation]] equations. We'd start with the bilinear equation, but since this is a special case with some elegant results, it is easier to start from linear interpolation.
在下面这些方程中,u<sub>k</sub> 与 v<sub>k</sub> 是点 k 处的纹理坐标,y<sub>k</sub> 是点 k 处的颜色值。不带下标的值表示像素点,带有下标 0、1、2、3 的值表示从左上沿顺时针方向下包围像素的纹素点,带有下标 a、b 的值分别表示像素点在点0、1的连线与点3、2连线上的投影点。由于双线性插值方程是[[线性插值]]方程的一种特殊形式,所以我们从较简单的线性插值方程开始分析。


:<math>y_a = y_0 + \frac{y_1-y_0}{u_1-u_0}(u-u_0) \,\!</math>
:<math>y_a = y_0 + \frac{y_1-y_0}{u_1-u_0}(u-u_0) \,\!</math>
:<math>y_b = y_2 + \frac{y_3-y_2}{u_3-u_2}(u-u_2) \,\!</math>
:<math>y_b = y_3 + \frac{y_2-y_3}{u_2-u_3}(u-u_3) \,\!</math>
:<math>y = y_b + \frac{y_b-y_a}{v_2-v_0}(v-v_0) \,\!</math>
:<math>y = y_a + \frac{y_b-y_a}{v_3-v_0}(v-v_0) \,\!</math>


假设纹理是正方形的位图,并且满足
Assuming that the texture is a square bitmap,


:<math>v_1 = v_0 \,\!</math>
:<math>v_0 = v_1 \,\!</math>
:<math>v_2 = v_3 \,\!</math>
:<math>v_3 = v_2 \,\!</math>
:<math>u_1 = u_3 \,\!</math>
:<math>u_0 = u_3 \,\!</math>
:<math>u_2 = u_0 \,\!</math>
:<math>u_1 = u_2 \,\!</math>
:<math>v_3 - v_0 = u_3 - u_0 = w \,\!</math>
:<math>v_2 - v_0 = u_1 - u_3 = w \,\!</math>


我们进一步定义,
Are all true. Further, define


:<math>U = \frac{u - u_0}{w} \,\!</math>
:<math>U = \frac{u - u_0}{w} \,\!</math>
:<math>V = \frac{v - v_0}{w} \,\!</math>
:<math>V = \frac{v - v_0}{w} \,\!</math>


这样就可以将插值方程化简为:
With these we can simplify the interpolation equations:


:<math>y_a = y_0 + (y_1-y_0)U \,\!</math>
:<math>y_a = y_0 + (y_1-y_0)U \,\!</math>
:<math>y_b = y_2 + (y_3-y_2)U \,\!</math>
:<math>y_b = y_3 + (y_2-y_3)U \,\!</math>
:<math>y = y_a + (y_b-y_a)V \,\!</math>
:<math>y = y_a + (y_b-y_a)V \,\!</math>


代入方程,得到:
And combine them:


:<math>y = y_0 + (y_1 - y_0)U + (y_2 - y_0)V + (y_3 - y_2 - y_1 + y_0)UV \,\!</math>
:<math>y = y_0 + (y_1-y_0)U + (y_3 - y_0)V + (y_2 - y_3 - y_1 + y_0)UV \,\!</math>


或者,
Or, alternatively:


:<math>y = y_0(1 - U)(1 - V) + y_1 U (1 - V) + y_2 (1 - U) V + y_3 U V \,\!</math>
:<math>y = y_0(1 - U)(1 - V) + y_1 U (1 - V) + y_3 V (1 - U) + y_2 U V \,\!</math>


这种表示相当简单。但是,如果图像只进行缩放处理,而没有旋转、扭曲、透视或者其它处理,那么使用独立的方程计算并且保存用于后面数据行计算的 y<sub>b</sub> 或者 y<sub>a</sub> 速度将更快。
Which is rather convenient. However, if the image is merely scaled (and not rotated, sheared, put into perspective, or any other manipulation), it can be considerably faster to use the separate equations and store y<sub>b</sub> (and sometimes y<sub>a</sub>, if we are increasing the scale) for use in subsequent rows.


== Sample Code ==
==示例代码==

This code assumes that the texture is square (an extremely common occurrence), that no [[Mipmap|mipmapping]] comes into play, and that there is only one channel of data (not so common. Nearly all textures are in color so they have red, green, and blue channels, and many have an alpha transparency channel, so we must make three or four calculations of y, one for each channel).
这部分代码假设纹理是常见的正方形,没有[[Mipmap]],并且只有一个通道的数据。只有一个通道的情况非常少见,几乎所有的纹理都是彩色的,都有红色、绿色与蓝色通道,有些还有阿尔法透明通道,所以每个 y 都要进行三到四次的计算


double getBilinearFilteredPixelColor(Texture tex, double u, double v) {
double getBilinearFilteredPixelColor(Texture tex, double u, double v) {
第58行: 第58行:
}
}


== Limitations ==
==局限==
Bilinear filtering is rather accurate until the scaling of the texture gets below half or above double the original size of the texture - that is, if the texture was 256 pixels in each direction, scaling it to below 128 or above 512 pixels can make the texture look bad, because of missing pixels or too much smoothness. Often, [[Mipmap|mipmapping]] is used to provide a scaled-down version of the texture for better performance; however, the transition between two differently-sized mipmaps on a texture in perspective using bilinear filtering can be very abrupt. [[Trilinear filtering]], though somewhat more complex, can make this transition smooth throughout.


在纹理缩减到一半或者放大一倍的范围内,双线性过滤都能够有非常好的精度。这也就是说,如果纹理在每个方向都有 256 个像素,那么将它缩减到 128 以下或者放大到 512 以上的时候,由于会丢掉太多的像素或者进行了过多的平滑处理,纹理看起来就会很差。通常,可以在缩减的过程中使用 [[Mipmap]] 来实现较好的性能;但是,在透视图中的纹理上的经过双线性过滤处理的两个不同尺寸的 mipmap 之间的过渡将非常明显。[[三线性过滤]]尽管比较复杂,但是可以使得过渡非常平滑。
For a quick demonstration of how a texel can be missing from a filtered texture, here's a list of numbers representing the centers of boxes from an 8-texel-wide texture, intermingled with the numbers from the centers of boxes from a 3-texel-wide texture (in blue). The red numbers represent texels that would not be used in calculating the 3-texel texture at all.

为了快速说明纹理过滤中如何丢失纹素,这里有一组用来自于 8 纹素宽纹理的数字表示的盒子的中心,它们与蓝色表示的来自于 3 纹素宽的纹理表示的盒子中心的一组数字混杂在一起。红色数字表示计算 3 纹素纹理中根本不需要的纹素。


0.0625, <span style="color: blue">0.1667</span>, 0.1875, <span style="color: red">0.3125</span>, 0.4375, <span style="color: blue">0.5000</span>, 0.5625, <span style="color: red">0.6875</span>, 0.8125, <span style="color: blue">0.8333</span>, 0.9375
0.0625, <span style="color: blue">0.1667</span>, 0.1875, <span style="color: red">0.3125</span>, 0.4375, <span style="color: blue">0.5000</span>, 0.5625, <span style="color: red">0.6875</span>, 0.8125, <span style="color: blue">0.8333</span>, 0.9375


== Special cases ==
==特殊情况==
Textures aren't infinite, in general, and sometimes you end up with a pixel coordinate that lies outside the grid of texel coordinates. There are a few ways to handle this:


通常纹理是有限大小的,我们经常会得到坐标位于纹素坐标之外栅格之外的像素。可以用以下几种方法来处理这种情况:
*Wrap the texture, so that the last texel in a row also comes right before the first, and the last texel in a column also comes right above the first. This works best when the texture is being tiled.

*Make the area outside the texture all one color. This is probably not that great an idea, but it might work if the texture is designed to be laid over a solid background or be transparent.
*旋绕纹理,这样一行中的最后一个纹素将出现在该行第一个纹素的前面,一列中的最后一个纹素将出现在该列第一个纹素前面。在纹理平铺的时候这种方法可以得到最好的效果。
*Repeat the edge texels out to infinity. This works well if the texture is designed to not be repeated.
*纹理之外的区域使用一种颜色。这可能并不是一个非常了不起的想法,但是如果纹理要放到固体或者透明背景上,那么就可以使用这种方法。
*无限重复边界纹素。如果所设计的纹理不是要重复使用的话,这种方法可以很好地工作。


==参见==
==参见==
*[[三线性滤]]
*[[三线性滤]]
*[[各向异性滤]]
*[[各向异性滤]]


[[Category:计算机图形学]]
[[Category:计算机图形学]]
[[Category:三维计算机图形学]]
[[Category:三维计算机图形学]]

[[de:Bilineare Filterung]]
[[en:Bilinear filtering]]
[[fr:Filtrage bilinéaire]]
[[ko:이중선형 필터링]]

2019年11月1日 (五) 08:41的最新版本

经过缩放的一小部分位图图像,左图使用了最近邻插值过滤,中间的图用了双线性过滤,右图是用了立方过滤。立方插值过滤类似于双线性插值

双线性过滤Bilinear filtering)是进行缩放显示的时候进行纹理平滑的一种纹理过滤方法。 在大多数情况下,纹理在屏幕上显示的时候都不会同保存的纹理一模一样,没有任何损失。正因为这样,所以一些像素要使用纹素之间的点进行表示,在这里我们假设纹素都是位于各个单元中心或者左上或者其它位置的点。双线性过滤器利用像素所表示点周围四个最近的点(纹素点)之间进行双线性插值

公式

[编辑]

在下面这些方程中,uk 与 vk 是点 k 处的纹理坐标,yk 是点 k 处的颜色值。不带下标的值表示像素点,带有下标 0、1、2、3 的值表示从左上沿顺时针方向到左下包围像素的纹素点,带有下标 a、b 的值分别表示像素点在点0、1的连线与点3、2连线上的投影点。由于双线性插值方程是线性插值方程的一种特殊形式,所以我们从较简单的线性插值方程开始分析。

假设纹理是正方形的位图,并且满足

我们进一步定义,

这样就可以将插值方程化简为:

代入方程,得到:

或者,

这种表示相当简单。但是,如果图像只进行缩放处理,而没有旋转、扭曲、透视或者其它处理,那么使用独立的方程计算并且保存用于后面数据行计算的 yb 或者 ya 速度将更快。

示例代码

[编辑]

这部分代码假设纹理是常见的正方形,没有Mipmap,并且只有一个通道的数据。只有一个通道的情况非常少见,几乎所有的纹理都是彩色的,都有红色、绿色与蓝色通道,有些还有阿尔法透明通道,所以每个 y 都要进行三到四次的计算

double getBilinearFilteredPixelColor(Texture tex, double u, double v) {
  u *= tex.size;
  v *= tex.size;
  int x = floor(u);
  int y = floor(v);
  double u_ratio = u - x;
  double v_ratio = v - y;
  double u_opposite = 1 - u_ratio;
  double v_opposite = 1 - v_ratio;
  double result = (tex[x][y]   * u_opposite + tex[x+1][y]   * u_ratio) * v_opposite + 
                  (tex[x][y+1] * u_opposite + tex[x+1][y+1] * u_ratio) * v_ratio;
  return result;
}

局限

[编辑]

在纹理缩减到一半或者放大一倍的范围内,双线性过滤都能够有非常好的精度。这也就是说,如果纹理在每个方向都有 256 个像素,那么将它缩减到 128 以下或者放大到 512 以上的时候,由于会丢掉太多的像素或者进行了过多的平滑处理,纹理看起来就会很差。通常,可以在缩减的过程中使用 Mipmap 来实现较好的性能;但是,在透视图中的纹理上的经过双线性过滤处理的两个不同尺寸的 mipmap 之间的过渡将非常明显。三线性过滤尽管比较复杂,但是可以使得过渡非常平滑。

为了快速说明纹理过滤中如何丢失纹素,这里有一组用来自于 8 纹素宽纹理的数字表示的盒子的中心,它们与蓝色表示的来自于 3 纹素宽的纹理表示的盒子中心的一组数字混杂在一起。红色数字表示计算 3 纹素纹理中根本不需要的纹素。

0.0625, 0.1667, 0.1875, 0.3125, 0.4375, 0.5000, 0.5625, 0.6875, 0.8125, 0.8333, 0.9375

特殊情况

[编辑]

通常纹理是有限大小的,我们经常会得到坐标位于纹素坐标之外栅格之外的像素。可以用以下几种方法来处理这种情况:

  • 旋绕纹理,这样一行中的最后一个纹素将出现在该行第一个纹素的前面,一列中的最后一个纹素将出现在该列第一个纹素前面。在纹理平铺的时候这种方法可以得到最好的效果。
  • 纹理之外的区域使用一种颜色。这可能并不是一个非常了不起的想法,但是如果纹理要放到固体或者透明背景上,那么就可以使用这种方法。
  • 无限重复边界纹素。如果所设计的纹理不是要重复使用的话,这种方法可以很好地工作。

参见

[编辑]