真正的水彩效果在shader中是比较难实现的,它需要进行中值滤波后累加等一些操作,还需要处理NPR中的笔触一类的概念。本文绕开这些概念,只从视觉效果上能尽量模拟出水彩的画的那种感觉来。
水彩画一个最大的特点是水彩在纸上流动扩散后会和周围的颜色搅拌在一起,另外一个特点就是水彩通常会形成一个个的色块,过渡不像照片那样的平滑。针对这两个特点。可以设计这样的一个算法来模拟水彩画的效果。
首先我们模拟扩散。简单的说,可以通过随机对附近的象素点进行采样来模拟颜色的扩散,而这个随机区域的大小我们可以称为扩散的力度。这在C++代码里应该是非常容易实现的,读者只需要使用Random函数就可以了。但是HLSL并没有提供这样的函数(似乎有个noise函数,不过不能用L)。怎么办呢?我们可以采用噪声纹理的方式,既事先计算好一个n*n的随机数数组,作为纹理传递给Pixel shader,这样在Pixel Shader里我们就能获得随机数了。得到随机数后,我们将随机数映射成纹理坐标的偏移值,就能模拟出色彩的扩散了。典型的噪声纹理是这个样子的:
图:噪声纹理
接下来处理色块,对颜色的RGB值分别进行量化。把RGB分量由原来的8bit量化成比特数更低的值。这样颜色的过渡就会显得不那么的平滑,而是会呈现出一定的色块效果。
通过以上两步处理后,图像依然有非常多的细节,尤其是第一步处理中产生的很多细节噪点。通过平滑模糊的方式来过滤掉这些高频噪声成分。
算法设计好了,接下来看看我们如何在RenderMonkey里实现这个算法。
类似上一个效果,我们需要两个pass来完成这个算法,第一个pass叫flow pass,模拟颜色的流动和处理颜色的量化。第二个pass叫Gauss pass,也就是前面提到的高斯模糊算法。实现的重点在第一个pass。
在模拟扩散的pass中,同样需要一个RenderTarget,以把结果保存在其中以便后续处理,然后还需要一个噪声纹理来产生随机数。具体代码如下:
代码中的_quatLevel用来表示对图像的量化比特数,值越小,色块越明显,比较合理的取值范围是2-6。_waterPower则表示图像颜色扩散范围,取值范围在8-64之间的效果比较好。
下面是经过水彩画处理后的图像:
图:水彩画效果。左图量化比特数为6比特,扩散范围为20象素。
l 总结
GPU进行数字图像处理,甚至是使用GPU进行数字视频编辑是目前非常流行的话题,市场是已经出现很多商业的产品,比如Mac公司的iMotion,就是完全采用GPU加速的视频非编软件,iMotion作者的对它的评价是:Play with the images in real-time。可见它的效率之高,本文只是简单的介绍了HLSL在图像处理领域的应用,希望能给读者拨开一些云雾。通过以上介绍的几种滤镜效果,读者应该大致掌握了使用HLSL进行数字图像处理的一些基本步骤和方法了,为了方便起见,我们并没有把处理完的图像保存下来而是仅仅把处理完的图像显示在屏幕上,其实在RendererMonkey中也是可以把处理完的结果保存起来的,我们可以创建一个和图像等大的RenderTarget。并把我们处理的结果绘制到这个RenderTarget中(关于如何设置当前的RenderTarget,以及如何设置多个RenderTarget,留给读者自己摸索),然后在RenderMonkey的工作区中选择那个RenderTarget,在右键菜单中选择保存到图像就可以了。
C++也好,Basic也好,乃至现在的HLSL/GLSL也好。它们都是语言而已,充分的了解这些语言,熟悉他们的特性都是非常简单的。但是如何充分发挥他们的作用,用它们做一些有意义的事情,就完全在于我们自己的实践和在实践中的创造性。如果读者在实践中还能创造出更多,更实用的效果。甚至是应用在商业产品中。
最后还得提一下的是,文中出现不少信号处理和数学的知识,可见多花点时间在数学上是非常值得的J
注:处于阅读方便,本文代码都未经过优化。