转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2592

在渲染场景的时候,一般来说分辨率和输出大小,也就是窗口大小相同。但在移动平台上,基本上没机会让你随便切换分辨率,都是只告诉你大小。在这时候,你如果想要用特定的分辨率渲染,就不可避免需要一次放缩。另一个常见的情况是,如果一个平台性能达不到需要的帧速率,也需要渲染较小的图之后,拉伸到指定分辨率。在XBox 360等console平台,有个专门的硬件放缩机制,只要打开就能自动把输入图像拉伸到720p或1080i/p(DX11.2才开始引入这样的机制)。但在PC和移动平台上,这个事情得在引擎里自己完成。所以在post process后端,加一个resizer就成了必然之选。

框架

这个放缩的框架很简单,把窗口大小填充给screen resolution,而渲染的分辨率则来自配置文件。如果两者不同,就加一个resizer拉伸一下。当然,如果两者连长宽比都不一样,就需要调整图像位置,在上下或者左右留边。

高质量放大

缩小比较容易,因为信息过剩。所以下文只讨论放大的问题。这里其实现在剩下的唯一问题就是,因为信息丢失,无论如何不能在放大后恢复出全部信息的。所以这里只能比较放大后的平滑程度,由此希望artifact尽量少。

质量比较

硬件本身能做的filter只有point和bilinear。如果需要更高质量的,可以用三阶的filter,比如bicubic。这里比较一下这几种filter对同样的输入会产生什么样的结果。原图来自于Windows的Playful Puppies主题的一张桌面墙纸,从1920×1200缩小到不同的分辨率,再upsample到1280×720,比较拉伸后的结果。bicubic的实现用的是wikipedia上的卷积公式,取A=-0.5。

896×504

首先试一下70%的大小。第一张是point filtering,已经有可察觉的锯齿(需要点开大图查看):

point 70%

理论上只要>50%,bilinear已经能达到非常高的效果,更高阶的也不会比bilinear好太多。

bilinear 70%

确实,除了高频的地方略微清晰之外,其他地方都看不出bicubic和bilinear的区别。

bicubic 70%

640×360

50%的大小下,point已经有明显的锯齿:

point 50%

bilinear总的来说还行,但能看出比较模糊了。

bilinear 50%
bicubic仍然在高频的地方比较清晰。

bicubic 50%

384×216

30%的时候,point已经没法看了。

point 30%

bilinear也有比较多的间断点。

bilinear 30%

bicubic的优势体现出来了。

bicubic 30%

51×29

顺便测试一个更极端的,4%的大小。point下已经完全没意义。

point 4%

bilinear也充满了十字形的artifact。

bilinear 4%

bicubic由于保证了一阶连续,虽然也看不出形状,但仍保持平滑。

bicubic 4%

速度比较

这里point和bilinear都是用直接一次采样的实现。bicubic用的是separable的优化,把原本需要的16次采样减少到8次。这些filter都是在pixel shader里完成的,还没有用到compute shader。测试的GPU是NV 4200M。

大小 类型 时间(ms)
896×504 point 0.02
bilinear 0.02
bicubic 0.79
640×360 point 0.02
bilinear 0.02
bicubic 0.56
384×216 point 0.02
bilinear 0.02
bicubic 0.30

从结果可以看出,point和bilinear在现代GPU上已经完全没区别。bicubic因为采样多了8倍,并有一定的计算,性能低了很多,不过也不算慢。

总结

总的来说,如果放大比率小于两倍,那么bilinear就已经足够。大于两倍的时候,bicubic是个不错的选择。以后有时间我会再测试其他的高质量filter,比如lanczos, sinc等。这个放缩机制以后有可能进一步扩展到dynamic resolution