在 WPF 中实现融合效果

打印 上一主题 下一主题

主题 949|帖子 949|积分 2851

1. 融合效果

融合效果是指对两个接近的元素进行高斯模糊后再提高对比度,使它们看上去“粘”在一起。在之前的一篇文章中,我使用 Win2D 实现了融合效果,效果如下:

不过 Win2D 不适用于 WPF,在 WPF 中可以使用 BlurEffect 配合自定义 Effect 实现类似的效果。HandyControl 中有一个使用自定义的 ContrastEffect 实现融合效果的 Demo,如下图:

但是 ContrastEffect 是通过 Contrast 属性同时控制 RGBA 四个通道的对比值,所以没办法控制准确地颜色。另外 HandyControl 也提供了 ColorMatrixEffect,不过 ColorMatrixEffect 很难控制对比度。
既然都用到自定义 Effect 了,这次索性自己写一个。
2. 自定义 Effect

在 Win2D 中,实现融合效果的步骤是先使用 GaussianBlurEffect 在两个元素间产生粘连在一起的半透明像素,再用 ColorMatrixEffect 加强对比对,使半透明的像素变得完全不透明。
在 WPF 中我们可以直接使用自带的 BlurEffect 实现高斯模糊,效果如下:

接下来需要加强对比度。WPF 中没有 ColorMatrixEffect 的替代品,不过我们可以使用 HLSL(高级着色器语言)编写 PixelShader 并生成自定义的 WPF Effect。编写 PixelShader 可以使用 Shazzam Shader Editor,  walterlv 有一篇关于如何使用这款编辑器的教程:
WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码
在这里我编写了一个对 Alpha 进行二值化处理的 PixelShader 实现加强对比度功能,它的作用很简单:当像素的 Alpha 大于阈值就将 Alpha 置为 1,否则为 0,代码如下:
  1. float Thresh : register(C0);
  2. float4 main(float2 uv : TEXCOORD) : COLOR
  3. {
  4.     float4 color;
  5.     color = tex2D(input, uv.xy);
  6.     if (color.a == 0 || color.a == 1 || Thresh == 0)
  7.     {
  8.         return color;
  9.     }
  10.     float4 resultColor = 0;
  11.     float opacity = color.a > Thresh ? 1 : 0;
  12.     if (opacity > 0)
  13.     {
  14.         resultColor.rgb = color.rgb / color.a * opacity;
  15.     }
  16.     resultColor.a = opacity;
  17.     return resultColor;
  18. }
复制代码

虽然确实实现了融合效果,但是圆形的边缘有严重的锯齿。很明显,问题出在上面的代码中 Alpha 通道最终不是 0 就是 1,为了使边缘平滑,应该留下一些“中间派”。修改后的代码引用了 LowerThresh 和 UpperThresh,处于这两个阈值之间的像素用作保持边缘平滑的“中间派”,具体代码如下:
  1. float UpperThresh : register(C0);
  2. float LowerThresh : register(C1);
  3. float4 main(float2 uv : TEXCOORD) : COLOR
  4. {
  5.     float4 color;
  6.     color = tex2D(input, uv.xy);
  7.     if (color.a == 0 || color.a == 1 || LowerThresh == 0)
  8.     {
  9.         return color;
  10.     }
  11.     if (UpperThresh < LowerThresh)
  12.     {
  13.         return color;
  14.     }
  15.     float4 resultColor = 0;
  16.     float opacity = 1;
  17.     if (color.a < LowerThresh)
  18.     {
  19.         opacity = 0;
  20.     }
  21.     if (color.a > LowerThresh && color.a < UpperThresh)
  22.     {
  23.         opacity = (color.a - LowerThresh) / (UpperThresh - LowerThresh);
  24.     }
  25.     if (opacity > 0)
  26.     {
  27.         resultColor.rgb = color.rgb / color.a * opacity;
  28.     }
  29.     resultColor.a = opacity;
  30.     return resultColor;
  31. }
复制代码

3. 最后

这篇文章介绍了如何使用自定义 Effect 实现融合效果,只要理解了融合效果的原理并动手实现了一次,之后就可以参考博客园的 ChokCoco 大佬玩出更多花样,例如这种效果::

更多好玩的效果可以参考 ChokCoco 大佬的博客:你所不知道的 CSS 滤镜技巧与细节
源码:https://github.com/DinoChan/wpf_design_and_animation_lab

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

东湖之滨

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表