C#+WPF 实现图像滤镜,打造惊艳视觉效果

210 阅读4分钟

前言

之所以想做这个项目,是因为在之前查找关于C#/WPF相关资料时,发现讲解图像滤镜的资源非常稀缺。

另外,注意到许多现有的开源库主要基于CPU进行图像渲染。这种方式在处理大量图像时,会导致CPU的渲染负担过重。

因此,将在下文中介绍如何通过GPU渲染来有效实现图像的各种滤镜效果。

效果图

生成的效果

生成效果的方法

生成效果的方法:主要是通过参考Shazzam Shader Editor 来编写HLSL像素着色器。

HLSL(High Level Shader Language,高级着色器语言)是Direct3D着色器模型所需的一种语言。WPF不仅支持Direct3D 9,还支持使用HLSL来创建着色器。

虽然可以使用多种编辑器来编写HLSL,但Shazzam Shader Editor是一款专为WPF设计的编辑器,它专门用于实现像素着色器。

使用Shazzam可以简化将像素着色器集成到WPF项目中所需的各种手动操作。(关于如何使用Shazzam,可在线查找详细教程。)

在项目中,根据所需的效果生成相应的.PS和.CS文件,并将这些文件添加到类库中。接着会在具体的项目中引入这个库来实现效果

项目实现细节

1、开发环境

MVVM 库:CommunityToolkit.Mvvm

目标框架:.NET 8.0

开发工具:Visual Studio 2022

2、使用的样式库

项目中采用了AduSkin样式库。

个人建议:不特别推荐使用AduSkin,主要是因为它缺乏官方文档,需要通过查看源代码来学习使用。

此外,一些样式的命名与源代码中的不一致,这可能会导致一些困惑。(

备注:我最初选择使用AduSkin是因为其UI设计在网络上获得了好评,尽管某些效果确实很吸引人,但缺少文档导致使用上的不便。)

项目结构概述

项目在构建过程中,考虑到几种特效之间存在一些共同的重复元素,如图片展示和图片导入功能,因此我将这些共用功能模块化。

操作区域的定制化: 对于每种不同的特效,操作区的需求也不尽相同。在Common控件中,我使用了Option控件来进行替代,以便于在外部进行定制。

特效的动态调整: 每种特效的具体实现都有所不同,因此我设定了一个独立的属性ImageEffect,允许从外部动态修改。

公共控件:CommonEffectControl作为一个公共控件,用于整合图片显示和图片导入的共通功能。

具体引用的步骤

需要添加命令空间

xmlns:common="clr-namespace:CT.WPF.MagicEffects.Demo.UserControls.Common"

1、前台代码

<common:CommonEffectControl ImageEffect="{Binding SelectedOrdinary.ObjectShaderEffect}">
    <common:CommonEffectControl.Option>
        <StackPanel Orientation="Vertical">
            <Border BorderBrush="Transparent" BorderThickness="0">
                <Skin:MetroScrollViewer ScrollViewer.VerticalScrollBarVisibility="Visible">
                    <ListView
                        Width="240"
                        Height="550"
                        BorderThickness="0"
                        ItemsSource="{Binding Ordinarys}"
                        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                        ScrollViewer.VerticalScrollBarVisibility="Hidden"
                        SelectedItem="{Binding SelectedOrdinary, Mode=TwoWay}"
                        SelectionMode="Single">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <StackPanel
                                    MinHeight="110"
                                    VerticalAlignment="Center"
                                    Orientation="Vertical">
                                    <Viewbox
                                        Width="99"
                                        Height="78"
                                        Margin="2">
                                        <Image Effect="{Binding ObjectShaderEffect}" Source="{Binding Path=Main.SelectedImagePath, Source={StaticResource Locator}}" />
                                    </Viewbox>
                                    <TextBlock
                                        HorizontalAlignment="Center"
                                        FontSize="14"
                                        Foreground="{Binding RelativeSource={RelativeSource AncestorType={x:Type Skin:MetroWindow}}, Path=BorderBrush, Mode=TwoWay}"
                                        Text="{Binding Title}"
                                        TextWrapping="Wrap" />
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                        <ListView.ItemsPanel>
                            <ItemsPanelTemplate>
                                <WrapPanel Orientation="Horizontal" />
                            </ItemsPanelTemplate>
                        </ListView.ItemsPanel>
                    </ListView>

                </Skin:MetroScrollViewer>
            </Border>
        </StackPanel>
    </common:CommonEffectControl.Option>
</common:CommonEffectControl>

2、ViewModel 部分

partial class OrdinaryEffectViewModel : ObservableObject 
{
    public OrdinaryEffectViewModel() 
    {
        Ordinarys = new ObservableCollection<Ordinarys> 
        {
            new Ordinarys(){ Title="灰度", ObjectShaderEffect= new GrayScaleEffect ()},
            new Ordinarys(){ Title="位移", ObjectShaderEffect= new DirectionalBlurEffect ()},
            new Ordinarys(){ Title="老电影", ObjectShaderEffect= new OldMovieEffect ()},
            new Ordinarys(){ Title="锐化", ObjectShaderEffect= new SharpenEffect ()},
        };
    }
    [ObservableProperty]
    private ObservableCollection<Ordinarys> ordinarys;
    [ObservableProperty]
    private Ordinarys selectedOrdinary;
}

3、model 部分

partial class Ordinarys : ObservableObject 
{
    [ObservableProperty]
    private string? title;
    [ObservableProperty]
    private ShaderEffect? objectShaderEffect;
}

摄像头滤镜覆盖

还有摄像头滤镜覆盖的效果欢迎大家体验!!!

已经发布NuGet

dotnet add package MagicEffects --version 1.0.0

项目地址

GitHub:github.com/CSGarden/CT…

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:庆喜

出处:cnblogs.com/qingxi11/p/18138196

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!