Makale Özeti

Bir önceki makalede directx ile bir oyun iskeleti oluşturup directx ile ilk programımızı yapmıştık. Bu makalede ise biraz daha derinlemesine DirectX yazılımı için gereken altyapıyı(Device,PresentParameters,Buffer) inceleyip, Vertex mimarisini üzerine bir yazılım gerçekleştireceğiz..

Makale

DirectX Programlama Alt Yapısı

Bir önceki makalede directx ile bir oyun iskeleti oluşturup directx ile ilk programımızı yapmıştık. Bu makalede ise biraz daha derinlemesine DirectX yazılımı için gereken altyapıyı(Device,PresentParameters,Buffer) inceleyip, Vertex mimarisini üzerine bir yazılım gerçekleştireceğiz..

DirectX device'ı oluştururken kullandığımız PresentParamaters sınıfı ile oluşturduğumuz device'ın nasıl çalışacağını, hangi altyapıyı kullanacağını tanımlarız. Direct3D.PresentParameters özelliklerinden bahsetmek gerekirse.

    Windowed: Yaptığımız programın pencereli yoksa penceresiz mi olduğunu belirtir.

    SwapEffect: Buffer işlemlerinin nasıl yapılacağına karar verir. Bufferlama tekniklerini ayrıntılı olarak inceleyeceğiz.

    BackBufferCount Bufferlama sayısını turar.

    BackBufferFormat Bufferlama format bilgisini tutar.

    BackBufferHeight Pencerenizin yükseklik bilgisini tutar.

    BackBufferWidth Pencerenizin genişlik bilgisini tutar.

Bir önceki makalede device nesnemizi nasıl oluşturduğumuz aşağıda gösterilmektedir.

    PresentParameters presentParams = new PresentParameters();

    presentParams.Windowed = true;

    presentParams.SwapEffect = SwapEffect.Copy;

    device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

Eski yapılan oyunlarda buffer kullanılmazdı fakat bilgisayar donanımları geliştikçe artık buffer mantığı kullanılmaya başlandı. Buffer mantığında görüntünün bir sonraki harekete geçiş sırasında tüm companentlerin silinip yeniden oluşturulması sırasında ekranda oluşan görüntü kırpılmasından kurtulmak istenmektedir. Bunun için tüm componentler silinirken arkadaki buffer görüntülenir yeni ekran bu ekranın üzerine gelir. Eski oyunlarda iki görüntü arasında tüm componentlerin silinip tekrar oluşturulmasında ekran boş olarak görülebilmekteydi. Bu da ekranın kırpılmasına yol açardı. Buffer kullanmak bu kırpılmanın önlenmesini sağlar ve daha kaliteli görüntü elde etmemizi sağlar fakat buffer kullanımı bilgisayar kaynaklarını çok yoğun olarak kullanmaktadır.Düşünün bir kere ekrana iki görüntü arası geçişinde ekrana başka bir görüntü(belkide birden fazla) gelecektir. Bufferın kullanılma amacından bir diğeri ise buffer sayısını artırarak triple-buffering yada quad buffering yaparak zaman kazanmaktır. Buffer sayısını artırmak kullanıcıların bu bufferlar ekranında görürken sizin kullanabileceğiniz zaman artmaktadır. Kaba bir hesapla triple-buffer ile 60hz bir ekranda fazladan 1.6 salise kazandirir, bu da 2ghz bir işlemcide yaklaşik 32 milyon işleme denk gelir. Bufferlama işleminin nasıl yapılması gerekiyorsa device nesnesi yaratırken PresentParameters sınıfının Swap Effect(Buffer işlemlerinin nasıl yapılacağına karar verir) özelliği ile yönetiriz.Direct3D.SwapEffect tekniklerinden bahsetmek gerekirse;

Copy: Önyüzdeki tüm objelerin buffer'a alınıp buffer'ın oluşturulması şeklidir. Tüm objelerin kopyalanıp yeniden oluşturulması performans olarak çok yoğun bir işlemdir. Bunun için büyük projelerde kullanılmaz. Fakat yazılımcı için ve ekran kartı performansı için büyük kolaylık getirir. Performansı nasıl etkilediğini anlamak için;  ekran büyüklüğünden de bahsetmek gerekmedir 32 bit bir 1200*1600 çözünürlükteki bir oyun için bir ekranın boyutu yaklaşık 7.32 megabytes'dır. Her bir görüntü geçisinde bunu kopyalama genel olarak performans olarak çok yorucudur.

Flip:Çoklu bufferlama yapılarak çalışır. Eğer program tam ekran çalışıyorsa gerçek memory kullanılmaz fakat pencereli bir yapıda  gerçek memory kullanılır.Pencereli bir yapıda görüntülerinizi memory'de saklar ve memory kopyalama ile buffering yapar. Copy ile Flip arasındaki en büyük fark copy de tüm objelerin yeniden yaratılması flip'de ise tüm objelerin kopyalanmasıdır(referencing).

Discarding:Ekran kartı üzerinde bufferlama yapılır. Çok karmasık bir çevirme algoritması vardır (Neat Algorithm). Tüm işlemler ekran kartı üzerinde yapılır. Profesyonel yazılımlar tarafından kullanılır.Copy ve Flip de back buffer kullanılması kesinlik kazanmaktadır fakat dicard da bu yetki ekran kartına verilmiştir. Ekran kartı gereken yerlerde back buffer kullanıp gereken yerlerde kullanmama hakkına sahiptir.

Device nesnesi oluşturmak. Device nesnesi oluşturmak için 5 parametreli kurucu fonksiyonu çağırmak gerekmektedir. Bu parametreler.

int adapter: Kullanmak istediğiniz adapterin Id numarasıdır. Bilgisayarınızda bulunan ekran kartları birtane ID numarası ile temsil edilirler. Birçok bilgisayar bir tane ekran kartına sahiptir. Ekran kartlarının ID numaraları 0'dan başlar. Yukarıda oluşturduğumuz device nesnesinde ekran kart sayımız 1 olduğu için adapter numarası 0 olarak verilmiştir.

DeviceType Nasıl bir device oluşturacağınıza karar verirsiniz. Üç çeşit tanımlama yapabilirsiniz bunlar; Direct3D.DeviceType.Software, Direct3D.DeviceType.Hardware ve Direct3D.DeviceType.Reference'dır. Software diyerek CPU üzerinde çalışan sanal bir ekran kartı varmış gibi nesnenizi yaratırsınız. Hardware diyerek bilgisayarın ekran kartı üzerinde çalışan bir nesne tanımlamış olursunuz. Reference diyerek ise gerçek hayatta kullanılmayacak sadece debugging işlemini yapmak için bir nesne tanımlamış olursunuz.

Control renderWindow Kullanılacak pencere nesnesini alır.

CreateFlags behaviorFlags Device nesnesinin davranışını belirler.Bu davranışlar HardwareVertexProcessing, SoftwareVertexProcessing ve MixedVertexProcessing'dır. HardwareVertexProcessing ile ekran kartı üzerinde nesnenizin davranışını, SoftwareVertexProcessing  CPU üzerinde nesnenizin davranışını,MixedVertexProcessing ile CPU ve ekran kartı kullanılarak nesne davranışını sağlamış olursunuz.

PresentParameters: Makalenin ilk başında oluşturduğumuz PresentParameters nesnesini alır. Device'ın nasıl bufferlama yapacağını vb. bilgileri verir.

Device nesnesini tanımladıktan sonra Vertex yapısını inceleyebiliriz. Vertex üç boyutlu uzayda bir noktayı temsil eder. Vertex kullanarak geometrik işlemler vasıtası ile nesnelerimizi tanımlarız. Biz vertex kullanarak ekranımıza rengarenk bir üçgen çizeceğiz. Üçgen için 3 nokta(3 vertex) tanımlamamız gerekmektedir. Bu vertex'leri üç boyutlu bir dizi içerisinde tanımlamak için global bir değişken tanımlarız.  CustomVertex.TransformedColored []vertices=new CustomVertex.TransformedColored[3]; Tanımladığımız bu noktaların ekran üzerinde koordinatlarını(x,y) ve renklerini vermemiz gerekmektedir. Noktalarımızın Position  özelliği ile ekran üzerinde hangi noktada olacağını tanımlarız. Position özelliği Vektor4 nesnesi almaktadır. Vektor4 nesnesi noktanın X,Y,Z ve W değerlerini alır ya Position.X ,Position.Y, Position.Z, Position.W olarak da değerler verilebilir.Color özelliği ile de noktanın rengini verebilmekteyiz. Üçgenimize rengini verirken device nesnesinin VertexFormat özelliğini TransformedColored vererek ücgenin renklenmesini sağlarız.

 

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Drawing;

using System.Windows.Forms;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

namespace gamer

{

public class MainClass : Form

{

    Device device = null;

    CustomVertex.TransformedColored []vertices=new CustomVertex.TransformedColored[3];

    public MainClass()

    {

        this.ClientSize = new System.Drawing.Size(640, 480);

        this.Text = "Mehmet Ali ECER";

    }

 

    public bool InitializeGraphics()

    {

        try {

                PresentParameters presentParams = new PresentParameters();

                presentParams.Windowed = true;

                presentParams.SwapEffect = SwapEffect.Copy;

                device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

                device.DeviceLost += new EventHandler(this.InvalidateDeviceObjects);

                device.DeviceReset += new EventHandler(this.RestoreDeviceObjects);

                device.Disposing += new EventHandler(this.DeleteDeviceObjects);

                device.DeviceResizing += new CancelEventHandler(this.EnvironmentResizing);

                vertices[0].Position = new Vector4(150, 100, 0, 1);

                vertices[0].Color = Color.Red.ToArgb();

                vertices[1].Position = new Vector4(this.Width/2 + 150,100,0,1);

                vertices[1].Color = Color.Green.ToArgb();

                vertices[2].Position = new Vector4(260, 300, 0, 1);

                vertices[2].Color = Color.Yellow.ToArgb();

                return true;

        } catch (DirectXException) {

                return false;

        }

    }

protected virtual void InvalidateDeviceObjects(object sender, EventArgs e)

{         MessageBox.Show("invalidate device");}

protected virtual void RestoreDeviceObjects(object sender, EventArgs e)

{         MessageBox.Show("device reset");}

protected virtual void DeleteDeviceObjects(object sender, EventArgs e)

{         MessageBox.Show("device delete");}

protected virtual void EnvironmentResizing(object sender, CancelEventArgs e)

{     MessageBox.Show("hareket");}   

protected virtual void FrameMove(){}

protected virtual void Render()

{

        if (device == null)

                return;

        device.Clear(ClearFlags.Target, System.Drawing.Color.Blue, 1.0f, 0);

        device.BeginScene();

        device.VertexFormat = CustomVertex.TransformedColored.Format;

        device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, vertices);

        device.EndScene() ;

        device.Present();

}

public void Run()

{

while (Created) {

    FrameMove();

    Render();

    Application.DoEvents();

}

}

protected override void OnPaint(PaintEventArgs e)

{

    this.Render();

}

protected override void OnKeyPress(KeyPressEventArgs e)

{

    base.OnKeyPress(e);

    if ((int)e.KeyChar == (int)System.Windows.Forms.Keys.Escape) {

    this.Close();

}

}

static void Main()

{

    using (MainClass mainClass = new MainClass()) {

        if (!mainClass.InitializeGraphics()) {

            MessageBox.Show("Hata");

return;

}

    mainClass.ShowIcon=true;

    mainClass.Icon=new System.Drawing.Icon("gears.ico");

    mainClass.Show();

mainClass.Run();

}}}}

Makalede ya da kodlarda bulunan hatalar ya da sorularınız için mehmetaliecer@gmail.com adresinden bana ulaşabilirsiniz. İyi çalışmalar.

Mehmet Ali ECER

www.mehmetaliecer.com