Хотя мы можем создавать и свои шейдеры, некоторые распространенные эффекты уже реализованы и являются частью фреймворка. К ним в частности, в инфраструктуре WPF и Silverlight уже реализованы эффект размытия (класс BlurEffect) и эффект тени (класс DropShadowEffect). Поэтому, если требуется создать эффект размытия, то, возможно, имеет смысл воспользоваться встроенным эффектом.
Итак, создадим новое приложение Silverlight. Для этого откроем Visual Web Developer 2010 Express и выберем в меню New Project (Создать проект). В списке шаблонов проекта выберем шаблон Silverlight Application
При создании нового приложения уберем галочку с поля, чтобы все настройки были следующие:
Добавим в приложение элемент Image, к изображению на котором мы будем применять эффект, и добавим кнопку, после нажатия которой и будет применяться эффект:
<UserControl x:Class="SilverlightShaders.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="250" d:DesignWidth="300"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="4*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Image x:Name="Image1" Grid.Row="0" Source="bronz.jpg" /> <Button x:Name="Button1" Grid.Row="1" Content="Applay Effect" Width="120" Height="30" Click="Button1_Click" /> </Grid> </UserControl>
В обработчике нажатия кнопки Button1_Click
для начала установим для изображения эффект размытия:
private void Button1_Click(object sender, RoutedEventArgs e) { System.Windows.Media.Effects.BlurEffect blurEffect = new System.Windows.Media.Effects.BlurEffect(); blurEffect.Radius = 10; Image1.Effect = blurEffect; }
Запустим приложения на выполнение.
Если до нажатия на кнопку у нас было такое изображение | То после нажатия оно будет следующим |
Эффект сработал. Но это вообщем-то была прелюдия. Использовать встроенные эффекты не так интересно, поэтому перейдем к самому созданию эффекта.
Чтобы создать свой эффект нам надо вначале создать текстовый файл и внести в него код шейдера на языке HLSL. Затем этот файл компилируется при помощи компилятора fxc.exe в бинарную форму и добавляется в качестве ресурса в проект WPF или Silverlight. Но даже после этого нам еще надо будет написать класс-обертку, чтобы мы могли использовать наш эффект в приложении, также как BlurEffect.
Для начала реализуем самый простой эффект - эффект инверсии цветов. Добавим в проект простой текстовый файл и назовем его invertcolor.txt. Затем добавим в него следующий код:
sampler2D InputTexture; float4 main(float2 uv : TEXCOORD) : COLOR { float4 color = tex2D(InputTexture, uv); float4 invertedcolor = float4(color.a-color.rgb,color.a); return invertedcolor; }
Что данный код делает? Во-первых, переменная InputTexture, являющаяся объектом sampler2D, у нас будет представлять текстуру или наше изображение в элементе Image1.
Далее у нас идет функция main, которая возвращает значение типа float4. float4 представляет массив значений с плавающей точкой. Например,
мы можем определить объект float4 и инициализировать его значение с помощью следующей строки:
float4 color = float4(1, 0, 0, 1);
. В нашем случае значение float4 будет нести цвет пикселя в виде float4(red, green, blue, alpha)
TEXCOORD и COLOR - это семантики шейдера. TEXCOORD представляет координаты текстуры, а COLOR - цвет.
Затем функция tex2D считывает двухмерную текстуру, и мы производим интертирование цвета.
Код написан, и мы можем скомпилировать шейдер. Для этого зайдем в меню Пуск и среди программ выберем Microsoft DirectX SDK (June 2010)-> Microsoft DirectX Command Prompt. В открывшейся консоли введем следующую строку:
fxc /T ps_2_0 /E main /Fo output.ps invertcolor.txt
После этого компилятор fxc.exe скопилирует нам файл output.ps:
Небольшое примечание. Поскольку по умолчанию в Visual Studio используется кодировка UTF, и если вы набираете hlsl-код в Visual Studio, то потом может произойти ошибка компиляции. Чтобы такого не происходило, надо кодировку файла изменить на UTF without BOM.
Теперь добавим в проект файл output.ps и убедимся, что для него для свойства Bild Action установлено значение Resource:
Теперь уже перейдем к шарпу. Для использования нашего шейдера, нужно создать класс, унаследованный от базового класса ShaderEffect. Класс-обертка (назовем его InvertEffect) будет иметь следующий код:
public class InvertEffect : ShaderEffect { private PixelShader pixelShader = new PixelShader(); public InvertEffect() { pixelShader.UriSource = new Uri("/SilverlightShaders;component/output.ps", UriKind.Relative); this.PixelShader = pixelShader; this.UpdateShaderValue(InputProperty); } public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(InvertEffect), 0); public Brush Input { get { return ((Brush)this.GetValue(InputProperty)); } set { this.SetValue(InputProperty, value); } } }
В нашем классе объявлено одно свойство зависимостей InputProperty. Оно и будет представлять те данные, которые потом будут подаваться на вход в пиксельный шейдер из растеризатора. И теперь мы готовы к применению нового эффекта. Изменим код обработчика нажатия кнопки следующим образом:
private void Button1_Click(object sender, RoutedEventArgs e) { InvertEffect invertEffect = new InvertEffect(); Image1.Effect = invertEffect; }
Запустим приложение, нажмем на кнопку, и наше изображение инвертирует все цвета:
И как и любой другой эффект, мы можем использовать наш эффект в xaml-коде:
<UserControl x:Class="SilverlightShaders.MainPage" xmlns:local="clr-namespace:SilverlightShaders" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="250" d:DesignWidth="300"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="4*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Image x:Name="Image1" Grid.Row="0" Source="bronz.jpg"> <Image.Effect> <local:InvertEffect /> </Image.Effect> </Image> <Button x:Name="Button1" Grid.Row="1" Content="Applay Effect" Width="120" Height="30" Click="Button1_Click" /> </Grid> </UserControl>
Мы использовали проект Silverlight, но то же самое относится и к WPF.
Очевидно, что чтобы создавать эффекты на основе шейдеров HLSL, надо знать сам язык HLSL или ориентироваться. Весь справочный материал по этому языку можно найти как всегда на msdn по адресу https://msdn.microsoft.com/en-us/library/windows/desktop/bb509561(v=vs.85).aspx
Теперь создадим еще несколько эффектов. Сделаем эффект оттенков серого. Он будет похож на наш предыдущий эффект:
sampler2D input; float4 main(float2 uv : TEXCOORD) : COLOR { float4 color = tex2D(input, uv); float gray = dot(color.rgb, float3(0.2126, 0.7152, 0.0722)); return float4(gray, gray, gray, color.a); }
Здесь надо отметить функцию dot, которая возвращает скалярное произведение двух векторов типа float3. Мы также создаем класс эффекта и устанавливаем новый эффект для объекта Image1:
И еще один эффект - создание сепии:
sampler2D Input; float4 main(float2 uv : TEXCOORD) : COLOR { float4 TintColor = float4(0.9,0.7,0.3,1); float4 color = tex2D(Input, uv); float gray = dot(color.rgb, float3(0.2126, 0.7152, 0.0722)); float4 grayColor = float4(gray, gray, gray, color.a); return grayColor * TintColor; }