Home > 程序/算法 > D3D9的Pixel和Texel(续)

D3D9的Pixel和Texel(续)

December 13th, 2010 Leave a comment Go to comments

前一篇只说了屏幕坐标和纹理坐标一一对应,也就是说没有缩放的情况。实际上有时候会需要把图素进行缩放后渲染,举个例子,把3×3像素的纹理(注意,不是整张纹理3×3,而是取一张纹理上3×3大小的一部分)渲染成屏幕上的4×4像素大小,如下图。和以前一样,红色的线框代表纹理像素的边界,灰色线框代表屏幕像素的边界,由于纹理被放大了,所以只有在图素边界处二者才是重合的。

这里有个不容易觉察到的问题,估计已经让很多程序员抓狂过,也就是放大后的图素的边界像素上会多出一些来历不明的颜色,放大倍数越大就越明显。实际上,这是采样到边界以外的纹理像素了。

有人会问了,上图中3×3纹理的边界和屏幕4×4像素的边界不是严格重合的吗,怎么会采样到外面?如果了解线性过滤的原理就会知道,采样纹理得到的最终颜色,是由采样点周围4个纹理像素的颜色根据距离混合出来的。上图中图素的四个角落已经标明了屏幕像素中心(灰点)和纹理像素中心(红点)的位置,可以看到,像素中心明显比纹理中心要靠“外”,线性过滤计算时会采样到外面的另外3个纹理像素,也就是说这几个像素被“污染”了。例如图素的边界应该是白色,纹理上相邻的像素是黑色,那么就会看到一条明显的灰色的边。

这种现象只有在打开了线性过滤的情况下才会发生,如果用点采样的话就没事。当然,为了得到比较好看的缩放效果,线性过滤是必须的。另外很容易推测,只有在放大的情况下才会发生污染,如果是缩小的话,屏幕像素中心比纹理像素中心要偏“里”,就不会混合到外面不相干的纹理像素。

那这个该怎么办呢?有两种解决方案:

方案1:在纹理上给独立的图素周围加一圈透明的像素

这样边界即使采样到外面也只是混合了透明的颜色,从而变得半透明,看起来比混合了其它非透明颜色要好。要注意,如果是原来设计中就是需要拼接在一起的图素,比如用来拼接窗口的“九宫格”元素,在纹理上必须是连续的,不能用透明像素隔开,否则放大时反而会出现裂缝。

方案2:对顶点的纹理坐标做细微修正,使角落上的纹理像素中心仍然和屏幕像素中心重合,如下图:

这样就能保证不受外界污染了,那需要修正多少呢?几何学的还行的同学可以看出,这里的放大倍数不再是4/3了,而是3/2;需要修正的纹理坐标相当于“纹理像素比屏幕像素大出来的尺寸的一半”,所以这里就是1/3/2=1/6,单位是“个纹理像素”,然后再根据整张纹理的像素尺寸,就能算出要修正多少uv值,注意左边和上边是加,右边和下边是减。

扩展到一般情况,假设u或者v方向上从x个像素放大到y个像素,那纹理坐标修正值就是( 1 – ( x – 1 ) / ( y – 1 ) ) / 2个纹理像素。当然如果要图省事的话可以一律修正0.5个纹理像素,理论上不管放大多少被都不会超过这个值。

方案2看起来比较完美,因为不要求纹理上的图素之间留有空隙;缺点是需要一个看起来稍有点复杂的计算,而且理论上这个处理是不正确的,如果需要将几个小图素拼成一个大图,问题就显露出来了:

上图示意了在同样放大比例下,用4个连续的3×3图素拼出一张8×8大图的情况,容易看出,纹理像素在8×8的像素范围内不是平均分布的,边界有一部分重合了。而采用方案1的话就不会有这个问题

总而言之,两种解决方案各有优缺点,方案一对图素在纹理上的分布有特殊要求;而方案二涉嫌得到了不正确的结果,虽然误差很小但终归让人心里不爽。

所以结论是:……呃……最好不要缩放。

Categories: 程序/算法 Tags: ,
  1. No comments yet.

 

Spam Protection by WP-SpamFree