Silverlight 3.0 ve Pixel Shader Efektleri

0 dakikada yazıldı

6492 defa okundu

Düzenle

Pixel Shader efektleri genelde oyun programcılarının duyduğu fakat web
veya windows programcılarının pek de haşır neşir olmadıkları bir
alandır. Tabi bunun birçok nedeni var; Pixel Shader efektleri HLSL
(High Level Shading Language) denilen  farklı bir dil ile yazılarak
DirectX'in SDK'sı ile beraber gelen bir compiler ile kullanılabilir hale
geliyor. Her ne kadar HLSL C tabanlı dillere benzese de özünde esas
problem bu efektlerin kullanılacağı ortamların azlığıydı.

Bundan yaklaşık 9 ay önce WPF'e SP1 ile beraber Pixel Shader
efektlerinin gelmesi sonrasında sizlerle bu konuda bir
yazı

paylaşmıştım. Bugün ise konumuz Silverlight içerisinde Pixel Shader
efeklerinin kullanımı. Silverlight 3.0 ile beraber artık istediğiniz bir
Pixel Shader efektini herhangi bir UIElement yani kontrole rahatlıkla
uygulayabiliyorsunuz. Hatta bu konuda Runtime ile beraber gelen hazır
iki efekt bulunuyor; DropShadow ve Blur.

[XAML]

        <Button

           Content="TIKLA"

           Height="100"

           Width="100">

            <Button.Effect>

                <BlurEffect

                   Radius="10" />

            </Button.Effect>

        </Button>

Yukarıdaki XAML kodu içerisinde gördüğünüz üzere basit bir Button
nesnesine Blur efekti vermek aslında gerçekten çok kolay. Blur
efektinin piksellere uygulanırken ne kadar uygulanacağı ile ilgili bir
de yarıçap verebiliyoruz. Her efektin kendine göre farklı parametreleri
olabiliyor. Verilen bu efektler tamamen gerçek zamanlı olarak
uygulandığı için gerektiğinde kod ile yaratılabilir ve kontrol
edilebilirler.

[XAML]

        <StackPanel>

            <Button

               Content="TIKLA"

               Height="100"

               Width="100">

                <Button.Effect>

                    <BlurEffect

                       x:Name="btnBlur"

                       Radius="10" />

                </Button.Effect>

            </Button>

            <Slider

               Minimum="0"

               Margin="10"

               Value="{Binding
Radius
, ElementName=btnBlur,
Mode
=TwoWay}" />

        </StackPanel>

Hali hazırda herhangi bir efektin farklı özelliklerine animasyonlar
uygulayabileceğiniz gibi isterseniz bu özellikleri yine Silverlight 3.0
ile beraber yeni gelen
ElementBinding
sistemini kullanarak editlenebilir hale de getirebilirsiniz. Yukarıdaki
örneğimizde özellikle gidip BlurEffect nesnemize btnBlur ismini
veriyoruz. Böylece artık bu efektin özelliklerine hem programatik olarak
hem de farklı Binding'lerde rahatlıkla ulaşabiliriz.

[XAML]

        <Storyboard

           x:Name="BlurOL">

            <DoubleAnimation

               Duration="00:00:01.000"

               From="0"

               To="10"

              
Storyboard.TargetName
="btnBlur"

              
Storyboard.TargetProperty
="(BlurEffect.Radius)" />

        </Storyboard>

Örneğimizdeki BlurEffect nesnesinin Radius özelliğini hedef alıp
anime eden bir animasyonun XAML kodunu yukarıda inceleyebilirsiniz. Söz
konusu animasyon çalıştırıldığında düğmemiz bir saniye içerisinde
yavaşça netliğini kaybediyor.

Peki hepsi bu kadar mı?

DropShadow ve Blur efektleri sadece hazır gelenler. Oysa esas güzellik
bizim de kendi Pixel Shader efektlerimizi yazabiliyor olmamız. Tabi
bunun için öncesinde bilgisayarınıza DirectX SDK'sını yüklemeniz ve SDK
içerisindeki fxc aracını kullanabiliyor olmanız şart. Kabaca
aşağıdaki komutlar ile yazmış olduğunuz bir PixelShader FX dosyasını
PS dosyasına compile edebilirsiniz.

fxc /T ps_2_0 /Fo DaronEfekt.ps DaronEfekt.fx

Eğer bilgisayarınıza DirectX SDK'sını yüklemek istemiyorsanız aşağıdaki
adresteki Silverlight uygulamasını kullanarak PS dosyaları
hazırlayabilirsiniz. Sayfa açıldığında karşınıza çıkan ilk kutucuğa
almak istediğiniz PS dosyasının adını, ikinci kutucuğa ise HLSL kodunu
koymanız yeterli olacaktır. Sistem otomatik olarak PS dosyasını compile
edip download linki sunacaktır.

http://www.voxpeeps.com/slpixelshadercompiler/

Pixel Shader efekti nasıl yazılır?

Bu makalenin amacı Pixel Shader nasıl yazılır sorusuna cevap vermek
olmadığı gibi ben de kişisel anlamda bir Pixel Shader uzmanı sayılmam. O
nedenle olabildiğince basit bir şekillde kabaca konunun üzerinden
geçeceğiz.

[HLSL]

sampler2D input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR

{

    float4 Color;

    Color = tex2D( input , uv.xy);

    Color.r=1;

    return Color;

}

Yukarıda gördüğünüz Pixel Shader efektinin yaptığı tek şey kendisine
verilen piksellerdeki kırmızı renk değerini maksimuma çekmek. input
parametresi üzerinden piksel bilgisi, yani bir anlamda boyama fırçası
gelirken uv parametresi ise offset koordinatları içeriyor. X ve Y
koordinatı olarak gelen bu bilgiler 0 ile 1 arasında oluyor. Yani özünde
tüm koordinatlar 0,0 ile 1,1 arasında diyebiliriz. Tüm bu bilgiler
üzerinden fırçamızdaki uygun koordinatlardaki rengi almak için ise
text2D metodunu kullanabiliyoruz. Elimize geçen renk değişkeni ARGB
değerleri olan klasik bir Color nesnesi olarak geliyor. Böylece biz hem
renk hem de koordinatlar üzerinden rahatlıkla oynama yapabiliyoruz.

Yukarıdaki efekt kendi içinde bir değer alarak kırmızı rengini 1 olarak
eşitliyor. Oysa biz bu değerin yeri geldiğinde bu efekte parametrik
olarak gönderilmesini de isteyebiliriz. Böyle durumlarda PixelShader
kodumuzda söz konusu parametreyi de tanımlamamız şart.

[HLSL]

sampler2D input : register(s0);

int kirmizilik : register(c0);

 

float4 main(float2 uv : TEXCOORD) : COLOR

{

    float4 Color;

    Color = tex2D( input , uv.xy);

 

    Color.r=kirmizilik;

    return Color;

}

Değişkenimizi tanımlarken register ettiğimiz c0 noktasına dikkat. Bu
nokta üzerinden söz konusu parametreye Silverlight tarafından da
ulaşabileceğiz. Eğer farklı parametreler tanımlayaracak c1, c2 şeklinde
devam edebilirsiniz. Gelin daha detalara girip makalemizi bir HLSL
makalesine çevirmeden PixelShader efektimizin Silverlight tarafındaki
kullanım şekline göz atalım.

PS dosyamızı Silverlight ile nasıl kullanırız?

PS dosyasını ilk olarak Silverlight projenize sağ tıklayarak gelen
menüden "Add / Existing Item" diyerek eklemeniz gerekiyor. Sonrasında
söz konusu PS dosyasını Solution Explorer içerisinde seçili tutup Visual
Studio'nun Properties paneline geçerseniz Build Action adında bir
özellik göreceksiniz. Söz konusu özelliğe "Content" değerini vererek PS
dosyasının XAP içerisine alınmasını sağlayabilirsiniz. Eğer bu şekilde
bir ayarlama yapmazsanız PS dosyası projede kalır fakat XAP dosyasına
eklenmez ve uygulamanız da söz konusu Shader efektini kullanamaz.

[VB]

Public Class DaronEfekti

    Inherits Effects.ShaderEffect

 

    Public Sub New()

        Dim YeniEfekt As New
Effects.PixelShader

        YeniEfekt.UriSource = New
Uri("DaronEfekt.ps",
UriKind.Relative)

        Me.PixelShader = YeniEfekt

    End Sub

 

End Class

[C#]

    public class DaronEfekti :
System.Windows.Media.Effects.ShaderEffect

    {

        public DaronEfekti()

        {

            System.Windows.Media.Effects.PixelShader YeniEfekt = new System.Windows.Media.Effects.PixelShader();

            YeniEfekt.UriSource = new
Uri("DaronEfekt.ps", UriKind.Relative);

            this.PixelShader =
YeniEfekt;

        }

    }

Yukarıdaki kodlar içerisinde de görebileceğiniz üzere kendi Shader
efektimizi yaratırken Silverlight ile beraber gelen ShaderEffect
sınıfını inherit ediyoruz. Bu sınıfın kendi içerisinde zaten mevcut bir
PixelShader Property'si var. Söz konusu Property'ye biz de kendi
PixelShader efektimizi yaratarak veriyoruz. Kendi PixelShader
efektimizi yaratırken de UriSource olarak Silverlight projemize
eklediğimiz PS dosyasının relatif yolunu vermeyi unutmuyoruz. Aslında
basit bir şekilde Silvelright için PixelShader efektimizi yaratma
işlemini bitirdik fakat unutmayın ki bizim efektin bir de ekstra
parametresi vardı. İşte bu parametre için Silverlight tarafında da bir
Dependancy Property tanımlamamız gerekiyor. Özellikle Dependancy
Property yaratmamızın nedeni aslında bu Property üzerinden gerektiğinde
StoryBoard'ların da animasyon yapabilmesini sağlamak.

[VB]

    Public Property Kirmizilik() As Double

        Get

            Return Me.GetValue(KirmiziProperty)

        End Get

        Set(ByVal value As Double)

            Me.SetValue(KirmiziProperty, value)

        End Set

    End Property

 

    Public Shared ReadOnly KirmiziProperty As DependencyProperty = _

    DependencyProperty.Register("Kirmizilik", GetType(Double), _

                                GetType(DaronEfekti), _

                               
New
PropertyMetadata(0.0,
PixelShaderConstantCallback(0)))

[C#]

        public double Kirmizilik

        {

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

            set {
SetValue(KirmiziProperty, value); }

        }

 

        public static readonly DependencyProperty KirmiziProperty =

            DependencyProperty.Register("Kirmizilik", typeof(double), typeof(DaronEfekti),
new
PropertyMetadata(0.0,
PixelShaderConstantCallback(0)
));

Yukarıdaki kodların normal bir Dependancy Property'den tek farkı
Metadata kısmı! İşte tam da bu noktada bir
PixelShaderConstantCallback görüyoruz. Söz konusu Callback nesnesine
parametre olarak PixelShader HLSL kodumuzu yazarken parametremize
verdiğimiz c0'ın sayısal kısmını veriyoruz :) Artık herşey hazır. Sıra
geldi bu efektimizi XAML içerisinde kullanmaya.

Efektimizi tepe tepe kullanalım :)

Efektimizi kullanabilmek için ilk aşamada XAML tarafında uygulamamızın
assemblysini bir XML namespace olarak tanımlıyoruz. Sonrasında söz
konusu namespace üzerinden rahatlıkla efektimizi herhangi bir nesneye
atayabiliyoruz. Tabi bu işlemleri C# veya VB kodu ile de yapabiliriz.

[XAML]

<UserControl

   x:Class="SilverlightApplication21.MainPage"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:daron="clr-namespace:SilverlightApplication21"

   Width="400"

   Height="300">

    <UserControl.Resources>

        <Storyboard

           x:Name="BlurOL">

            <DoubleAnimation

               Duration="00:00:01.000"

               From="0"

               To="1"

              
Storyboard.TargetName
="Renk"

              
Storyboard.TargetProperty
="(DaronEfekti.Kirmizilik)" />

        </Storyboard>

    </UserControl.Resources>

    <Grid

       x:Name="LayoutRoot"

       Background="White">

        <StackPanel>

            <Button
x
:Name="btnTikla"

               Content="TIKLA"

               Height="100"

               Width="100">

                <Button.Effect>

                    <daron:DaronEfekti
x
:Name="Renk"></daron:DaronEfekti>

                </Button.Effect>

            </Button>

            <Slider

               Minimum="0"

               Maximum="1"

               Value="{Binding
Kirmizilik
, ElementName=Renk,
Mode
=TwoWay}" />

        </StackPanel>

    </Grid>

</UserControl>

Yukarıdaki XAML kodunda da inceleyebileceğiniz üzere hazırladığımız
efektin özelliklerine bir Slider aracılığı ile erişebildiğimiz gibi
gerekirse doğrudan söz konusu efektin bir özelliğine animasyon da
verebiliyoruz. Örneğimizdeki animasyon Button kontrolünün
görselliğindeki tüm kırmızılık renk değerlerini en düşük seviyesinden en
yükseğe doğru anime edecektir.

Hazırı yok mu bunların?

Kendi Pixel Shader'larınızı yazmak her zaman doğru seçim olmayabilir :)
Bazen başkaları yazsa da biz hemen kullansak hissiyatı kapılmamak elde
değil. Esasen bu hissiyatın nedeni HLSL'in çoğumuza yabancı bir uzmanlık
alanı olması. Bu çerçevede tabi ki konunun uzmanları "Özgür Yazılım"
çerçevesinde :) codeplex'te açık kaynak kodları ile Silverlight için
birçok Pixel Shader kütüphanesini paylaşmış durumdalar. Zaten bu
kütüphanelerin neredeyse hepsi daha önce de WPF için downloada
sunulmuştur.

http://wpffx.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=25285

Yukarıdaki adresten Silverlight 3.0 ile uyumlu Pixel Shader
kütüphanelerini indirebilirsiniz.

Hepinize kolay gelsin.