OpenGL ile Vertex Array Kullanımı

OpenGL ile Vertex Array Kullanımı

Bu yazıda, OpenGL ile vertex dizileri (array) kullanarak, çizim işlemlerinizi nasıl hızlandırabileceğinizi anlatmaya çalışacağım.

Neden?

OpenGL ile poligonlardan oluşan 3B nesneleri çizmede üç yöntem kullanılabilir.

  • glBegin/End

En klasik ve basit yöntem, vertex (nokta), doku ve normal koordinatlarınızı glBegin ve glEnd komutları arasında glVertex*, glNormal* ve glTexCoord* fonksiyonları ile ekran kartına yollarsınız. OpenGL öğrenmeye başladığımda, ustamın bana ilk söylediği bu yöntemin yasak olduğuydu (şanslıydım, erken öğrendim). Bu yöntemin tek avantajı, basit olması. Ancak ciddi bir iş yapmaya çalışıyorsanız (oyun gibi), bu yöntemi artık unutmalısınız, çünkü çok yavaaşşşş. Eger 3B nesneleriniz çok poligondan oluşmuyor ise (vertex sayısı < 200) performans olarak kullanmanızda bir sakınca yok. Ancak yüzlerce vertexi teker teker ekran kartına yollamaya çalışmak yanlış bir yöntem. Bir nevi ekran kartına hakaret. Günümüz ekran kartları, yüksek sayıda poligonu bir kerede almaya daha uygunlar. Teker teker yolladığınızda gereksiz yere hem kartı, hem işlemciyi, hem de aradaki BUS'ı meşgul etmiş, oyalamış olursunuz.

  • Display Lists

İlkine göre hız olarak çok daha avantajlı olan bu yöntemin tek bir dezavantajı var. Display List içinde kullandığımız noktalara ve işlemlere, oluşturduktan sonra müdahale etme şansımız malesef yok. Konuyla ilgili bir döküman için [ buraya ] bakabilirsiniz.

  • Vertex Arrays

Bu yöntem ile Display List'ler ile gelen hızı, hiçbir dezavantaj olmadan yakalayabiliriz. Yani, program çalışırken noktalarımızı, dokularımızı değiştirebilir, glBegin/End ile gelen performans problemine de takılmayız. Bu yöntem ile, noktalarımız, ekran kartına tek bir fonksiyon çağırımı ile yollanır.

Nasıl?

Gelelim bu yöntemin kullanımına. Yöntemin kullanımı vertex bilginizi programınızda nasıl sakladığınıza göre değişebilir. Burada interleaved vertex array kullanımına örnek vereceğim, sonra da diğer alternatif yöntemlere kısaca değineceğim.

Nesnelerinizin veri yapısını nasıl tuttuğunuza göre çok değişebilecek bir kullanımı var vertex array'lerin. Şöyle bir vertex veri tipi düşünelim.

class Vertex3D

{

public:

        float s;        //!< s doku koordinatı

        float t;        //!< t doku koordinatı

        

        float nx;       //!< vertex normalinin x değeri

        float ny;       //!< vertex normalinin y değeri

        float nz;       //!< vertex normalinin z değeri



        float x;        //!< x koordinatımız

        float y;        //!< y koordinatımız 

        float z;        //!< z koordinatımız

};

Yani her nokta için doku koordinatlarını, normali ve noktanın yerini tutuyorum. Şimdilik diyorum çünkü ileride birden fazla doku kullanımı (multitexturing), renk, ışık kullanımına göre veri tipiniz değişebilir.

Yani hafızada, ardarda, doku, normal ve nokta koordinatları şeklinde çok sayıda nokta bulunmakta. Bunun adı; interleaved array... Yine bir şekilde, günümüz ekran kartlarının tercih ettiği bir veri yapısı bu da. Neyse, fazla karıştırmayalım ortalığı.

Noktalarımı da nesnenin içinde indeksli (indexed) olarak tutuyorum. Yani nesnem 1000 adet noktadan oluşuyorsa, 1000 adet Vertex3D tutan bir pointer'ım, ve üçgenlerin nasıl oluşturduğunu belirten de bir unsigned short pointer'ım var. Indeksler sırayla hangi noktaların (kaçıncı) bir üçgeni oluşturduğunu belirtiyor. Örneğin indeks pointerının gösterdiği bölgedeki ilk üç değer 8, 25, 698 olsun. Bu, 3B nesnemizi oluşturacak ilk üçgenin, noktalarımızdan 8. 25. ve 698. kullanılarak oluşturulacağını belirtiyor. İndeksli kullanım başta kafa karıştırıcı gözükse de, ilerledikçe ihtiyaç duyacağınız ve kullanacağınız bir yöntem.

Kabaca şöyle bir Nesne (Mesh) sınıfımız olduğunu düşünelim;

class Nesne

{

public:

	//! Kaç tane indeksimiz var?

	int GetIndexCount(){return m_nIndices;};

	//! İndekslerimize olan pointerı alalım

	unsigned short* GetIndices(){return m_pIndices;};

	//! Kaç tane noktamız var?

	unsigned short GetVertexCount(){return m_nVertices;};

	//! Noktalara olan pointer'a buradan erişelim

	const Vertex3D* GetVertices(){return m_pVertices;};

protected:

	//! İndekslere bir pointer

	unsigned short* m_pIndices;

	//! İndeks sayısı

	unsigned short m_nIndices;

	//! Nokta sayısı

	unsigned short m_nVertices;

	//! Noktalarımız da burada işte

	Vertex3D* m_pVertices;	

};

Buraya kadar yazdıklarımı umarım takip edebiliyorsunuzdur, başlangıçta karışık gelebilir, anlayamadığınız bir nokta varsa tekrar okumayı deneyin.

Önce OpenGL sürücüsüne hangi arrayleri yollayacağımızı söyleyelim.

	// vertex array kullanımını açalım

	glEnableClientState( GL_VERTEX_ARRAY );

	// normalleri de göndereceğiz

	glEnableClientState( GL_NORMAL_ARRAY );

	// doku koordinatları da gidecek

	glEnableClientState( GL_TEXTURE_COORD_ARRAY );

Hazırlık aşaması tamam, şimdi karta yollayalım artık.

	// interleaved olarak yollayacağımız vertex adresini belirle

	glInterleavedArrays(GL_T2F_N3F_V3F, sizeof(XVertex3D), pMesh->GetVertices());

	// ve ekran kartına yolla indekslerle

	glDrawElements(GL_TRIANGLES, pMesh->GetIndexCount(), GL_UNSIGNED_SHORT, pMesh->GetIndices());

Hepsi bu kadar, yukarıdaki iki satır ile, tüm noktalarımızı (üçgen çizecek şekilde) karta yolladık. İlk satırda noktalarımızı, iki doku, üç normal ve üç nokta koordinatı alacak şekilde ayarladık ve noktalarımızı yolladık. İkinci satırda da indekslerimiz gittiler.

Bitirmeden, eski duruma dönmemiz gerekli.

	// vertex array kullanımını kapatalım

	glDisableClientState( GL_VERTEX_ARRAY );

	glDisableClientState( GL_NORMAL_ARRAY );

	glDisableClientState( GL_TEXTURE_COORD_ARRAY );

Alternatif olarak, nokta, normal ve doku bilgilerini ayrı ayrı array'lerde tutmayı seçebilirsiniz. Böylelikle, çoklu doku (multitexture) kullanımı, renk gibi özellikleri eklemek de daha kolay olabilir. Bu tür bir kullanımı seçecekseniz, her array'i ayrı ayrı OpenGL komutları ile belirlemeniz gerekir. Eğer takip ettiyseniz, bir önceki yazımda vertex array'leri bu şekilde kullanmıştım. [ Buradan ] o dökümana gidebilirsiniz.

Bitti

Sonuç olarak, bu yazıda, 3B nesnemizin noktalarını nasıl tutabileceğimize bir örnek vermiş ve bu yapıyı standart (v 1.1) OpenGL fonksiyonları kullanarak vertex array yöntemi ile nasıl hızlı ve verimli bir şekilde ekran kartına yollayabileceğimizi görmüş olduk. Bazı OpenGL eklentilerini (extension) kullanarak, daha iyi performans elde etmek de mümkün bu arada.

Bu yazı ile ilgili, hatalar, görüşleriniz, eleştirileriniz için mentat@cfxweb.net adresini kullanabilirsiniz. Ve tabi www.oyunyapimi.org forumlarını. Umarım yazdıklarım işinize yarar.

Not: Bir önceki yazımın (ve tahminen bunun) Türkçe karakter problemini düzeltmek için zaman harcayan deniz'e çok teşekkürler.

mentat :: 10.03.2003 :: www.oyunyapimi.org




Bu haberin geldigi yer: oyunyapimi.org
http://www.oyunyapimi.org

Bu haber icin adres:
http://www.oyunyapimi.org/modules.php?name=Sections&op=viewarticle&artid=29