作者:樊一鹏樊樊一鹏一鹏
当你做纹理映射的时候,是否经常会注意到屏幕上显示出的那些明显锯齿,而且你用的纹理像素化得太明显了?现在,我们将谈论如何来解决这个问题,而我们使用的方法就是对你的纹理进行滤波。下面我们将介绍几种常用的滤波方法,最后再详细介绍双线性插值滤波的具体实现。
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]
int x0 = int(xReal), y0 = int(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];
观察这段公式,你会看出,我们很有效地使用了纹理座标的小数部分来对四个纹理中的相邻像素进行插值。我们按对应像素的距离来决定各个像素所占的权重。也就是说,当纹理的U座标的小数部分增加时,左边相邻像素的权重就会减少,减少出来的权重会增加右边的相邻像素上去。对垂直方向的V座标的情况也同此类似。
此处略去各种初始化代码,直接观察我们最关心的部分 其中:U和V是16.16格式的定点整数 d
u和dv是浮点数 du = (U & 0xFFFF) / 65536.0
dv = (V & 0xFFFF) / 65536.0
invdu = 1.0 - du
invdv = 1.0 - dv
// 根据到相邻四个像素的距离计算各自的权重 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硬件都能支援这些功能)。但我相信你在理解了双线性插值滤波的思想以后,一定能举一反三,利用它为你的游戏图像更添魅力。