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.
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.
İ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.
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