插值滤波:纹理映射的双线性插值滤波



  当你做纹理映射时候是否经常会注意到屏幕上显示出那些明显锯齿而且你用纹理像素化得太明显了?现在我们将谈论如何来解决这个问题而我们使用思路方法就是对你纹理进行滤波下面我们将介绍几种常用滤波思路方法最后再详细介绍双线性插值滤波具体实现

Bi-linear Interpolation

  双线性插值是通过对纹理中相邻像素进行处理来平滑掉屏幕输出像素间锯齿使用双线性插值会使屏幕输出图像显得更平滑下面先来看看它基本计算公式

double texture[N][M];  // x = [0, N), y = [0, M)
double xReal;          // xReal = [0, N - 1]
double yReal;          // yReal = [0, M - 1]

 x0 = (xReal), y0 = (yReal);
double dx = xReal - x0, dy = yReal - y0,
omdx = 1 - dx, omdy = 1 - dy;

double bilinear = omdx * omdy * texture[x0][y0] +
omdx * dy * texture[x0][y0+1] +
dx * omdy * texture[x0+1][y0] +
dx * dy * texture[x0+1][y0+1];

  观察这段公式你会看出我们很有效地使用了纹理座标小数部分来对 4个纹理中相邻像素进行插值我们按对应像素距离来决定各个像素所占权重也就是说当纹理U座标小数部分增加时左边相邻像素权重就会减少减少出来权重会增加右边相邻像素上去对垂直方向V座标情况也同此类似

  在实际应用中直接按这段公式来计算显然会很慢你可以用定点整数和查表法来取消浮点和整型混合运算以及去掉乘法(提示:针对A、B两种颜色混合建立 x*A+(1-x)*B 结果表)

Mip-Mapping

  我第次看到Mip-mapping技术是在游戏QUAKE里而现在这种技术早已是随处可见了这种技术是由Williams在1983年发明“Mip”这个名称起源于“multum in parvo”大概就是在小块地方有很多东西意思

  具体说来Mip-Mapping思想就是构建套纹理总共需要大约1.3倍内存其中每块子纹理是通过对父纹理过滤而得到长和宽都是其父纹理1/2其面积为父纹理1/4接下来在应用时候你根据距离选取最合适块来进行映射实战证明这种技术虽然简单但对提高纹理映射质量确实非常有效

  通过Mip-Mapping可以为较小多边形映射上面积较小纹理这对减少纹理扰动大有好处举个例子你有块256x256大小纹理当它开始向远离观察者方向开始移动时你会看到它开始闪烁和颤动这种现象出现是我们把大块纹理映射到个很小区域而引起你可能在上帧时是纹理中(50,20)处像素到了下却画是纹理中(60,30)处像素如果这两个像素相差很大你就会观察到前面所说现象了来说这种剧烈纹理座标变化会损害图像品质并且影响CACHE效率而Mip-Mapping无疑是解决这个问题好办法

Tri-linear Interpolation

  在介绍了双线性插值和Mip-Mapping以后该来讲讲 3线性插值了其实 3线性插值也很简单它就是前两种技术结合它在对Mip-Mapping每块纹理做双线性插值同时还要对Mip-Mapping中相邻两块纹理按距离再做次插值既算出较大块纹理上某点双线性插值像素值和较小块纹理上某点双线性插值像素值再按目标同两块纹理距离做次类似插值

  使用 3线性插值可以消除Mip-Mapping里纹理切换(既上帧时用是某个大小块纹理而下帧时又换了情况)时突然变化从而可以提供很平畅高质图像输出

  同前两种技术相比 3线性插值运算量非常大目前只能依靠硬件来实现

双线性插值纹理映射实现

  下面我们通过段描述性代码来简单看看双线性插值纹理映射是如何实现

        此处略去各种化代码直接观察我们最关心部分
其中:U和V是16.16格式定点整数
du和dv是浮点数

du = (U & 0xFFFF) / 65536.0
dv = (V & 0xFFFF) / 65536.0

invdu = 1.0 - du
invdv = 1.0 - dv

        // 根据到相邻 4个像素距离计算各自权重

Weight1 = invdu*invdv
Weight2 = invdu*dv
Weight3 = du*invdv
Weight4 = du*dv

        // 求得各个像素RGB颜色分量

r00 = Texture[V >> 16][U >> 16].Red
g00 = Texture[V >> 16][U >> 16].Green
b00 = Texture[V >> 16][U >> 16].Blue

r01 = Texture[(V >> 16) + 1][U >> 16].Red
g01 = Texture[(V >> 16) + 1][U >> 16].Green
b01 = Texture[(V >> 16) + 1][U >> 16].Blue

r10 = Texture[V >> 16][(U >> 16) + 1].Red
g10 = Texture[V >> 16][(U >> 16) + 1].Green
b10 = Texture[V >> 16][(U >> 16) + 1].Blue

r11 = Texture[(V >> 16) + 1][(U >> 16) + 1].Red
g11 = Texture[(V >> 16) + 1][(U >> 16) + 1].Green


b11 = Texture[(V >> 16) + 1][(U >> 16) + 1].Blue

        // 按权重混合RGB颜色分量

Red = Weight1*r00 + Weight2*r01 + Weight3*r10 + Weight4*r11
Green = Weight1*g00 + Weight2*g01 + Weight3*g10 + Weight4*g11
Blue = Weight1*b00 + Weight2*b01 + Weight3*b10 + Weight4*b11

        // 按最后求得RGB颜色分量画点

PutPixel(X, Y, Pack(Red, Green, Blue))

  这段代码显然未经优化(起码不要去用那个PutPixel)如果你功力不够可能会无法达到理想优化目标这时你可以直接使用硬件去实现(新3D硬件都能支援这些功能)但我相信你在理解了双线性插值滤波思想以后定能举反 3利用它为你游戏图像更添魅力 
Tags:  双线性插值 线性插值 插值滤波位同步 插值滤波

延伸阅读

最新评论

发表评论