[PL] Shadery w WPF – moje pierwsze podejście
Artur Żarski u mnie w firmie, od jakiegoś czasu, a dokładniej odkąd zobaczył moje zabawy z Xna męczył mnie abym napisał Shader do WPF. W końcu znalazłem chwilkę.
Przy okazji zabawy obaliłem jedną tezę opublikowaną na poniższym blogu:
https://blogs.msdn.com/greg_schechter/archive/2008/09/16/introducing-multi-input-shader-effects.aspx
Co prawda nie mam takiego ładnego bindowania, ale wiele inputów (samplerów) jestem w stanie dołożyć już teraz. No ale do dzieła.
Założyłem sobie dwa projekty, jeden z aplikacją WPF, drugi z biblioteką Shaderów.
Finalnie wygląda to tak:
Na swoim oknie w WPF osadziłem jedną z kontrolek WPFKit czyli kalendarz, w Visual Studio wygląda to następująco:
Nic wielkiego, ale podobnie jak na wymienionym wyżej blogu w Xaml dodałem sobie dodatkowy obrazek:
<Window.Resources>
<ImageBrush x:Key="phongMap" x:Name="phongMap" ImageSource="phong.jpg" />
</Window.Resources>
Mój phong.jpg wygląda dokładnie tak:
W kodzie mojego okna dodałem jeszcze obsługę mojego Shadera:
InversePhongShader inversePhongEffect;
public EffectWindow()
{
InitializeComponent();
inversePhongEffect = new InversePhongShader();
inversePhongEffect.Input2 = (Brush)Resources["phongMap"];
calendar1.Effect = inversePhongEffect;
calendar1.MouseMove += new MouseEventHandler(calendar1_MouseMove);
}
void calendar1_MouseMove(object sender, MouseEventArgs e)
{
Point mP = e.GetPosition(calendar1);
mP.X/=calendar1.ActualWidth;
mP.Y/=calendar1.ActualHeight;
inversePhongEffect.MousePosition = mP;
}
Co tutaj się wydarzyło? Stworzyłem instancję swojego Effektu (Shadera), który opiszę później. Linijkę niżej dodałem drugi Input w postaci właśnie wymienionego wyżej JPG.
Greg Schechter obiecuje, że w RTM będzie się dało ładnie takie parametry bindować w Xamlu, ja niestety sposiłkowałem się kodem. (ale działa już teraz!)
Podpiąłem też zdarzenie generowane, gdy mysz będzie poruszana na kontrolce kalendarza.
Bedzie mi to potrzebne w shaderze, gdyż to co ma on robić to Odwracać kolorystykę oryginalnej kontrolki, tak żeby była ciemna nie jasna (Inverse) oraz żeby dodawał efekt światełka z latarki w miejscu gdzie jest kursor myszy. Zdarzenie przekazuje parametry związane z pozycją myszy.
Jest tam wykonane dodatkowe dzielenie ponieważ shader rozumie współrzędne w skali 0.0-1.0 a nie w pixlach.
Aby te parametry (MousePosition, dodatkowy Sampler) zadziałały potrzebna była jeszcze modyfikacja w kodzie klasy efektu:
public class InversePhongShader : ShaderEffect
{
…
public InversePhongShader()
{
this.PixelShader = _pixelShader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(Input2Property);
UpdateShaderValue(MousePositionProperty);
}
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
public Brush Input2
{
get { return (Brush)GetValue(Input2Property); }
set { SetValue(Input2Property, value); }
}
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(InversePhongShader), 0);
public static readonly DependencyProperty Input2Property =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input2", typeof(InversePhongShader), 1);
public Point MousePosition
{
get { return (Point)GetValue(MousePositionProperty); }
set { SetValue(MousePositionProperty, value); }
}
public static readonly DependencyProperty MousePositionProperty =
DependencyProperty.Register("MousePosition", typeof(Point), typeof(InversePhongShader),
new UIPropertyMetadata(new Point(.5,.5), PixelShaderConstantCallback(0)));
…
}
Potem już tylko Pixel Shader w HLSL:
//-----------------------------------------------------------------------------------------
// Shader constant register mappings (scalars - float, double, Point, Color, Point3D, etc.)
//-----------------------------------------------------------------------------------------
float2 mousePosition : register(C0);
//--------------------------------------------------------------------------------------
// Sampler Inputs (Brushes, including ImplicitInput)
//--------------------------------------------------------------------------------------
sampler2D implicitInputSampler : register(S0);
sampler2D phongMapSampler : register(S1);
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = 1 - tex2D(implicitInputSampler, uv);
float4 colorPhong;
float2 phongUV = mousePosition;
float dist = length(phongUV-uv);
if (dist>.3) {
colorPhong = (0,0,0,0);
color/=4;// colorPhong;
} else {
colorPhong = tex2D(phongMapSampler, (.5+dist));
color = color + colorPhong*(1.0-dist*2);
}
color.a=1.0;
return color;
}
I taki mam efekt, gdy uruchomię swoją aplikację:
Oczywiście jak ruszam mychą to światełko za nią podąża.
Technorati Tags: Polish posts,WPF,coding,DirectX
Comments
Anonymous
January 05, 2009
Witam was w Anno Domini 2009. Dwa tygodnie laby i praktycznie zero komputera. Założenia świąteczne dotrzymałemAnonymous
March 11, 2009
W ostatnią sobotę miałem okazję i przyjemność ponownie odwiedzić Kraków i uczestniczyć w konferencji