OpenGL’in çizim
fonksiyonlarını ve temel geometrik şekillerin çizimi için bu fonksiyonların
nasıl kullanılacağını ele almıştık. Temel geometrik şekillerin çizimi ile ilgili
yazımızda oluşturduğumuz şekiller iki boyutlu idi. OpenGL üç boyutlu çizimi
de destekleyen bir kütüphanedir. Bu yazımızda üç boyutlu çizimin nasıl yapılacağını
ele alacağız.
OpenGL’de iki boyutlu
çizim için çizimin başladığını ve sonlandığını belirten OpenGL kütüphanesi fonksiyonları
arasında, ne çizeceğimizi bir sembolik sabitler ile ifade ettikten sonra glVertex2f
fonksiyonunu kullanmıştık. Üç boyutlu çizim için de bu fonksiyonun glVertex3f
isimli versiyonunu kullanacağız. Bu fonksiyon, x ve y koordinat değerlerinin
yanısıra bir de z koordinat değerini parametre değişkeni olarak alır. Burada
z değeri, çizimin üçüncü boyutunu yani noktanın ekrandaki derinliğini belirler.
OpenGL ile üç boyutlu çizime başlamadan önce ekran ile ilgili birtakım ayarlamaların
yapılması gereklidir. Bu ayarlamalar genellikle çizimin daha iyi görünmesi için
düşünülmüştür. Bu ayarlamaların bir kısmı Derinlik Tamponu (Depth Buffer) ile
ilgilidir. Derinlik Tamponu, ekran üzerindeki tabakalar (katmanlar - layers)
olarak düşünülebilir. CAD programlarının birçoğunda da tabaka kavramı kullanılmıştır.
Objeler belli özelliklerine göre ayrı tabakarlarda çizilirler. Tabakalar da
çizim önceliğine göre sıralanarak ekranda hangi objenin daha üstte, hangi objenin
daha altta çizileceği belirlenir. Tabaka kavramı sayesinde objelere özelliklerine
göre erişim işlemleri de kolaylaşır. Burada bahsettiğimiz derinlik kavramı da,
tamamen üçüncü boyutla ilgili olarak hangi objenin daha önde hangisinin daha
arkada çizileceğini belirler. Üçüncü boyutu da ekran yüzeyinin normal doğrusu
olarak düşünebiliriz. Üç boyutlu OpenGL programlarının tamamında derinlik tamponu
kullanılır. Uygulamaya geçmeden önce derinlik tamponu ile ilgili fonksiyonların
prototiplerine ve ne iş yaptıklarına bakalım.
void
glClearDepth(GLclampd depth); |
Derinlik
tamponunun temizlenme değerini belirler. Bu değer, glClear fonksiyonunun
derinlik tamponu için kullandığı değerdir. 0-1 arasında değişir. Derinlik
tamponu temizleme değeri, glGet fonksiyonuna GL_DEPTH_CLEAR_VALUE parametresi
geçilerek öğrenilebilir. Bu fonksiyon, glBegin ve ona ilişkin glEnd fonksiyonları
arasında çağrılırsa hata oluşur. |
void
glDepthFunc(GLenum function); |
Derinlik
tamponu karşılaştırmaları için kullanılacak fonksiyonu belirler. Bu fonksiyonlar,
tampondaki z değeri ile o anda gelen z değerini karşılaştırırlar. Fonksiyona
parametre olarak geçilebilecek fonksiyonlar sembolik sabit olarak belirtilmiştir.
Bu karşılaştırmaların yapılabilmesi için derinlik testinin çalıştırılabilir
hale getirilmesi gereklidir. Bunun için de glEnable fonksiyonu, GL_DEPTH_TEST
sembolik sabiti parametre olarak geçilerek çağrılır. Derinlik testi varsayılan
olarak çalışmaz (disable) durumdadır. Bu fonksiyon, glBegin ve ona ilişkin
glEnd fonksiyonları arasında çağrılırsa hata oluşur. |
Ekranı çizime hazırladığımız
fonksiyon içerisinde, yukarıda bahsettiğimiz fonksiyonları da kendi belirlediğimiz
değerlerle çağırarak gerekli ayarlamaları yapabiliriz. Önceki yazılarımızda
yaptığımız uygulamalarda ekranı hazırladığımız fonksiyonun ismi InitializeGL
idi. Şimdi bu fonksiyonu yeni uygulamamıza uyarlayalım :
int InitializeGL(GLvoid)
{
    glClearColor(0.0f,0.0f,0.0f,0.0f);
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
    return TRUE;
} |
Önce glClearColor
fonksiyonunu çağırarak ekranı siyaha boyuyoruz. Burada ilk üç parametrenin değerini
değiştirerek farklı renklerde ekranlar elde edilebilir. Parametre olarak geçilecek
değerler 0 ile 1 arasında bir değer olmalıdır. glClearDepth fonksiyonunu 1.0
değeri ile çağırarak derinlik tamponunun temizlenmesini sağlıyoruz. Derinlik
testinin yapılabilmesi için GL_DEPTH_TEST sembolik sabitini, glEnable fonksiyonuna
parametre olarak geçerek çağırıyoruz. Daha sonra glDepthFunc fonksiyonu ile
derinlik testinde kullanılacak fonksiyonu belirtiyoruz. Burada parametre olarak
geçilen GL_LEQUAL sembolik sabitinin belirttiği derinlik testi fonksiyonu, o
an gelen z değeri, tamponda saklanan z değerine eşit ise veya ondan daha küçükse
başarılı olur. Sonraki adımda çağrılan glHint fonksiyonu, perspektif ayarlamalarını
yapar. Burada parametre olarak geçilen değerler ile en iyi perspektifin sağlanması
isteği iletilmiştir. Bu fonksiyon performans düşüklüğüne neden olabilir, ancak
görünüşü daha iyi hale getirir. Ekran ile ilgili ayarlamalarımızı yaptıktan
sonra çizim fonksiyonumuza geçebiliriz. Uygulamamızda önce tabanı kare olan
yüzeyleri üçgen olan bir prizma çizelim. Bunun için çizime başlarken üçgen çizeceğimizi
belirten GL_TRIANGLES sembolik sabitini kullanacağız.
//... glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(-1.5f,0.0f,-6.0f);
glRotatef(25.0,0.0f,1.0f,0.0f);
glBegin(GL_TRIANGLES);
glColor3f(0.5f,0.2f,0.5f);
glVertex3f(2.0f,1.0f,0.0f);
glColor3f(0.6f,0.0f,0.1f);
glVertex3f(1.0f,-1.0f,1.0f);
glColor3f(0.5f,0.2f,0.0f);
glVertex3f(3.0f,-1.0f,1.0f);
glColor3f(0.0f,0.5f,0.9f);
glVertex3f(2.0f,1.0f,0.0f);
glColor3f(0.9f,1.0f,0.2f);
glVertex3f(3.0f,-1.0f,1.0f);
glColor3f(0.2f,0.8f,0.0f);
glVertex3f(3.0f,-1.0f,-1.0f);
glColor3f(0.0f,0.5f,0.4f);
glVertex3f(2.0f,1.0f,0.0f);
glColor3f(1.0f,0.5f,0.0f);
glVertex3f(3.0f,-1.0f,-1.0f);
glColor3f(0.2f,1.0f,0.0f);
glVertex3f(1.0f,-1.0f,-1.0f);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f(2.0f,1.0f,0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(1.0f,-1.0f,-1.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(1.0f,-1.0f,1.0f);
glEnd(); //...
|
Her bir noktayı
belirtmeden önce glColor3f fonksiyonunu çağırarak köşe renklerinin farklı olmasını
sağlıyoruz. Burada noktaların koordinat değerlerini üçgenler oluşturacak şekilde
hesaplıyoruz. Noktaların z değerlerinin farklı olması ile şekil derinlik kazanıyor.
glTranslatef fonksiyonu x, y ve z eksenleri boyunca öteleme işlemi yapar. Yukarıdaki
parametreler ile ekran üzerinde x ekseni boyunca 1.5 birim sola öteliyoruz,
y ekseni boyunca öteleme yapmıyoruz, z ekseni boyunca 6 birim öteleme yapıyoruz.
z ekseni boyunca yapılan ötelemeler ekrana yakınlaşmayı veya uzaklaşmayı belirler.
z ekseni boyunca yapılacak ötemele işlemi için negatif değer girilmesi derinliği
artırır, pozitif değer girilmesi derinliği azaltır. (Yani negatif değer girdiğimizde
ekrana daha uzaktan bakarız.) x ekseni boyunca girilen pozitif değerler sağa
ötelemeyi, negatif değerler ise sola ötelemeyi belirtir. y ekseni boyunca girilen
pozitif değerler yukarıya doğru ötelemeyi, negatif değerler aşağıya doğru ötelemeyi
belirtir. Öteleme işlemini de yaptıktan sonra glRotatef fonksiyonu ile şekli
x, y ve z eksenleri etrafında döndürüyoruz. Bu fonksiyona geçilen ilk parametre
döndürme açısını belirtir. Yukarıda kullandığımız değerler ile şekli (0.0,1.0,0.0)
noktası etrafında 25 derece döndürüyoruz. Döndürme işlemini de yaptıktan sonra
glBegin ve glEnd fonksiyonları arasında üçgen yüzeylerimizin köşe koordinatlarını
belirtiyoruz. Bu kod çalıştırıldığında ekranda göreceğimiz çizim şöyle olacaktır
:
Şimdi bir küp oluşturmak
için glBegin fonksiyonunu GL_QUADS sembolik sabitini parametre olarak geçerek
çağıralım ve kübün altı yüzeyinin köşe noktalarının koordinat değerlerini verelim.
Kübü de üç boyutlu çizeceğimiz için glVertex3f fonksiyonunu kullanmalıyız. Fonksiyona
z değeri olarak noktaların derinlik değerlerini geçeceğiz.
//...
glBegin(GL_QUADS);
glColor3f(0.0f,0.5f,0.1f);
glVertex3f(1.0f,1.0f,-1.0f);
glVertex3f(-1.0f,1.0f,-1.0f);
glVertex3f(-1.0f,1.0f,1.0f);
glVertex3f(1.0f,1.0f,1.0f);
glColor3f(0.0f,0.4f,0.0f);
glVertex3f(1.0f,-1.0f,1.0f);
glVertex3f(-1.0f,-1.0f,1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(1.0f,-1.0f,-1.0f);
glColor3f(0.0f,0.2f,0.7f);
glVertex3f(1.0f,1.0f,1.0f);
glVertex3f(-1.0f,1.0f,1.0f);
glVertex3f(-1.0f,-1.0f,1.0f);
glVertex3f(1.0f,-1.0f,1.0f);
glColor3f(0.0f,0.0f,0.5f);
glVertex3f(1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,1.0f,-1.0f);
glVertex3f(1.0f,1.0f,-1.0f);
glColor3f(0.8f,0.1f,0.2f);
glVertex3f(-1.0f,1.0f,1.0f);
glVertex3f(-1.0f,1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,1.0f);
glColor3f(0.0f,0.8f,0.0f);
glVertex3f(1.0f,1.0f,-1.0f);
glVertex3f(1.0f,1.0f,1.0f);
glVertex3f(1.0f,-1.0f,1.0f);
glVertex3f(1.0f,-1.0f,-1.0f);
glEnd();
//...
|
glColor3f fonksiyonunu
çağırdıktan sonra sırasıyla bir yüzeyin dört köşe noktası için koordinat değerlerini
glVertex3f fonksiyonu ile belirtiyoruz. Böylece her yüzey bir ve birbirinden
farkl ırenge sahip olmaktadır. Şekli üç boyutlu olarak daha iyi bir biçimde
görebilmek için glTranslatef fonksiyonu ile 50 derece döndürüyoruz. Bu çizim
sonrasında ekran görüntümüz ise şöyle olmaktadır :
glDepthFunc fonksiyonuna
farklı sembolik sabitleri parametre olarak geçerek ön yüzeylerin arka yüzeylerden
daha arkada görünmesini sağlayabiliriz. Eğer yüzey olarak bir görüntü istemiyorsak,
yani yüzeylerin boyanmamasını istiyorsak sadece yüzeyi sınırlayan çizgileri
belirtmemiz gereklidir. Bir başka seçeneğimiz ise yine şekli yüzey olarak çizip,
yani GL_QUADS ile çizime başlamak, ancak çizimden hemen önce poligon (çoklu
doğru) modunu değiştirmektir. Poligonun çizim modunu değiştirmek için OpenGL
kütüphanesinin glPolygonMode fonksiyonu kullanılabilir. Bu fonksiyon, poligonun
çizim stilini ayarlamamızı sağlar. Çizilen şekillerin her yüzeyinin içinin boyanması,
içinin boş bırakılıp çizgiler ile sınırlanması ya da sadece noktalar ile belirtilmesi
gibi seçenekler belirtebiliriz. Bu fonksiyonun prototipini inceleyelim :
void
glPolygonMode(GLenum face, GLenum mode); |
Fonksiyonun
ilk parametresi, belirtilen stilin poligonun hangi yüzeylerine uygulanacağını
belirler. Bu parametre yerine geçilebilecek bazı sembolik sabitler tanımlanmıştır.
Bunlar GL_FRONT, GL_BACK, GL_FRONT_AND_BACK sembolik sabitleridir. İsimlerinden
de anlaşılacağı üzere ilk sembolik sabit seçimin poligonun ön yüzeylerini
etkilemesini, ikinci sembolik sabit poligonun arka yüzeylerini etkilemesini,
son sembolik sabit ise hem ön hem de arka yüzeylerini etkilemesini sağlar.
İkinci parametre ise seçilen stili belirler. Bu stil bilgisi için de bazı
sembolik sabitler tanımlanmıştır. Bu sembolik sabitler, GL_POINT, GL_LINE
ve GL_FILL olarak isimlendirilmiştir. GL_POINT stili, poligonun sadece köşe
noktalarının, yani poligonu sınırlayan çizgilerin kontrol noktalarının işaretlenmesini
sağlar. GL_LINE stili, sadece poligonu sınırlayan kenarların işaretlenmesini
sağlar. Bu stilde yüzeyler boyanmaz ve poligon bir iskelet görünümü alır.
GL_FILL stili ise yüzeylerin boyanmasını sağlar. Bu fonksiyon glBegin ve
ona ilişkin glEnd fonksiyonları arasında çağrılmamalıdır. Çizime başlanmadan
önce çağrılmalıdır. |
Yukarıdaki çizimlerde,
yüzeylerin boyanmamasını ve şeklin iskelet görünümünde olmasını sağlamak için
glBegin fonksiyonlarından önce aşağıdaki kodu eklemeliyiz :
//... glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); glBegin(...) //... |
glPolygonMode fonksiyonuna
etkide bulunulacak yüzeyleri belirten parametre için GL_FRONT_AND_BACK sembolik
sabitini, çizim stili parametresi için de GL_LINE sembolik sabitini geçtik.
Yani poligonlarımızın tüm yüzeyleri sadece kendilerini sınırlayan çizgiler işaretlenerek
çizilecektir. Bu seçenekler belirtildikten sonra küp için elde edilen ekran
görüntüsü aşağıda solda, prizma için elde edilen ekran görüntüsü ise aşağıda
sağda görülmektedir :
  
Görüldüğü üzere
poligonlara ait yüzeyler sadece onları sınırlayan kenarlar işaretlenerek çizilmiştir
ve yüzey içleri boyanmadan bırakılmıştır. Bu yazımızda yapmış olduğumuz prizma
uygulamasını indirmek için burayı, küp
uygulamasını indirmek için burayı tıklayabilirsiniz.
Bir yazımızın daha sonuna geldik, herkese mutlu ve serin günler dilerim..
Kaynaklar :
- www.nehe.gamedev.net
- OpenGL Yardım Sayfaları
Makale:
C ve OpenGL : Üç Boyutlu Çizimlere Giriş C ve Sistem Programlama Çiğdem Çavdaroğlu
|