WPF 使用 HLSL + Clip 实现高亮歌词光照效果
在 WPF 中实现高亮歌词的光照效果(如舞台追光、聚光灯扫过文字),可以通过 HLSL 像素着色器(Pixel Shader) + Clip 几何裁剪 相结合的方式,实现高性能、流畅且视觉惊艳的动画效果。下面是一个完整的技术方案与实现示例。
✅ 效果目标
- 歌词文本静态显示;
- 一个“光斑”从左到右扫过当前行歌词;
- 光斑区域高亮(白色/暖色),其余区域保持原色或变暗;
- 支持平滑动画,60fps 流畅运行;
- 利用 GPU 加速,避免频繁重绘文本。
🔧 技术组合
| 技术 | 作用 |
|---|---|
WPF TextBlock |
显示歌词文本 |
ShaderEffect (HLSL) |
实现动态光照遮罩 |
Clip 属性 |
限制光照仅作用于歌词区域(防溢出) |
DoubleAnimation |
驱动光斑位置变化 |
第一步:编写 HLSL 像素着色器(HighlightLight.ps)
创建 HighlightLight.ps 文件(编译为 .ps 后缀):
// HighlightLight.ps
sampler2D InputSampler : register(s0);
float2 LightCenter : register(c0); // 光斑中心(归一化坐标 0~1)
float LightRadius : register(c1); // 光斑半径(归一化)
float4 AmbientColor : register(c2); // 背景/暗部颜色
float4 HighlightColor : register(c3); // 高亮颜色
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 original = tex2D(InputSampler, uv);
// 计算当前像素到光斑中心的距离(归一化)
float dist = distance(uv, LightCenter);
// 光照强度:使用 smoothstep 实现柔和边缘
float intensity = smoothstep(LightRadius, LightRadius * 0.7, dist);
// 注意:smoothstep(edge0, edge1, x) 在 x<edge0 时为1,x>edge1 时为0
// 混合:高亮区用 HighlightColor,其他用 AmbientColor
float4 finalColor = lerp(HighlightColor, AmbientColor, intensity);
// 保留原始 alpha(确保透明背景)
return float4(finalColor.rgb, original.a);
}
💡 编译命令(使用 fxc):
fxc /T ps_3_0 /E main /Fo HighlightLight.ps HighlightLight.hlsl
第二步:在 C# 中封装 ShaderEffect
public class HighlightLightEffect : ShaderEffect
{
public static readonly DependencyProperty InputProperty =
RegisterPixelShaderSamplerProperty("Input", typeof(HighlightLightEffect), 0);
public static readonly DependencyProperty LightCenterProperty =
DependencyProperty.Register("LightCenter", typeof(Point), typeof(HighlightLightEffect),
new UIPropertyMetadata(new Point(0.5, 0.5), PixelShaderConstantCallback(0)));
public static readonly DependencyProperty LightRadiusProperty =
DependencyProperty.Register("LightRadius", typeof(double), typeof(HighlightLightEffect),
new UIPropertyMetadata(0.3, PixelShaderConstantCallback(1)));
public static readonly DependencyProperty AmbientColorProperty =
DependencyProperty.Register("AmbientColor", typeof(Color), typeof(HighlightLightEffect),
new UIPropertyMetadata(Colors.Gray, PixelShaderConstantCallback(2)));
public static readonly DependencyProperty HighlightColorProperty =
DependencyProperty.Register("HighlightColor", typeof(Color), typeof(HighlightLightEffect),
new UIPropertyMetadata(Colors.White, PixelShaderConstantCallback(3)));
public Brush Input
{
get => (Brush)GetValue(InputProperty);
set => SetValue(InputProperty, value);
}
public Point LightCenter
{
get => (Point)GetValue(LightCenterProperty);
set => SetValue(LightCenterProperty, value);
}
public double LightRadius
{
get => (double)GetValue(LightRadiusProperty);
set => SetValue(LightRadiusProperty, value);
}
public Color AmbientColor
{
get => (Color)GetValue(AmbientColorProperty);
set => SetValue(AmbientColorProperty, value);
}
public Color HighlightColor
{
get => (Color)GetValue(HighlightColorProperty);
set => SetValue(HighlightColorProperty, value);
}
public HighlightLightEffect()
{
PixelShader = new PixelShader
{
UriSource = new Uri("pack://application:,,,/Shaders/HighlightLight.ps")
};
UpdateShaderValue(InputProperty);
UpdateShaderValue(LightCenterProperty);
UpdateShaderValue(LightRadiusProperty);
UpdateShaderValue(AmbientColorProperty);
UpdateShaderValue(HighlightColorProperty);
}
}
第三步:XAML 布局 + Clip 裁剪
<Grid>
<!-- 背景 -->
<Rectangle Fill="Black" />
<!-- 歌词容器(关键:设置 Clip 限制光照范围) -->
<Border
x:Name="LyricContainer"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent">
<!-- 应用着色器的 TextBlock -->
<TextBlock
x:Name="LyricText"
Text="这是一句高亮歌词示例"
FontSize="48"
Foreground="White"
Effect="{StaticResource HighlightLightEffect}" />
</Border>
</Grid>
⚠️ 为什么需要
Clip?
若不裁剪,光照会作用于整个渲染区域(包括透明背景),造成性能浪费和视觉溢出。可通过代码动态设置Clip为歌词边界:
// 在窗口 Loaded 事件中
var bounds = LyricText.RenderSize;
LyricContainer.Clip = new RectangleGeometry(new Rect(bounds));
第四步:启动动画(C# 后台)
private void StartHighlightAnimation()
{
var effect = (HighlightLightEffect)LyricText.Effect;
var animation = new DoubleAnimation
{
From = -0.2, // 从左侧外开始
To = 1.2, // 到右侧外结束
Duration = TimeSpan.FromSeconds(3),
RepeatBehavior = RepeatBehavior.Forever,
AutoReverse = true
};
var centerPoint = new Point();
var centerBinding = new PropertyGroupDescription();
// 绑定 X 坐标动画
Storyboard.SetTarget(animation, effect);
Storyboard.SetTargetProperty(animation, new PropertyPath("LightCenter.X"));
var sb = new Storyboard();
sb.Children.Add(animation);
sb.Begin();
}
🌟 优势总结
- GPU 加速:HLSL 在显卡上运行,CPU 零负担;
-
视觉柔和:
smoothstep实现无锯齿光斑边缘; - 灵活可控:可调节光斑大小、颜色、速度;
-
资源高效:
Clip避免无效像素处理; - WPF 原生集成:无需第三方库,兼容 .NET Framework / .NET Core。
🔜 扩展方向
- 多光斑同步(副歌部分双光效);
- 结合音频节奏驱动光斑速度;
- 使用
WriteableBitmap实现更复杂的粒子+光照混合。
通过 HLSL + Clip + 动画 的组合,WPF 完全可以实现媲美游戏引擎的动态歌词高光效果,既保持了 XAML 的声明式优势,又释放了 GPU 的渲染潜力。