[WPF] 使用 Shazzam Shader Editor 编写一个 Lighten Effect
之前在一篇文章(实现两个任天堂 Switch 的加载动画)里为了实现不同亮度的 Grid,使用了一个 LightenConverter
类,但是它只能处理 SolidColorBrush。为了可以应用在更多场合,这篇文章自己写一个 Effect 来实现相同 Lighten 的效果。
1. WPF 中的 Effect#
Wpf 自带两种 Effect:BlurEffect 和 DropShadowEffect,用法如下:
<Grid.Effect>
<BlurEffect/>
</Grid.Effect>
除了 WPF 自带的这两个,还可以在 Microsoft Blend for Visual Studio 2015 里找到由 Microsoft.Expression.Effects
这个 dll 提供的一些 Effect。
现在这个 dll 也可以在 Nuget 上找到。
2. 编写 Shader#
WPF 中的 Effect 使用 HLSL(高级着色器语言)编写,如果需要自定义 Effect 可以使用 Shazzam Shader Editor, 关于这款编辑器 walterlv 有一篇如何使用的教程:
WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码 - walterlv
其实我之前也没写过,语法什么的完全不懂,可是从网上抄一抄,很快就搞明白了一些基础,最后从 Lightness.fx 这个改一改完成了我需要的 LightenEffect:
// Copyright (c) 2014 Marcus Schweda
// This file is licensed under the MIT license (see LICENSE)
sampler2D input : register(s0);
float delta : register(c0);
// RGB -> HSL
float4 hsl(float4 c) {
float4 c2 = c.a;
float M = max(c.r, max(c.g, c.b)),
m = min(c.r, min(c.g, c.b));
float chroma = M - m;
// Lightness
c2[2] = (M + m) / 2;
// Hue
if (chroma != 0) {
if (M == c.r)
c2[0] = ((c.g - c.b) / chroma) % 6;
else if (M == c.g)
c2[0] = (c.b - c.r) / chroma + 2;
else
c2[0] = (c.r - c.g) / chroma + 4;
if (c2[0] < 0)
c2[0] += 6;
// Saturation
c2[1] = chroma / (1 - abs(2 * c2[2] - 1));
} else {
c2[0] = c2[1] = 0;
}
return c2;
}
float4 rgb(float4 c) {
float4 c2 = c[3];
float chroma = c[1] * (1 - abs(2 * c[2] - 1));
float X = chroma * (1 - abs(c[0] % 2 - 1));
if (0 <= c[0] && c[0] < 1)
c2.rgb = float3(chroma, X, 0);
else if (1 <= c[0] && c[0] < 2)
c2.rgb = float3(X, chroma, 0);
else if (2 <= c[0] && c[0] < 3)
c2.rgb = float3(0, chroma, X);
else if (3 <= c[0] && c[0] < 4)
c2.rgb = float3(0, X, chroma);
else if (4 <= c[0] && c[0] < 5)
c2.rgb = float3(X, 0, chroma);
else if (5 <= c[0] && c[0] < 6)
c2.rgb = float3(chroma, 0, X);
c2.rgb += c[2] - chroma / 2;
return c2;
}
float4 main(float2 uv : TEXCOORD) : COLOR {
float4 hcyin = hsl(tex2D(input, uv));
if( delta>0)
{
hcyin[2] = saturate(hcyin[2] + (1-hcyin[2])* delta);
}else
{
hcyin[2] = saturate(hcyin[2] + hcyin[2] * delta);
}
return rgb(hcyin);
}
这份代码分三部分,首先是定义的两个变量 input 和 delta,input 即输入的图像,是每个 Shader 的固定部分,不要修改它;delta 是定义来控制 LightenEffect 亮度变化率的变量。然后是两个自定义的函数,用于 rgb 和 hsl 互相转换。最后是 main 函数,这也是每个 Effect 必须包含的部分,这个函数的输入 uv 看起来是坐标,用 tex2D(input, uv)
获取 input 在 uv 的颜色,函数的返回值是处理后的 uv 所在的颜色。
在这段代码里的 main 函数还算简单,就是把当前位置的颜色转换为 hsl,然后根据 delta 调整亮度,再转换为 rgb 返回。
函数完成并运行 Apply Shader 后可以使用 Shazzam Shader Editor 的 Tryout 功能验证效果。可以看到 Delta 为 -1 即全黑,为 1 就全白。
2. 应用 Effect#
验证完这个 Shader,把生成的 C# 代码和 .ps
文件放进项目,改好命名空间,编译后就能使用(关于这部分的详细操作,请参考 walterlv 的 这篇文章)。现在来看看生成的 C# 代码:
public class LightenEffect : ShaderEffect
{
public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(LightenEffect), 0);
public static readonly DependencyProperty DeltaProperty = DependencyProperty.Register("Delta", typeof(double), typeof(LightenEffect), new UIPropertyMetadata(((double)(0D)), PixelShaderConstantCallback(0)));
public LightenEffect()
{
PixelShader pixelShader = new PixelShader();
pixelShader.UriSource = new Uri("/WpfDesignAndAnimationLab.Effects;component/Shaders/LightenEffect.ps", UriKind.Relative);
this.PixelShader = pixelShader;
this.UpdateShaderValue(InputProperty);
this.UpdateShaderValue(DeltaProperty);
}
private Brush Input
{
get
{
return ((Brush)(this.GetValue(InputProperty)));
}
set
{
this.SetValue(InputProperty, value);
}
}
public double Delta
{
get
{
return ((double)(this.GetValue(DeltaProperty)));
}
set
{
this.SetValue(DeltaProperty, value);
}
}
}
首先是自定义的两个变量 Input 和 Delta,它们被封装成依赖属性。然后看看这句话,这句话定位产生的 .ps
文件,一定要保证位置正确:
pixelShader.UriSource = new Uri("/WpfDesignAndAnimationLab.Effects;component/Shaders/LightenEffect.ps", UriKind.Relative);
最后使用时只需要在前面加上 Effect 的命名空间。
<Rectangle.Effect>
<effects:LightenEffect Delta=".5"/>
</Rectangle.Effect>
3. 最后#
感谢 walterlv 写的文章,让我终于学会了 Shazzam Shader Editor 的用法。
4. 源码#
作者:Dino.C
出处:https://www.cnblogs.com/dino623/p/write_a_lighten_effect_using_th_shazzam_shader_editor.html
版权:本文采用「CC BY 4.0」知识共享许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?