Использование pixel shaders в Windows Presentation Foundation – Часть 2

Использование в WPF

 

В WPF Вы можете сначала на High Level Shading Language (HLSL) создать собственный эффект с использованием пиксельных шейдеров. Далее, с помощью компилятора шейдеров Direct3D fxc.exe, скомпилировать HLSL код в байт-код.

fxc /T ps_2_0 /E main /Fo<name of HLSL file>.ps <name of HLSL file>.fx

 

Создать PixelShader для загрузки байт-кода и обернуть эффект в ShaderEffect . В итоге этот эффект можно будет присвоить свойству Effect объекта UIElement.

 

Рассмотрим процесс создания «обертки» на примере шейдера с яркостью и контрастностью.


 

Шейдер для изменение яркости и контрастности:

 

sampler2D input : register(s0); float brightness : register(c0); float contrast : register(c1);

 

 

float4 main(float2 uv : TEXCOORD) : COLOR {     float4 color = tex2D(input, uv);     float4 result = color;     result = color + brightness;     result = result * (1.0+contrast)/1.0;     return result; }

 

 

Создаем класс, производный от ShaderEffect, объявляем входные параметры, определим свойства зависимостей, которые представляют собой параметры эффекта и вводные значения поверхности на основе объекта Brush:

 

public class BrightContrastEffect : ShaderEffect     {         …

 

 

      public Brush Input         {             get { return (Brush)GetValue(InputProperty); }             set { SetValue(InputProperty, value); }         }

 

 

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(BrightContrastEffect), 0);

 

 

Загружаем пиксельный шейдер из ресурсов (вы должны схокпилировать ps файл как ресурсный):

 

private static PixelShader m_shader = new PixelShader() { UriSource = new Uri(@"pack://application:,,,/CustomPixelRender;component/bricon.ps") };

 

 

Параметры, с добавочным специальным PixelShaderConstantCallback, который получает числовое значение id для зарегистрированных свойств из пиксельного шейдера.

 

 

public float Brightness         {             get { return (float)GetValue(BrightnessProperty); }             set { SetValue(BrightnessProperty, value); }         }

 

 

        public static readonly DependencyProperty BrightnessProperty = DependencyProperty.Register("Brightness", typeof(double), typeof(BrightContrastEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0)));

 

 

        public float Contrast         {             get { return (float)GetValue(ContrastProperty); }             set { SetValue(ContrastProperty, value); }         }

 

 

public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast", typeof(double), typeof(BrightContrastEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(1)));

 

 

 

Несколько обновлений:

 

 

public BrightContrastEffect()         {             PixelShader = m_shader;             UpdateShaderValue(InputProperty);             UpdateShaderValue(BrightnessProperty);             UpdateShaderValue(ContrastProperty);

 

 

        }

 

 

Следующий шаг – описание интерфейса на языке XAML.

 

 

Создадим 2 ползунка для управления яркостью и контрастностью:

 

 

<UniformGrid Grid.Row="1">

 

 

            <TextBlock Text="Brightness"/>

 

 

            <Slider Maximum="1" Minimum="-1" Name="bVal"/>

 

 

            <TextBlock Text="Contrast"/>

 

 

            <Slider Maximum="1" Minimum="-1" Name="cVal"/>

 

 

</UniformGrid>

 

 

Каждый элемент пользовательского интерфейса в WPF имеет свойство Effect, которое разработано специально для создания произвольных шейдерных эффектов. Мы работаем с изображением, также привяжем к параметрам пиксельного шейдера значения с ползунков:

 

 

<Image Source="img.jpg">

 

 

            <Image.Effect>

 

 

                <l:BrightContrastEffect

 

 

                    Brightness="{Binding ElementName=bVal, Path=Value}"

 

 

                    Contrast="{Binding ElementName=cVal, Path=Value}"/>

 

 

            </Image.Effect>

 

 

</Image>

 

 

Получили простое приложение, использующее пиксельные шейдеры в WPF.

 

 

Еще один пример – управление прозрачностью.  Используем один параметр "Opacity". Эффект делает элемент управления полупрозрачным в зависимости от этого параметра. (Давайте опустим тот факт, что эффект полностью безполезный).

 

 

Создаем пиксельный шейдер Transparency.fx:

 

 

sampler2D implicitInputSampler : register(S0);

 

 

float opacity : register(C0);

 

 

float4 main(float2 uv : TEXCOORD) : COLOR {

 

 

  float4 color = tex2D(implicitInputSampler, uv);

 

 

  return color * opacity;

 

 

}

 

 

Ключевое слово register используется для связи констант с регистрами, где будут сохраняться входные параметры. Существует несколько регистров изображения, которые содержат  входные данные изображения и называются S0, S1, S2, и т.д. (большинство пиксельных шейдеров используют только один такой регистр). Также существуют "регистры с плавающей точкой" C0, C1, C2, и т.д., эти регистры сохраняют другую входную информацию.

 

 

Функция main – входная точка шейдера, возвращает значение типа float4. Такая функция должна быть в каждом пиксельном шейдере.

 

 

Класс эффекта, содержащийся в файле Transparency.cs:

 

 

public class Transparency : ShaderEffect {

 

 

  static Transparency() {

 

 

    // Associate _pixelShader with our compiled pixel shader

 

 

    _pixelShader.UriSource = Global.MakePackUri("Transparency.ps");

 

 

  }

 

 

 

  private static PixelShader _pixelShader = new PixelShader();

 

 

 

  public Transparency() {

 

 

    this.PixelShader = _pixelShader;

 

 

    UpdateShaderValue(InputProperty);

 

 

    UpdateShaderValue(OpacityProperty);

 

 

  }

 

 

 

  public Brush Input {

 

 

    get { return (Brush)GetValue(InputProperty); }

 

 

    set { SetValue(InputProperty, value); }

 

 

  }

 

 

 

  public static readonly DependencyProperty InputProperty =

 

 

      ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(Transparency), 0);

 

 

 

  public double Opacity {

 

 

    get { return (double)GetValue(OpacityProperty); }

 

 

    set { SetValue(OpacityProperty, value); }

 

 

  }

 

 

 

  public static readonly DependencyProperty OpacityProperty =

 

 

      DependencyProperty.Register("Opacity", typeof(double), typeof(Transparency),

 

 

        new UIPropertyMetadata(1.0d, PixelShaderConstantCallback(0)));

 

 

}

 

 

Пиксельный шейдер хранится в  private static поле _pixelShader. Он хранится как static, потому что одного экземпляра скомпилированного кода шейдера достаточно для всего класса. Конструктор инициализирует свойство UriSource объекта_pixelShader - оно отвечает за место расположения скомпилированного байт-кода пиксельного шейдера. Метод Global.MakePackUri() представляет имя файла в виде пути примерно следующего содержания: "pack://application:,,,/[assemblyname];component/Transparency.ps".

 

 

Должно быть свойство типа Brush под названием "Input". Это свойство содержит входное изображение и обычно оно не задается напрямую – оно будет установлено  автоматически, когда наш эффект будет применен. 
				

 


Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.