[WPF] 使用 Effect 玩玩阴影、内阴影、 长阴影
最近在学习怎么用 Shazzam Shader Editor 编写自定义的 Effect,并试着去实现阴影、内阴影和长阴影的效果。结果我第一步就放弃了,因为阴影用到的高斯模糊算法对我来说太太太太太太太太难了,最后只好用些投机取巧的方法来模仿这几种效果。
1. 阴影
WPF 中的 DropShadowEffect 简单来说就是将输入源的图像进行高斯模糊,然后根据 Color、Opacity、Direction、ShadowDepth 这几个属性来修改颜色、透明度和位移,形成一张新的图像作为阴影,平铺在原图像的背后。要自己实现 DropShadowEffect 最大的难点就在高斯模糊这里,既然写不出高斯模糊算法,就只好依赖 WPF 现有的东西。我的做法是用一个 VisualBrush 获取需要做阴影的图像,然后再用 WPF 的 BlurEffect 让它变模糊:
<Grid ClipToBounds="True">
<Grid>
<Grid.Effect>
<BlurEffect Radius="38" />
</Grid.Effect>
<Grid.Background>
<VisualBrush Stretch="None" Visual="{Binding ElementName=ForegroundElement}" />
</Grid.Background>
</Grid>
</Grid>
<Grid x:Name="ForegroundElement">
<TextBlock VerticalAlignment="Center"
FontFamily="Lucida Handwriting"
FontSize="148"
FontWeight="ExtraBold"
Foreground="#f7e681"
TextAlignment="Center">
FAKE<LineBreak />
SHADOW</TextBlock>
</Grid>
现在的它看起来就是这个样子。

然后写一个 FakeDropShadowEffect。它获取输入源的 Alpha 通道,将 RGB 替换为指定的颜色(默认是黑色),组合成新的颜色。再通过 Angle 和 Depth 计算出偏移:
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 c = 0;
float rad = Angle * 0.0174533f;
float xOffset = cos(rad) * Depth;
float yOffset = sin(rad) * Depth;
uv.x += xOffset;
uv.y += yOffset;
c = tex2D(Texture1Sampler, uv);
c.rgb = Color.rgb * c.a * Opacity;
c.a = c.a * Opacity;
return c;
}
最后在应用了 BlurEffect 的元素外面再套一层 Grid,然后在这个 Grid 应用刚刚写的 FakeDrpShadowEffect:
<Grid ClipToBounds="True">
<Grid.Effect>
<effects:FakeDropShadowEffect Angle="225"
Depth="0.03"
Opacity="0.5" />
</Grid.Effect>

成果如上图所示,和 DropShadowEffect 几乎一样了。
2. 内阴影
关于内阴影的实现,我之前写过另一篇文章介绍过:实现 WPF 的 Inner Shadow。现在用 Effect,我首先想到的做法是叠加两个元素,上层的元素根据另一个元素的 VisualBrush 剪切出一个洞,然后在这个洞投下阴影:
<Grid x:Name="BackgroundElement">
<TextBlock x:Name="Text"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="150"
FontWeight="Bold"
Foreground="{StaticResource LightBackground}"
TextAlignment="Center">
INNER<LineBreak />
SHADOW</TextBlock>
</Grid>
<Grid ClipToBounds="True">
<Grid.Effect>
<DropShadowEffect BlurRadius="8"
Opacity="0.7"
ShadowDepth="5" />
</Grid.Effect>
<Grid Background="{StaticResource LightBackground}">
<Grid.Effect>
<effects:ClipEffect>
<effects:ClipEffect.Blend>
<VisualBrush Stretch="None" Visual="{Binding ElementName=BackgroundElement}" />
</effects:ClipEffect.Blend>
</effects:ClipEffect>
</Grid.Effect>
</Grid>
</Grid>
在上面的 XAML 中,ClipEffect 有另一个输入 Blend,这个输入就是要剪切的形状。ClipEffect 的代码很简单,就只是几行,关键的功能是用 input 的Alpha 通道减去 blend 的 Alpha 通道作为结果输出:
sampler2D blend : register(s1);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 inputColor = tex2D(input, uv);
float4 blendColor = tex2D(blend, uv);
float4 resultColor = 0;
float opacity = inputColor.a - blendColor.a;
resultColor.rgb = inputColor.rgb * opacity;
resultColor.a = opacity;
return resultColor;
}
下图是上面的 XAML 实现的效果:

3. 长阴影
我以前写过一篇在 UWP 实现长阴影的博客:使用GetAlphaMask和ContainerVisual制作长阴影(Long Shadow)。这次在 WPF 里重新用 Effect 实现一次。长阴影的原理是不断向左上角(因为偷懒就只是做向右下的阴影)检查,直到遇到 Alpha 通道为 1 的像素,然后计算这个像素与自身的距离得出阴影的 Alpha,所有代码如下:
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 srcColor = tex2D(input, uv);
if (srcColor.a == 1)
{
return srcColor;
}
float4 tempColor = 0;
float2 offset = 0;
int maxDepth = 400;
float a = 0;
for (float i = 1; i < maxDepth; i++)
{
if (i < ShadowLength)
{
if (a == 0)
{
offset = uv.xy - float2(i / Width, i / Height);
if (offset.x > 0 && offset.y > 0)
{
tempColor = tex2D(input, offset);
if (tempColor.a == 1)
{
a = (1 - i / max(1,ShadowLength));
}
}
}
}
}
if (a == 0)
{
return srcColor;
}
a = min(1,a);
tempColor.rgb = Color.rgb * a * Opacity;
tempColor.a = a * Opacity;
float4 outColor = (1 - srcColor.a) * tempColor + srcColor;
return outColor;
}
使用起来的 XAML 和效果如下,需要输入 ShadowLength 和 Color,因为 Effect 没法知道输入源的尺寸,所以还需要主动输入 Width 和 Height:
<Grid x:Name="Root" Background="Transparent">
<Grid.Effect>
<effects:LongShadowEffect Width="{Binding ElementName=Root, Path=ActualWidth}"
Height="{Binding ElementName=Root, Path=ActualHeight}
ShadowLength="100"
Color="Red" />
</Grid.Effect>
<TextBlock x:Name="TextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="150"
FontWeight="Bold"
Text="NEXT" />
</Grid>

4. 源码
https://github.com/DinoChan/wpf_design_and_animation_lab
[WPF] 使用 Effect 玩玩阴影、内阴影、 长阴影的更多相关文章
- 扁平化设计的最新趋势 – 长阴影(Long Shadow)
随着互联网的发展,网页设计变得越来越复杂,如今设计的外观和感觉实现网站功能说使用的开发技术一样重要.互联网的功能远远不只是基本的信息共享,现在人们对网站的期望是远远大于几年前的. 如今,HTML5 & ...
- 20款精致的长阴影 LOGO 设计【附免费生成工具】
长阴影(Long Shadow)概念来自于最新非常流行的扁平化设计(Flat Design).扁平化设计趋势影响最大的是用户界面元素和图标,但它也开始蔓延到其他网页设计的其他部分. 长阴影其实就是扩展 ...
- 小教程:自己创建一个jQuery长阴影插件
长阴影设计是平面设计的一个变体,添加了阴影,产生了深度的幻觉,并导致了三维的设计.在本教程中,我们将创建一个jQuery插件,通过添加完全可自定义的长阴影图标,我们可以轻松地转换平面图标. 戳我查看效 ...
- Long Shadows Generate是一款在线使用纯CSS3实现长阴影的效果,一款强大的扁平化长投影制造器。
Long Shadows Generate是一款在线使用纯CSS3实现长阴影的效果,一款强大的扁平化长投影制造器. Long Shadows Generate 彩蛋爆料直击现场 Long Shadow ...
- [UWP]使用GetAlphaMask和ContainerVisual制作长阴影(Long Shadow)
1. 什么是长阴影 前几年扁平化设计(Flat Design)十分流行,后来在扁平化的基础上又流行起了长阴影(Long Shadow).长阴影其实就是扩展了对象的投影,感觉是一种光线照射下的影子,通常 ...
- CSS3+HTML5实现块阴影与文字阴影
CSS 3 + HTML 5 是未来的 Web,它们都还没有正式到来,虽然不少浏览器已经开始对它们提供部分支持.本教程分5节介绍了 5 个 CSS3 技巧,可以帮你实现未来的 Web,不过,这些技术不 ...
- 【HLSL学习笔记】WPF Shader Effect Library算法解读之[BandedSwirl]
原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[BandedSwirl] 因工作原因,需要在Silverlight中使用Pixel Shader技术,这对于我来 ...
- WPF的Effect效果
一.阴影效果(DropShadowEffect) <TextBlock Text="> <TextBlock.Effect> <DropShadowEffect ...
- HTML连载70-相片墙、盒子阴影和文字阴影
一. 制作一个相片墙 二. <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...
随机推荐
- access偏移注入原理
前言:近段时间在学习access偏移注入时,在网上查询了大量的资料,感觉很多资料讲解的十分模糊并且我个人认为有很多不够严谨的地方,于是我便在线下经过大量测试,写出以下文章,如有错误,望指出. 如要转载 ...
- [STM32F10x] 标准库初始化问题
硬件:STM32F103C8T6 平台:ARM-MDK V5.11 STM32F系列提供的标准库都是通过结构体来初始化的.比如,以下是GPIO初始化的一个示例代码: GPIO_InitTypeDef ...
- jsp文本框输入限制问题
1.jsp文本窗口实现控制输入格式 <input onkeyup = "value=value.replace(/[\W]/g,'')" onbeforepaste=&quo ...
- i-Urban Renovation使用3D Tiles可视化鸟取县Munakata建筑状态
Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 日本的鸟取县,使用i-Urban Renovation appl ...
- Mysql 死锁分析
1. 结论 死锁检查机制 当事务A需要获取一个行锁时(例如更新一行数据),假如需要获取行1的锁 检查其他事务有没有已获取了行1的锁. 如果有,例如事务B已获取了行1的锁. 继续检查事务B在等待的锁,如 ...
- 【程序6】用*号输出字母C的图案
我自己写的 print(' * * *\n'); print(' * \n'); print(' * \n'); print(' * * * *\n'); 官方答案 print('*' * 10) f ...
- Vue.js项目的兼容性与部署配置
一.处理兼容性问题的相关插件: 1> 解决移动端某些版本的浏览器,点击事件有3s延时触发的问题 · 安装 fastclick 依赖包:npm install fastclick --save-d ...
- Caffeine缓存 最快缓存 内存缓存
一.序言 Caffeine是一个进程内部缓存框架. 对比Guava Cache Caffeine是在Guava Cache的基础上做一层封装,性能有明显提高,二者同属于内存级本地缓存.使用Caffei ...
- Nacos服务注册与发现的2种实现方法!
Spring Cloud Alibaba 技术体系中的 Nacos,提供了两个重要的功能:注册中心(服务注册与发现)功能和配置中心功能. 其中注册中心解决了微服务调用中,服务提供者和服务调用者的解耦, ...
- js instanceof 解析
js中的instanceof运算符 概述 instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上 语法 obj instanceofOb ...