|
Bu eğitselde vertex ve index buffer kavramlarını açıklayıp, kullanımlarını örnekleyeceğiz. Direct3d yüksek performanslı çizimler için oluşturulan vertexleri en uygun bellek bölgesine koymayı sağlayan yapılar barındırır. Vertex Buffer kullanarak tanımladığımız vertexler video memory'ye yada agp memory'ye yerleştirilir ve sistem belleğinden çok daha hızlı bir şekilde erişilebilirler. Bkz. Video/Agp Memory. İşlemci-Bellek arası erişim hızları kabaca şöyledir: GPU < = > Video Memory - Çok hızlı okuma yazma CPU < = > Video Memory - Yavaş okuma yazma GPU < = > Agp Memory - Hızlı okuma yazma CPU < = > Agp Memory - Hızlı yazma yavaş okuma Vertexlerin doğru bellek bölgesine yerleştirilmeleri dışında verimli kullanılmalarıda önemlidir. Örneğin bir kare çizmek için 6 vertex kullanılırken aslında karenin sadece 4 köşesi vardır. Daha kompleks cisimlerde bu fark devasa boyutlara ulaşabilir(küpte 8 köşe için 36 vertex). Bunu önlemek için kullandığımız yapıyada Index Buffer deniyor. Kullanmak için mutlak(unique) vertexleri tanımlayıp, doğru sırada indexlemek gerekmekte. Ayrıca index buffer kullanırken daha önce kullanılmış vertexler bir vertex-cache te tutulur ve tekrar kullanılacağında çok daha hızlı erişilebilinir. Bu detaylardan sonra kodu incelemeye başlayalım. Vertex olarak aynı tipte bir vertex kullanacağız. Burada sadece kolaylık olsun diye bir yapıcı metod ekledik: //3d pozisyon + renk vertex3d { vertex3d(){} //yapıcı metod vertex3d(float _x, float _y, float _z, DWORD _col) { x = _x; y = _y; z = _z; color = _col; } float x, y, z; DWORD color; }; #define position_fvf D3DFVF_XYZ | D3DFVF_DIFFUSE
Direct3d'de vertex buffer IDirect3DVertexBuffer9 sınıfı, index buffer IDirect3DIndexBuffer9 sınıfı ile tanımlanıyor. //tanımlar Aygit* aygit; IDirect3DVertexBuffer9* vbuffer1; //tek başına mavi çizecek IDirect3DVertexBuffer9* vbuffer2; //index ile yeşil çizecek IDirect3DIndexBuffer9* ibuffer; //yeşil çizecek const float PI = 3.1416f; int render_type = 1; //1 normal,2 vertex buffer,3 vertex+index buffer vertex3d kirmizi_noktalar[6]; //normal vertex3d* mavi_noktalar; //vertex buf. vertex3d* yesil_noktalar; //vertex + index buf.
Bir karşılaştırma yapmak için 3 farklı yöntemde 1600'er adet üçgen çizeceğiz. Sistem belleğinden okuyarak kırmızı , sadece vertex buffer kullanarak mavi, vertex + index buffer kullanarak yeşil renkte çizim yapılacak. void ilklemeler() { ....... //sistem belleğinden çizilecek kırmızı üçgenler kirmizi_noktalar[0] = vertex3d(0.0, 1.0, 0.0, D3DCOLOR_XRGB(255,0,0)); kirmizi_noktalar[1] = vertex3d(1.0, 1.0, 0.0, D3DCOLOR_XRGB(255,0,0)); kirmizi_noktalar[2] = vertex3d(0.0, 0.0, 0.0, D3DCOLOR_XRGB(255,0,0)); kirmizi_noktalar[3] = vertex3d(0.0, 0.0, 0.0, D3DCOLOR_XRGB(255,0,0)); kirmizi_noktalar[4] = vertex3d(1.0, 1.0, 0.0, D3DCOLOR_XRGB(255,0,0)); kirmizi_noktalar[5] = vertex3d(1.0, 0.0, 0.0, D3DCOLOR_XRGB(255,0,0)); .......
İlklemeler daha önceki gibi transformasyonlar ve ışığın iptali ile başlıyor. İlk olarak kırmızı noktaları ilkliyoruz. Verilen noktaları kağıt üzerinde çizerseniz bir kare(quad) oluştuğunu görebilirsiniz. Şimdi ilk vertex buffer 'ı oluşturalım. //vertex buffer1'i oluştur aygit->getDevice()->CreateVertexBuffer(6*sizeof(vertex3d), //byte uzunluğu D3DUSAGE_WRITEONLY, //kullanım yöntemi position_fvf, //FVF D3DPOOL_MANAGED, //bellek kullanımı &vbuffer1, //oluştur NULL); //kullanılmıyor
Device'ın CreateVertexBuffer metodu ile buffer'ı oluşturduk. 1. parametre ayrılacak bellek bölgesinin byte uzunluğu. 2. parametre çeşitli kullanım ayarı sabitlerinin kombinasyonundan oluşuyor. Bazıları şöyle: D3DUSAGE_DYNAMIC : Yüksek ihtimalle agp memory'de oluşturulur. Vertex buffer'ın içeriğinin sıklıkla değişmesi muhtemel ise kullanılır.(Örn. Partikül Sistemi) D3DUSAGE_POINTS : Nokta çizimleri yada "point sprites" çizimleri için kullanılır. D3DUSAGE_WRITEONLY : Buffer'a sadece yazma işlemi yapacağımızı belirtiriz ve böylece yazma işlemine en uygun bellek bölgesi seçilir. Bu durumda okuma yapmaya kalkma hata oluşturur. 3. parametre vertex tipi 4. parametre bellek kullanımının nasıl yönetileceğine dair bilgi verir. Şu değerler olabilir: D3DPOOL_DEFAULT : Bu ayarda yaratılan vertexler sistem yada agp belleğinde bulunur. D3DUSAGE_DYNAMIC kullanılacak bufferlar bu ayarı kullanmalıdır. D3DPOOL_MANAGED : Bu flag ile oluşturulan bufferın bellek yönetimini aygıt halleder. Bilgilerin bir kopyasını sistem belleğinde tutar ve gerekli oldukça video belleğine kopyalar. Aygıt "lost" konumuna düştüğünde bu şekilde yaratılan bufferlar kendi kendini tekrar yaratabilir. D3DPOOL_SYSTEMMEM : Sistem belleği kullanılır. 5. parametre oluşturulacak vertex buffer'dır. Vertex buffer'ı oluşturduk artık içini doldurma zamanı: //vertex buffer1'i doldur - 6 vertex //mavi üçgenler vbuffer1->Lock(0, 0, (void**)&mavi_noktalar, 0); mavi_noktalar[0] = vertex3d(0.0, 1.0, 0.0, D3DCOLOR_XRGB(0,0,255)); mavi_noktalar[1] = vertex3d(1.0, 1.0, 0.0, D3DCOLOR_XRGB(0,0,255)); mavi_noktalar[2] = vertex3d(0.0, 0.0, 0.0, D3DCOLOR_XRGB(0,0,255)); mavi_noktalar[3] = vertex3d(0.0, 0.0, 0.0, D3DCOLOR_XRGB(0,0,255)); mavi_noktalar[4] = vertex3d(1.0, 1.0, 0.0, D3DCOLOR_XRGB(0,0,255)); mavi_noktalar[5] = vertex3d(1.0, 0.0, 0.0, D3DCOLOR_XRGB(0,0,255)); vbuffer1->Unlock();
Vertex bufferın belleğine erişmek için "lock" etmek ve işimiz bitince "unlock" etmek zorundayız. Lock metodunun ilk parametresi başlangıçtan itibaren kaç byte'lık bir offset ile kilitleme yapılacağıdır. Mesela 64 değeri verildiğinde ilk 64 byte'a erişemeyiz. İkinci parametre kaç byte'lık bir kilitleme yapılacağı. 0 vererek tümünü kilitledik. 3. parametre bağdaştırılacak veri yapısı(void**). Son parametre kilitlemenin nasıl yapılacağını belirtir: Bazıları şunlardır : D3DLOCK_DISCARD : Dinamik bufferlar için kullanılır. Buffer'da bulunana datayı siler ve yeni bir gösterici sunar. D3DLOCK_NOOVERWRITE : Dinamik bufferlar için kullanılır. Mevcut buffer'a ekleme yapılırken kullanımı faydalıdır. Ekleme yapılırken çizim işlemine devam edilebiliir. D3DLOCK_READONLY : Sadece okuma yapmak istediğinizi bildirirsiniz. Write-Only bufferlar ile kullanılmaz. Unlock metodu ile işlemi bitiriyoruz. //vertex buffer2'yi oluştur aygit->getDevice()->CreateVertexBuffer(4*sizeof(vertex3d), //byte uzunluğu D3DUSAGE_WRITEONLY, //kullanım yöntemi position_fvf, //FVF D3DPOOL_MANAGED, //bellek kullanımı &vbuffer2, //oluştur NULL); //kullanılmıyor //index buffer oluştur aygit->getDevice()->CreateIndexBuffer(6*sizeof(WORD), //uzunluk-word 16 bit D3DUSAGE_WRITEONLY, //kullanım yöntemi D3DFMT_INDEX16, //16 bitlik(0-65536) D3DPOOL_MANAGED, //bellek kullanımı &ibuffer, //oluştır NULL); //kullanılmıyor 2. vertex buffer'ı benzer şekilde yarattık. Dikkat ettiyseniz burada 6 yerine 4 vertex yeri ayırdık. Index buffer'ı kullanarak hangi vertexler ile üçgen oluşturulacağını aygıta bildireceğiz. CreateIndexBuffer methodu ile index buffer'ı oluşturduk. İlk parametre byte olarak indexlerin kaplayacağı yer. 16 bitlikindexleme için 6 adet WORD(unsigned short) kullanılacak yani 0-2^16(65536) arası değerler kullanılabilir. 3. parametrede de indexlemenin tipini belirttik(16 yada 32 bitlik indexleme). Şimdi 2. vertex buffer ve index buffer'ı nasıl doldurduğumuza bakalım: //vertex buffer2'yi doldur - 4 vertex //yeşil üçgenler vbuffer2->Lock(0, 0, (void**)&yesil_noktalar, 0); yesil_noktalar[0] = vertex3d(0.0, 0.0, 0.0, D3DCOLOR_XRGB(0,255,0)); yesil_noktalar[1] = vertex3d(0.0, 1.0, 0.0, D3DCOLOR_XRGB(0,255,0)); yesil_noktalar[2] = vertex3d(1.0, 1.0, 0.0, D3DCOLOR_XRGB(0,255,0)); yesil_noktalar[3] = vertex3d(1.0, 0.0, 0.0, D3DCOLOR_XRGB(0,255,0)); vbuffer2->Unlock(); //index buffer'ı doldur - yukarıdaki ile aynı sırada WORD* indices = 0; ibuffer->Lock(0, 0, (void**)&indices, 0); //üçgen1 indices[0] = 1; indices[1] = 2; indices[2] = 0; //üçgen2 indices[3] = 0; indices[4] = 2; indices[5] = 3; ibuffer->Unlock();
2. vertex buffer'a sadece karenin 4 köşesinin koordinatlarını ekledik. Hangi köşeleri kullanarak üçgen çizileceğini index buffer'a ekliyoruz. Index buffer'da aynı şekilde aynı parametrelerle kilitleniyor(Lock). 1. üçgen için 1.,2.,0. ve 2. üçgen için 0.,2.,3. vertexler kullanılacak. Bunu 1. vertex buffer'a eklediğimiz 6 vertex ile karşılaştırırsanız, aynı üçgenleri çizdiğimi görürsünüz. Index buffer sadece vertex buffer'a referanslar içeriyor. Yani index buffer tek başına bir işe yaramaz. Artık çizim işlemlerine geçebiliriz. Numpad1-2-3 yada 1-2-3 tuşlarına basarak 3 yöntem arasında geçiş yapabilirsiniz. //doğrudan sistem belleğinden 1600 üçgen çizimi (kırmızı) if (render_type == 1) { for (int i=-20;i<20;i++) { for (int j=-20;j<20;j++) { //world transform D3DXMATRIX M; D3DXMatrixTranslation(&M,i*2.0f,j*2.0f,0.0); aygit->getDevice()->SetTransform(D3DTS_WORLD, &M); aygit->getDevice()->DrawPrimitiveUP(D3DPT_TRIANGLELIST, //türü 2, //sayisi kirmizi_noktalar, //vertex bilgisi sizeof(vertex3d)); //vertex boyutu } } }
Burada daha önceleri yaptığımız gibi üçgenin konumu belirleyip DrawPrimitiveUP ile bellekten çizim yapıyoruz. //vertex buffer ile 1600 üçgen çizimi (mavi) else if (render_type == 2) { for (int i=-20;i<20;i++) { for (int j=-20;j<20;j++) { //world transform D3DXMATRIX M; D3DXMatrixTranslation(&M,i*2.0f,j*2.0f,0.0); aygit->getDevice()->SetTransform(D3DTS_WORLD, &M); aygit->getDevice()->SetStreamSource(0, //akış(stream) no vbuffer1, //data 0, //offset(bytes) sizeof(vertex3d)); //vertex boyutu(byte) aygit->getDevice()->DrawPrimitive(D3DPT_TRIANGLELIST, //türü 0, //başlangıç 2); //sayısı } } } Vertex buffer ile çizim yapmak için vertexlerin konumunu aygıta bildirmemiz gerekmekte. Bunu SetStreamSource metodu ile yapıyoruz. Bunu her bir vertex buffer için bir kere çağırmamız yeterli ama burada göstermek için böyle yazdık. Verilen ilk parametre hangi akışı kullanacağımızı belirtiyor. Shader'larla uğraşana kadar bu değere hep 0 vereceğiz. 2. parametre vertex buffer. 3. parametre vertex bufferın başından itibaren geçerli olan bir offset değeri. Yani vertex bufferın tamamını çizmek zorunda değilsiniz. Bu offset değerini belirlemek için bunu donanımınızın destekliyor olması lazım(D3DDEVCAPS2_STREAMOFFSET). Son parametre kendini açıklıyor. Artık DrawPrimitive ile çizim yaptırabiliriz. Parametreler çok açık. //vertex+index buffer ile 1600 üçgen çizimi (yeşil) else if (render_type == 3) { for (int i=-20;i<20;i++) { for (int j=-20;j<20;j++) { //world transform D3DXMATRIX M; D3DXMatrixTranslation(&M,i*2.0f,j*2.0f,0.0); aygit->getDevice()->SetTransform(D3DTS_WORLD, &M); aygit->getDevice()->SetStreamSource(0, //akış(stream) no vbuffer2, //data 0, //offset(bytes) sizeof(vertex3d)); //vertex boyutu(byte) aygit->getDevice()->SetIndices(ibuffer);
aygit->getDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, //türü 0, //offset değeri 0, //min index 4, //vertex sayısı 0, //başlangıç indexi 2); //sayısı } } } Son olarak index buffer kullanımından bahsedelim. Farklı olarak önce aygıta indexleri SetIndices metodu ile bildiriyoruz. Aynı şekilde bu metoduda her seferinde çağırmamıza gerek yok. Indexlenmiş şekilleri çizmek için DrawIndexedPrimitive metodu kullanılıyor. 2. parametre bir offset değeri. Eğer birden fazla geometrinin indexlerini tek bir buffer'da toplarsak bu değer yardımı ile farklı şekilleri çizdirebiliriz. Mesela aynı index buffer hem bir küp'ün, hem bir küre'nin hemde bir karakter modelinin indexlerini tutuyor olabilir. 3. parametre raferans edilecek en küçük index değerini belirtir ve offset değerinin üzerine eklenir. Yani offset değeri 50 iken bu değer 1 ise minumum 51. index'i kullanabilirsiniz. Son olarak başlangıç indexi parametresi hangi index'ten başlanarak çizim yapılacağını belirtir. Mesela 3 değerini verirseniz 1. üçgen çizilmez. Programı çalıştırıp fps değerlerini karşılaştırınca beklenen durum 1<2<3 iken çıkan sonuç 1<3<2 ' dir. Çünkü burada index buffer kullanmanın bize getirisi götürüsünden fazladır. Ortak kullanılan vertexlerin sayısı arttıkça index buffer daha verimli hale gelecektir. Hangi durumlarda kullanmanız gerektiğini deneyerek tecrübe etmelisiniz. Kaynak Kodu İndir -------- EKSTRALAR -------- Ekran Kartı Bilgisi Kodu: http://directx.oyungelistirici.net/dx/onbilgi.rar Kavramlar: http://www.oyungelistirici.net/index.php/egitseller/14-oyunyapimi/233-kavramlar DirectX Kaynakları: http://www.chadvernon.com/blog/tutorials/ http://www.directxtutorial.com/ http://www.zanir.szm.sk/index.html http://www.pieterg.com/ http://www.32bits.co.uk/ http://triplebuffer.devmaster.net/tutorials/archive/ http://www.two-kings.de/ http://www.mvps.org/directx/indexes/index.htm http://www.drunkenhyena.com/cgi-bin/directx.pl DirectX Kitapları: http://www.amazon.com/ adresinde DirectX'i aratmanız yeterli. -------- ÖNEMLİ -------- -Bu sitedeki dökümanlar Türkçe kaynak eksikliği yüzünden hazırlanmış ve ihtiyaç duyanların serbest kullanımına sunulmuştur. -Sunulan kodlar ve bilgilerin doğruluk ve etkinlikleri konusunda bir garanti verilmemekle beraber her türlü talep ve düzeltmelerinizi mail yada ilgili forum başlığı yoluyla iletebilirsiniz -Dökümanlarda daha çok genel kavramlara değinilmiş olup detaylı bilgi edinmek için directx dökümanını ve çeşitli kaynakları (özellikle kitap) okumanız tavsiye edilir. Farklı kaynakların bağlantılarını bu dökümanın sonunda bulabilirsiniz. -Bu eğitsellerden verim alabilmek için orta düzeyde c++ bilgisi yeterlidir. Onun dışında winAPI hakkında bilgi sahibi olmak directx'i kavramakta size yardımcı olacaktır. Ayrıca eğitseller ardışık bağlantılı olduğu için bu işte yeni iseniz sırayla gitmeniz tavsiye edilir. -Örnek kodlar Virusl Studio 2003 projeleridir, vs2005 ile de kullanılabilir. -Kavramları Türkçeleştirmek için çok çaba sarfedilmemiştir, çoğu yerde İngilizce isimler kullanılmıştır. Merak ettikleriniz için kavram sözlüğüne bakabilirsiniz. -İndireceğiniz yazılımlardan dolayı PC'nizin ya da bünyenizin zarar görmesi durumunda sorumluluk kabul etmeyiz, geçmiş olsuna gelebiliriz :) -Yazılanların hakkı saklı değildir, sağda solda ben yaptım diye dağıtabilirsiniz :)
Yunus KARA
|