新的项目中大量使用了将sprite镜像拼接的方式来做底板,结果同事发现有的时候拼的时候中间会出现一个莫名的黑缝,如图:
看了下贴图没啥问题,也不是因为贴图压缩的问题,但是这条黑缝总是存在,然后想到这条黑缝很可能是采用uv的时候将uv采到了sprite和其他图的接缝处,然后在跟着看了下NGUI计算uisprite的uv的代码,发现ngui的计算确实存在着严重的问题,NGUI是这样计算一个sprite四个顶点的uv的
static public Rect ConvertToTexCoords (Rect rect, int width, int height)
{
Rect final = rect;
if (width != 0f && height != 0f)
{
final.xMin = rect.xMin / width;
final.xMax = rect.xMax / width;
final.yMin = 1f - rect.yMax / height;
final.yMax = 1f - rect.yMin / height;
}
return final;
}
这段代码的计算方式完全是错误的,其中width和height是atals大图的尺寸,rect是sprite在atals大图上的区域,可能作者对uv采样的计算尤其是uv坐标的区间并不是十分详细的了解,当然这个很多人可能都不全然了熟于心。
这里是因为有几个概念没有明确:uv的坐标范围,texture的坐标范围,采样的概念。
首先uv的坐标范围是这样的
texel的uv以左上角为0 ,右下角为1,也就是说第一个像素中央位置的uv是0.17 0.17
再看一张texute图片的坐标范围
可以看出他们的坐标范围是不一样的,这样在采样贴图颜色的时候,会出现下面的情况
某些像素可能恰好采到边缘处,这样就会根据采样策略进行插值,点采样就直接去最近的像素,线性采样就取上下左右四个整数值加权平均。
回头看下ngui的算法,假设一个atlas的大图如下,左上角两个像素是一个sprite
当我们使用左上角的这个sprite的时候,按照ngui的算法,这个矩形的uv应该是
这样的话,对于我们的左上角和右下角的那两个顶点,当他们采样的时候,变落入了边界上,由于线性采样,就会在右边看到一个橘色和白色的混合平均色,即一条杂边。
如何解决?我们需要线性采样来保证中间像素的质量,但是对于边缘我们需要保证他们的uv严格落入sprite的区域而不能落入边界。
所以我修改了ngui这段计算uv的错误代码,修改后,这个sprite四角的uv成为下图,原理是把原先uv落入的区间(蓝色)缩短为新的区间(红色)。具体修改的代码因为项目代码的保密我就不贴了,但是具体的原理都在这里了。
看一下修改后的拼接效果,天衣无缝~~
其实这个问题早在上一个项目《暗黑血统》里面就遇到过多次,只是那时比较忙没细追究没去眼睛ngui代码,这次决定看了一下,其实就是个贴图采样的小问题,看来遇到问题还是要刨根问底才行