原因:我之所以想做這個專案,是因為在之前查詢關於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檔案,並將這些檔案新增到類庫中。接著,我會在具體的專案中引入這個庫來實現效果
專案實現細節
開發環境
- 使用的MVVM庫:CommunityToolkit.Mvvm
- 目標框架:.NET 8.0
- 開發工具:Visual Studio 2022
使用的樣式庫
專案中採用了AduSkin樣式庫。個人建議:我不特別推薦使用AduSkin,主要是因為它缺乏官方文件,需要透過檢視原始碼來學習使用。此外,一些樣式的命名與原始碼中的不一致,這可能會導致一些困惑。(備註:我最初選擇使用AduSkin是因為其UI設計在網路上獲得了好評,儘管某些效果確實很吸引人,但缺少文件導致使用上的不便。)
專案結構概述
專案在構建過程中,考慮到幾種特效之間存在一些共同的重複元素,如圖片展示和圖片匯入功能,因此我將這些共用功能模組化。
- 操作區域的定製化:對於每種不同的特效,操作區的需求也不盡相同。在Common控制元件中,我使用了Option控制元件來進行替代,以便於在外部進行定製。
- 特效的動態調整:每種特效的具體實現都有所不同,因此我設定了一個獨立的屬性
ImageEffect
,允許從外部動態修改。 - 公共控制元件:
CommonEffectControl
作為一個公共控制元件,用於整合圖片顯示和圖片匯入的共通功能。
具體引用的步驟
需要新增命令空間
xmlns:common="clr-namespace:CT.WPF.MagicEffects.Demo.UserControls.Common"
前臺程式碼
檢視程式碼 <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>
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;
}
model部分
檢視程式碼 partial class Ordinarys : ObservableObject {
[ObservableProperty]
private string? title;
[ObservableProperty]
private ShaderEffect? objectShaderEffect;
}
還有攝像頭濾鏡覆蓋的效果歡迎大家體驗!!!
已經發布nuget:dotnet add package MagicEffects --version 1.0.0
github:CT.WPF.MagicEffects