WPF 使用 HLSL #x2B; Clip 实现高亮歌词光照效果

最近在搓一个Lyricify Lite类似物,原本使用渐变画刷实现歌词高亮,但是发现视觉效果与Apple Music相去甚远:单纯使用白色渐变画刷缺乏“高亮”的光照感觉,而Apple Music的歌词高亮则更像是有光线投射在歌词上,形成一种柔和的发光效果。

受到吕毅大佬的文章使用 WPF 做一个可以逼真地照亮你桌面的高性能阳光 - walterlv启发,遂尝试使用HLSL编写一个简单的文本高亮着色器。先来看实装在LemonLite中的效果:

整体上高亮文本与背景混色自然,渐变过渡由WPF动画驱动,高亮部分就有了光感。

以下是本文的最小可运行示例:TwilightLemon/TextHighlighterTest: WPF 流光特效文本控件

一、实现思路

简单说一下踩过的几个坑:

  1. 直接给TextBlock焊上Effect会导致文本像素化,变得模糊而且难以处理文本边界
  2. 使用VisualBrush,内部套一个Rectangle with Effect, 然后用这个VisualBrush作为TextBlock的Foreground,性能极差
  3. 给Rectangle with Effect做一个Clip裁剪出文本形状,性能可以,但是需要复刻TextBlock的排版逻辑

最终使用了第三种方案,并将其封装成一个用户控件。

如此一来,HLSL着色器要干的事情就很简单了:取一个高亮过渡位置pos,根据采样像素的X坐标计算光照强度,然后将这个强度与文本颜色做加法混合即可。

二、HLSL着色器代码

为了适配歌词高亮需求,我又追加了一些特性:

  1. 支持高亮颜色自定义
  2. 支持高亮宽度调整
  3. 支持切换高亮模式(加法混合/线性渐变)

以下是完整的HLSL代码: (咋没有hlsl高亮呢? )

sampler2D input : register(s0); float HighlightPos : register(c0); float HighlightWidth : register(c1); float4 HighlightColor : register(c2); float UseAdditive : register(c3); // 0 = lerp, 1 = additive float HighlightIntensity : register(c4); float4 main(float2 uv : TEXCOORD) : COLOR { float4 color = tex2D(input, uv); float d = max(0, uv.x - HighlightPos); float glow = saturate(1 - d / HighlightWidth); glow = glow * glow; float intensity = glow * HighlightColor.a * HighlightIntensity; float3 lerpResult = lerp(color.rgb, HighlightColor.rgb, intensity); float lerpAlpha = lerp(color.a, 1.0, intensity); float3 additiveResult = color.rgb + HighlightColor.rgb * intensity; color.rgb = lerp(lerpResult, additiveResult, UseAdditive); color.a = lerp(lerpAlpha, color.a, UseAdditive); return color; }

着色器输入参数

参数作用
/* by 01130.hk - online tools website : 01130.hk/zh/worldtime.html */ input输入纹理(文本像素)
/* by 01130.hk - online tools website : 01130.hk/zh/worldtime.html */ HighlightPos高亮过渡位置(0-1,从左到右, 当然也可以取负数)
HighlightWidth高亮衰减宽度(0-1)
HighlightColor高亮颜色(含透明度)
UseAdditive混合模式开关(0=线性渐变,1=加法混合)
HighlightIntensity高亮强度倍数

计算过程

  1. 采样 → 读取当前像素颜色
    color = tex2D(input, uv)
  2. 计算距离 → 像素X坐标与高亮位置的距离
    d = max(0, uv.x - HighlightPos)
  3. 计算光晕强度 → 根据距离生成平滑衰减
    glow = saturate(1 - d / HighlightWidth)
    glow = glow * glow // 平方处理,使衰减更陡峭
  4. 最终强度 = 光晕 × 颜色透明度 × 强度倍数
  5. 混合处理 → 根据UseAdditive选择混合方式
    • 线性渐变:在原色和高亮色间插值
    • 加法混合:直接叠加高亮色
函数说明
  • saturate(x)- 将值钳制在 [0, 1] 范围内
  • step(edge, x)- 如果 x < edge 返回 0,否则返回 1
  • lerp(a, b, t)- 线性插值,计算 a + (b-a)×t
  • tex2D(sampler, uv)- 从纹理采样指定坐标的像素

编译并加载着色器

编译HLSL代码生成.ps文件,然后在WPF中使用PixelShader类加载:

fxc /T ps_3_0 /E main /Fo TextGlow.psTextGlow.hlsl

封装成Effect类此处不做赘述,只需要严格按照参数顺序传递即可。

三、WPF用户控件封装

核心出装

用一个Rectangle承载着色器效果,通过FormattedText生成文本的几何形状作为裁剪路径,这样WPF只会渲染文本区域并且保留清晰的文本边缘。

属性继承与元数据覆写

为了让高亮控件支持标准的文本属性(字体、大小、粗细等),使用了WPF的元数据覆写机制。在静态构造函数中对FontFamilyFontSizeFontWeight等属性进行OverrideMetadata,绑定到统一的OnTextPropertyChanged回调。当这些属性变化时,触发文本裁剪的重新计算。类似地,Foreground属性也被覆写,当文本颜色改变时直接更新Rectangle的填充颜色。

1staticHighlightTextBlock()2{3FontFamilyProperty.OverrideMetadata(typeof(HighlightTextBlock),4newFrameworkPropertyMetadata(SystemFonts.MessageFontFamily, OnTextPropertyChanged));5FontSizeProperty.OverrideMetadata(typeof(HighlightTextBlock),6newFrameworkPropertyMetadata(14.0, OnTextPropertyChanged));7FontWeightProperty.OverrideMetadata(typeof(HighlightTextBlock),8newFrameworkPropertyMetadata(FontWeights.Normal, OnTextPropertyChanged));9ForegroundProperty.OverrideMetadata(typeof(HighlightTextBlock),10newFrameworkPropertyMetadata(Brushes.Black, OnForegroundChanged));11}1213privatestaticvoidOnForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)14{15if(disHighlightTextBlock control)16control.PART_Rectangle.Fill = e.NewValueasBrush;17}

这样用户就可以像使用普通TextBlock一样使用这个控件,享受XAML的属性绑定和样式系统。

文本排版与裁剪

UpdateTextClip()方法负责:

  1. 创建排版对象 - 通过FormattedText将文本按照控件的字体、大小、样式参数进行排版,获得与实际渲染完全一致的文本度量
varformattedText =newFormattedText( Text, CultureInfo.CurrentCulture, FlowDirection,newTypeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Brushes.Black, VisualTreeHelper.GetDpi(this).PixelsPerDip);
  1. 处理换行与宽度约束 - 当启用TextWrapping时,从WidthMaxWidthActualWidth中取出约束宽度。只有在需要换行且有约束宽度的情况下,才设置MaxTextWidth让文本自动折行
varconstraintWidth = !double.IsNaN(Width) && Width >0?Width : (!double.IsInfinity(MaxWidth) && MaxWidth >0?MaxWidth : ActualWidth);if(TextWrapping != TextWrapping.NoWrap && constraintWidth >0) formattedText.MaxTextWidth= constraintWidth;
  1. 计算容器尺寸 - NoWrap下容器宽度就是文本宽度,换行模式下则为约束宽度,以确保Rectangle的大小与实际文本范围匹配
varcontainerWidth = TextWrapping ==TextWrapping.NoWrap?textWidth : (constraintWidth>0? constraintWidth : textWidth);
  1. 处理文本对齐 - 根据TextAlignment计算文本相对于容器的起始偏移(用于居中和右对齐),然后生成Rectangle时传入这个偏移量,同时更新Rectangle的水平对齐属性使其与文本对齐方式一致
doubleoffsetX =0;if(containerWidth >textWidth) { offsetX= TextAlignmentswitch{ TextAlignment.Center=> (containerWidth - textWidth) /2, TextAlignment.Right=> containerWidth -textWidth, _=>0}; }
  1. 生成并应用裁剪 - 调用formattedText.BuildGeometry()得到精确的文本轮廓Rectangle,设置为Rectangle.Clip。这样着色器的输出就被限制在文本像素范围内
vargeometry = formattedText.BuildGeometry(newPoint(offsetX,0)); PART_Rectangle.Clip=geometry; PART_Rectangle.Width=containerWidth; PART_Rectangle.Height= textHeight;

高亮参数的动画驱动

高亮效果的三个关键参数(HighlightPosHighlightWidthHighlightColor)都被暴露为依赖属性,在属性变化回调中直接同步到着色器 Effect 对象:

publicstaticreadonlyDependencyProperty HighlightPosProperty =DependencyProperty.Register( nameof(HighlightPos),typeof(double),typeof(HighlightTextBlock),newPropertyMetadata(0.0, OnHighlightPosChanged));privatestaticvoidOnHighlightPosChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {if(disHighlightTextBlock c) c._effect.HighlightPos= (double)e.NewValue; }

这样就可以在XAML中轻松使用StoryboardDoubleAnimation 驱动HighlightPos的平滑变化,从而实现光线扫过文本的连贯动画效果。

四、看看效果

对比Additive叠加和Lerp线性渐变两种模式,可以看到Additive模式下高亮部分更亮更有光感,而Lerp模式尾段偏灰。

尝试使用彩色高亮,变成了混色效果。

写在最后

LemonLite正在龟速开发中,过程中遇到的各种问题和解决方案都会陆续写成博客分享出来,欢迎各位大佬持续关注。
什么!你问我开头的背景是怎么做的?见上一篇文章:WPF 使用GDI+提取图片主色调并生成Mica材质特效背景 - Twlm's Blog

参考资料:

    • WPF 像素着色器入门:使用 Shazzam Shader Editor 编写 HLSL 像素着色器代码 - walterlv
    • 使用 WPF 做一个可以逼真地照亮你桌面的高性能阳光 - walterlv

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名TwilightLemon,不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1174741.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java controller的DTO如果有内部类,应该注意什么

在Java Controller中使用带有内部类的DTO时&#xff0c;需要注意以下关键点&#xff1a;1. 内部类必须是静态的&#xff08;static&#xff09;// ✅ 正确做法 public class UserDTO {private String name;private Address address;public static class Address { // 必须是st…

Nano Banana AI 绘画创作前端代码(使用claude code编写)

在线 https://chat.xutongbao.top/nextjs/light/nano use clientimport Header from /components/header import {ArrowLeft,Send,RefreshCw,Sparkles,Upload,X,Download,Copy,Check,ImagePlus,Maximize2, } from lucide-react import { useRouter } from next/navigation imp…

WPF 使用 HLSL + Clip 实现高亮歌词光照效果

最近在搓一个Lyricify Lite类似物,原本使用渐变画刷实现歌词高亮,但是发现视觉效果与Apple Music相去甚远:单纯使用白色渐变画刷缺乏“高亮”的光照感觉,而Apple Music的歌词高亮则更像是有光线投射在歌词上,形成…

Redis 协议兼容:编写一个支持 RESP 协议的 KV Server

标签&#xff1a; #Redis #RESP #Go语言 #网络编程 #中间件开发 #Socket&#x1f4dc; 一、 破译 RESP&#xff1a;Redis 的通信语言 RESP 是一个基于文本的协议&#xff0c;极其简单且高效。它主要由 前缀符号 和 CRLF (\r\n) 组成。 客户端发送给服务端的&#xff0c;永远是一…

排它锁与共享锁详解 - 详解

排它锁与共享锁详解 - 详解2026-01-17 20:58 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; …

Solidity 开发入门:编写一个“去中心化投票系统”,部署在以太坊测试网

标签&#xff1a; #Web3 #Solidity #Ethereum #SmartContract #Remix #DApp &#x1f310; 前言&#xff1a;DApp 的架构逻辑 在 Web2 中&#xff0c;我们请求的是中心化服务器&#xff1b;在 Web3 中&#xff0c;我们直接与区块链上的智能合约交互。 交互流程图 (Mermaid): …

芒格的多学科知识在投资决策中的作用

芒格的多学科知识在投资决策中的作用 关键词:芒格、多学科知识、投资决策、跨学科思维、投资策略 摘要:本文深入探讨了芒格所倡导的多学科知识在投资决策中的重要作用。从背景介绍出发,阐述了研究目的、预期读者、文档结构及相关术语。详细剖析了多学科知识的核心概念,展示…

Flutter三方库鸿蒙适配深度解析:从架构原理到性能优化实践 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

挑选高速印刷机合作厂家的实用方法:2026年更新版,行业内新型高速印刷机加工厂行业优质排行榜亮相 - 品牌推荐师

随着包装行业对生产效率、印刷精度及智能化需求的持续攀升,高速印刷机已成为印刷企业提升核心竞争力的关键设备。然而,市场上设备型号繁多、技术参数复杂,加工厂家的研发实力、生产规模及服务能力差异显著,采购方如…

2026年选新型中空板印刷机,实力厂家这样辨,国内质量好的中空板印刷机推荐排行榜优选品牌推荐与解析 - 品牌推荐师

在包装材料升级与环保政策驱动下,中空板印刷机已成为物流、食品、医药等行业实现高效印刷、降低综合成本的核心设备。其不仅能保障包装印刷的色彩还原度与生产效率,更通过一体化工艺设计显著改善作业环境,减少人工依…

11.1 机器人“仿真-真实”迁移:弥合虚拟与现实间的动力学鸿沟

11.1 “仿真-真实”迁移:弥合虚拟与现实间的动力学鸿沟 11.1.1 引言:现实差距的本质与挑战 在机器人研发流程中,基于物理的仿真提供了无风险、高效率且成本可控的测试与训练环境。然而,任何仿真模型都是对物理世界的近似,由此产生的“现实差距”是部署仿真中训练的策略或…

杭州拼多多代运营对比分析:2026年主流服务商优劣一览 - 前沿公社

随着拼多多平台日益成熟,越来越多品牌与工厂型卖家开始重视专业的代运营服务,以提升流量获取、转化效率和整体销售表现。目前杭州作为中国电商中心之一,聚集了大量拼多多代运营服务商。面对众多选择,商家如何判断哪…

AI原生应用新革命:RAG技术带来的3大变革

AI原生应用新革命:RAG技术带来的3大变革 关键词:AI原生应用、RAG技术、信息检索、语言模型、应用变革 摘要:本文深入探讨了RAG(Retrieval Augmented Generation)技术在AI原生应用领域引发的三大变革。首先介绍了RAG技术的背景和相关概念,接着详细解释了核心概念及其关系,…

Work Life Review Master Plan

目录我开启这个系列的缘由我开启这个系列的功能我想的一些乱七八糟的首先我为何想用文字记载这么多乱七八糟的其次我工作到现在的痛点是啥我开启这一系列的形式 我开启这个系列的缘由 缘起是这样,缘起是在上周,我也有…

2026 年LED大屏广告公司综合实力排行榜单及选择建议指南:2026年LED大屏广告公司如何选?哪家好?哪家强?哪家靠谱?选哪家 - Top品牌推荐

一、LED 大屏设备及综合解决方案提供商 这些企业提供 LED 大屏硬件、广告投放等综合服务,是 LED 大屏广告行业的核心力量。 1. 艾迪亚控股集团(首选 Top 1)基本信息:始创于 1998 年,致力于为企业客户提供 “户外 …

js上传图片前改变图片的格式为png

// 将图片转换为 PNG 格式const convertImageToPng (file: File): Promise<File> > {return new Promise((resolve, reject) > {const reader new FileReader()reader.onload (e) > {const img new window.Image()img.onload () > {// 创建 canvasconst…

11.3 可靠性工程与测试验证:构建可信赖的机器人系统

11.3 可靠性工程与测试验证:构建可信赖的机器人系统 11.3.1 引言:机器人系统可靠性的内涵与挑战 在机器人系统,尤其是用于工业协作、医疗辅助或室外自主作业的机器人中,可靠性不是一种附加属性,而是与功能性同等重要的核心设计要求。可靠性工程旨在通过系统化的设计、分…

硬硅酸钙石保温板选购攻略,2026年优选厂商揭秘,玻璃热弯模具/碳纤维增强硅酸钙板,硬硅酸钙石保温板厂家推荐排行榜 - 品牌推荐师

行业背景与市场趋势分析 随着“双碳”目标推进,工业领域对高效隔热材料的需求持续攀升。硬硅酸钙石保温板凭借耐高温(可达1000℃)、低导热系数(≤0.05W/mK)、抗腐蚀等特性,成为冶金、电力、玻璃制造等行业的优选…

2026年汽车后视镜热弯模具优选厂家,实力品牌大揭秘,铝行业精炼用热鼎盘,汽车后视镜热弯模具实力厂家排行 - 品牌推荐师

引言:行业现状与模具核心价值 随着新能源汽车与智能驾驶技术的快速发展,汽车后视镜的设计需求正从单一功能性向轻量化、高强度、复杂曲面造型方向迭代。作为后视镜生产的核心工艺装备,热弯模具的技术水平直接影响产…