WPF/C#實現影像濾鏡最佳化方案:打造炫目視覺體驗!

庆喜發表於2024-04-16

原因:我之所以想做這個專案,是因為在之前查詢關於C#/WPF相關資料時,我發現講解影像濾鏡的資源非常稀缺。此外,我注意到許多現有的開源庫主要基於CPU進行影像渲染。這種方式在處理大量影像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何透過GPU渲染來有效實現影像的各種濾鏡效果。

生成的效果

WPF/C#實現影像濾鏡最佳化方案:打造炫目視覺體驗!WPF/C#實現影像濾鏡最佳化方案:打造炫目視覺體驗!WPF/C#實現影像濾鏡最佳化方案:打造炫目視覺體驗!

生成效果的方法:我主要是透過參考Shazzam Shader Editor來編寫HLSL畫素著色器。

HLSL(High Level Shader Language,高階著色器語言)是Direct3D著色器模型所需的一種語言。WPF不僅支援Direct3D 9,還支援使用HLSL來建立著色器。雖然可以使用多種編輯器來編寫HLSL,但Shazzam Shader Editor是一款專為WPF設計的編輯器,它專門用於實現畫素著色器。使用Shazzam可以簡化將畫素著色器整合到WPF專案中所需的各種手動操作。(關於如何使用Shazzam,可線上查詢詳細教程。)

在我的專案中,我根據所需的效果生成相應的.PS和.CS檔案,並將這些檔案新增到類庫中。接著,我會在具體的專案中引入這個庫來實現效果

WPF/C#實現影像濾鏡最佳化方案:打造炫目視覺體驗!

專案實現細節

開發環境

  • 使用的MVVM庫:CommunityToolkit.Mvvm
  • 目標框架:.NET 8.0
  • 開發工具:Visual Studio 2022

使用的樣式庫

專案中採用了AduSkin樣式庫。個人建議:​我不特別推薦使用AduSkin,主要是因為它缺乏官方文件,需要透過檢視原始碼來學習使用。此外,一些樣式的命名與原始碼中的不一致,這可能會導致一些困惑。(備註:我最初選擇使用AduSkin是因為其UI設計在網路上獲得了好評,儘管某些效果確實很吸引人,但缺少文件導致使用上的不便。)

WPF/C#實現影像濾鏡最佳化方案:打造炫目視覺體驗!

專案結構概述

專案在構建過程中,考慮到幾種特效之間存在一些共同的重複元素,如圖片展示和圖片匯入功能,因此我將這些共用功能模組化。

  • 操作區域的定製化:​對於每種不同的特效,操作區的需求也不盡相同。在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

相關文章