C#nedir?com
 
YAZAR HAKKINDA
Çiğdem Çavdaroğlu
Çiğdem Çavdaroğlu
http://www.csharpnedir.com/
İletişme geçmek için tıklayın.
28 Makalesi yayınlanmakta.
Yazar hakkında detaylı bilgi için tıklayın.
Yayınlanan diğer makaleleri için tıklayın.
İlgili etiketler: belirtir. degeri fonksiyon fonksiyonu fonksiyonunu isimli istedigimiz kontrol opengl parametre pencere pencerenin program return verilen C / Sys Prog. Çiğdem Çavdaroğlu
 
YAZI HAKKINDA
Türü : Makale
Serbest Köşede C#nedir?com üyelerinin hazırladıkları yazılar yayınlanır. Bu yazılar editör incelemesine girmeden yayınlanır.
Seviyesi : İleri
Kategori : C / Sys Prog.
Yayınlanma Tarihi : 27.6.2005
Okunma Sayısı : 27938
Yorum Sayısı : 0     yorum yaz
Site İçi AramaSİTE İÇİ ARAMA
Üye Girişini AçÜye GİRİŞİ
Üye girişi için tıklayın.
Kullanıcı Adı
Şifre
 
Beni her zaman hatırla
Bir hafta boyunca kullanıcı bilgilerinizi kullanıcı çıkışı yapana kadar hatırlar. (Paylaşılan bilgisayarlarda önerilmez.)
 
Şifremi / Kullanıcı Adımı unuttum.
 
.net TV RSS Serbest KÖŞE (?)
Serbest Köşede C#nedir?com üyelerinin hazırladıkları yazılar yayınlanır. Bu yazılar editör incelemesine girmeden yayınlanır.
emre TAŞ
XML - Deniz Kılınç
emre TAŞ
yazının devamı >
emre TAŞ
Decompiling and Reverse Engineering .Net Radyo
emre TAŞ
yazının devamı >
emre TAŞ
Masaüstü mü ? İnternet Mi? .Net Radyo
emre TAŞ
yazının devamı >
emre TAŞ
.Net Kavramları - .Net Radyo
emre TAŞ
yazının devamı >
emre TAŞ
Yeni Başlayanlar için - .Net Radyo
emre TAŞ
yazının devamı >
Makale Gönder Bende Yazmak İstiyorum
.net TV RSSBlogroll
Turhal Temizer 'in Blogu
Conda install environment.yml Package 31.3.2020
Turhal Temizer 'in Blogu
Mac OS/X Removing CUDA 31.3.2020
Burak Selim Şenyurt
Sekiz Saatlik Sonsuz Döngü 31.3.2020
Burak Selim Şenyurt
Switch Case Kullanmadan Kod Yazılabilir mi? 31.3.2020
  Diğer Herşey
Sponsorlar
BT Akademi
Medya Portakal
Video Hosting Sponsoru
Csharpnedir.com bir Ineta üyesidir
Uzman Abi
Her Yönüyle C# - Sefer Algan
C ve OpenGL : Bir OpenGL Penceresi Oluşturmak
 
Kapat
Sayfayı Yazdır Sık Kullanılanlara Ekle Arkadaşıma Gönder MySpace Del.Ico.Us Digg Facebook Google Mixx Reddit StumbleUpon
OpenGL ve C başlığı altında yürüteceğimiz yazı dizimizde, OpenGL teknolojisinin ne olduğunu, bu teknoloji ile neler yapılabileceğini ve OpenGL kullanarak C dilinde nasıl kod yazılacağını ele alacağız. Grafik programlamaya temel teşkil edebilecek bir proje oluşturarak, pratik bilgimizi de pekiştireceğiz. Örnekleri iyi anlayabilmek için bir miktar Windows API Programlama bilgisine de ihtiyacımız olacak. Bu yazı dizisinde API programlama, sadece projemizin gerektirdiği kadar ele alınacaktır. (OpenGL ile daha ayrıntılı bilgi için www.opengl.org ve www.nehe.gamedev.net sitelerinden yararlanabilirsiniz.)

OpenGL (Open Graphics Library), iki veya üç boyutlu grafik programlamada kullanılan bir teknoloji olarak tanımlanabilir. OpenGL kütüphaneleri, birtakım grafik işlemler yapan API (Application Programming Language) fonksiyonları içerirler. API, işletim sistemlerinin, altlık olarak veya yardımcı olarak kullanacağımız programların gerçekleştirdiği bir işlemi, programcının kullanmasını sağlayan yöntemler olarak tanımlanabilir. Kullandığımız programlama dilinde, gerekli olduğu yerlerde OpenGL’in API fonksiyonlarını çağırarak grafiksel işlemler yapılmasını sağlarız. Aslında OpenGL bir kütüphanedir. Bu kütüphanedeki fonksiyonlar sayesinde biz, grafik donanımını yönetme olanağı elde ederiz. OpenGL kütüphanesi, grafiksel programlamayı standartlaştırmayı ve işletim sistemleri arasında taşınabilir kılmayı amaçlar. OpenGL’i platformdan tamamen bağımsız kılmak için OpenGL Araç Kiti (GLUT) geliştirilmiştir. Programımız içerisinde bir form oluşturmak, kullanıcıdan veri alabilmek için gerekli bileşenleri oluşturmak için kullandığımız işletim sistemine bağlıyızdır. GLUT bu bağımlılığı ortadan kaldırmak için geliştirilmiştir. GLUT ile OpenGL programlarımızda formlar oluşturabilir, kullanıcıdan veri alabiliriz.

OpenGL teknolojisini hangi programlama dillerinde kullanabiliriz ?


  * Ada * C * C++
  * C# * Fortran * Python
  * Perl *Java ...  
Yüksek seviyeli dillerde, OpenGL kütüphanesinin kullanımını daha basit hale getiren sınıflar oluşturulmuştur. C dili için böyle bir şey söz konusu değildir. Bu nedenle projemizde yapmak istediğimiz her şeyi kendimiz kodlayacağız. OpenGL bir kütüphane olduğuna göre, programımızı çalıştırabilmek için öncelikle işletim sistemimizde çalışma zamanı kütüphanelerinin (run-time libraries) bulunması gereklidir. OpenGL yaygın olarak kullanıldığı için, OpenGL çalışma zamanı kütüphaneleri bazı işletim sistemlerinin kurulumu sırasında sistemimize otomatik olarak eklenir. Bu işletim sistemleri arasında, Linux, Unix, Mac OS, Windows 95 / 98 / NT / 2000 sayılabilir. Normal şartlar altında bu işletim sistemlerinde kütüphane hatalarının çıkmaması gereklidir. Programımızı yazarken kullandığımız araçta da, bu kütüphaneleri kullanacağımızı derleyiciye anlatabilmek için birkaç ufak işlem yapmamız gereklidir. Projemizi geliştirmemiz sırasında bu ayrıntılara değinilecektir. OpenGL projesi geliştirirken izleyebileceğimiz birkaç yol vardır. GLUT araç kitini kullanarak pencere oluşturma, kullanıcıdan bilgi alma gibi işlemleri bu araç ile yapabiliriz. Dolayısıyla bu işlemler için kendimizin kod yazmasına gerek kalmayacaktır ve kod azalacaktır. Sadece istediğimiz işlemleri yapan kütüphane fonksiyonlarını çağırmamız gerekecektir. İzleyebileceğimiz diğer bir yol ise, kısa ve kolay bir şekilde OpenGL uygulaması geliştirmemizi sağlamak için geliştirilen uygulama sihirbazlarını kullanmaktır. Örneğin Jeff Burns tarafından geliştirilen OpenGL sihirbazı bu amaçla kullanılabilir. (Bu sihirbazı internetten bulabilir ve bilgisayarınıza kurabilirsiniz.) Başka bir yol ise bu araçların hiçbirisini kullanmamak ve gerekli tüm kodları, OpenGL kitaplığını da kullanarak kendimiz yazmaktır. İşin detaylı bir şekilde öğrenilmesi için tercih edebileceğimiz en iyi yol üçüncü yoldur.

İşe OpenGL kitaplığındaki sık kullanılan fonksiyonları inceleyerek başlayabiliriz.


    * TEMEL OPENGL FONKSİYONLARI :

void glBegin(enum kip); Bir çizime başlandığını belirtir. Parametresi çizilen geometrik şekli tanımlar. Bu parametre yerine geçilmesi gereken sembolik sabitler şunlardır :
   - GL_POINTS => Nokta çizileceğini belirtir.
   - GL_LINES => Verilen noktaların birleştirilerek bir çizgi çizileceğini belirtir.
   - GL_POLYGON => Verilen noktaların birleştirilerek doğrular oluşturulacağını ve oluşan şeklin alansal bir şekil olacağını, içinin renklendirileceğini belirtir.
   - GL_QUADS => Verilen dört noktadan içi renkelndirilmiş bir dörtgen oluşturulacağını belirtir.
   - GL_TRIANGLES => Verilen üç noktadan içi renklendirilmiş üçgen oluşturulacağını belirtir.
   - GL_TRIANGLES_STRIP => Verilen noktaların üçer üçer sırasıyla birleştirerek üçgenler oluşturulacağını belirtir.
   - GL_QUAD_STRIP => Verilen noktaların dörder dörder sırasıyla birleştirilerek dörtgenler oluşturulacağını belirtir.
   - GL_TRIANGLE_FAN => Verilen noktaların ilk nokta ikişer ikişer alınıp her adımda ilk noktayı üçüncü nokta kabul ederek birleştirileceğini ve yelpazemsi bir şekil oluşturulacağını belirtir.
void glEnd(glVoid); glBegin() fonksiyonu ile başlatılmış çizim işleminin bittiğini belirtir. Çizdirilen şekil ekrana bastırılmak üzere saklanır. Saklanmış bu şeklin çizdirilmesi için başka bir fonksiyon kullanılır.
void glFlush(glVoid); Tampon bellekteki tüm şekillerin ekrana çizdirilmesini sağlar.
void glClear(GLbitfield maske); Tamponların içeriğini belli sembolik sabitleri dikkate alarak temizler. Parametre olarak da bu sembolik sabitleri veya bunların "veya" bağlaçları ile olan kombinasyonlarını alır. Bu sembolik sabitler şunlardır :
   - GL_COLOR_BUFFER_BIT
   - GL_DEPTH_BUFFER_BIT
   - GL_ACCUM_BUFFER_BIT
   - GL_STENCIL_BUFFER_BIT
void glClearColor(GLclampf kirmizi,GLclampf yesil,GLclampf mavi,GLclampf donukluk); glClear() fonksiyonu ile temizlenen ekran renk tamponunun ne renk alacağını belirler. Her parametre için 0 ya da 1 değeri geçilir.
void glColor3s(short kirmizi,short yesil,short mavi); Çizilecek şeklin rengini belirler. Ön tanımlı değerler 0’dır.
void glLineWidth(float genislik); Çizgi kalınlığını belirler. Ön tanımlı değer 1.0’dır.
void glLineStipple(int carpan,short oruntu); Çizginin kesikli ya da düz görünmesini sağlar. Örüntüdeki bit 0 ise bu bite karşılık gelen benek ekrana basılmaz, 1 ise basılır. Böylece çizgi kesikli görünür.
void glEnable();
void glDisable();
Çizim performansında artış sağlamak için kesiklilik, ışıklandırma, dokuma gibi özellikler glDisable() fonksiyonu ile kaldırılır. Böylece çizimin hızlanması sağlanır. glEnable() fonksiyonu ile özellikler tekrar kullanılabilir hale getirilir.
Örnek :
    glEnable(GL_LINE_STIPPLE); //Kesikli çizgi çizilebilmesini sağlar
    glEnable(GL_SMOOTH); //Renk geçişlerini yumuşatır.
void glRecti(int x1,int y1,int x2,int y2); Dörtgen çizer. Bu fonksiyon ile aynı işi yapan glRects, glRectf, glRectd fonksiyonları da vardır. Bu fonksiyonların sadece parametre değişkenlerinin türleri farklıdır. Parametre değişkenlerinin türleri sırasıyla short, float ve double türlerindendir.
void glVertex2f(float x,float y); Bir geometrik şekle ait kontrol noktasının koordinat değerlerini belirtir. Geometrik şeklin ne olacağı glBegin fonksiyonunun parametresi ile belirlenir. Bu fonksiyon iki boyutlu çizim yapılacağı zaman kullanılır. Koordinatın sadece x ve y değerleri verilir. Bu fonksiyon ile aynı işi yapan glVertex2s, glVertex2i, glVertex2d fonksiyonları sırasıyla short, integer, double türden parametre değişkenleri alırlar.
void glVertex3f(float x,float y,float z); Bir geometrik şekle ait kontrol noktasının koordinat değerlerini belirtir. Geometrik şeklin ne olacağı glBegin fonksiyonunun parametresi ile belirlenir. Bu fonksiyon üç boyutlu çizim yapılacağı zaman kullanılır. Koordinatın x ve y değerlerine ilaveten z değeri de verilir. Bu fonksiyon ile aynı işi yapan glVertex3s, glVertex3i, glVertex3d fonksiyonları sırasıyla short, integer, double türden parametre değişkenleri alırlar.
    * OPENGL DÖNÜŞÜM FONKSİYONLARI :

void glRotate(double aci,double x,double y,double z); Şekil "aci" parametresi ile belirtilen derece değeri kadar döndürülür. x, y ve z parametrelerine 0 değeri verilirse o eksen etrafında dönme olmaz, 1 ise o eksen etrafında dönme olur, -1 ise o eksen etrafında ters yöne dönme olur.
void glTranslated(double x,double y,double z); Koordinat sistemini, o anki koordinatlara x, y, z değerlerini ekleyerek öteler.
void glLoadIdentity(); Yapılmış tüm dönüşümleri geri alır.
void glScaled(double x,double y,double z); Ölçekleme yapar. Parametre olarak verilen değerler 1’den küçük ise şekil küçültülür, büyükse şekil büyütülür. Bu fonksiyonla aynı işi yapan glScalef fonksiyonu float türden parametre alır.
* Grafik Donanımı ve Görüntü Yönetimi Üzerine Temel Bilgiler :

Ekrandaki görüntünün yenilenmesi, CRT (Cathode Ray Tube) tarafından yapılır. CRT, bilgisayar grafik donanımının bir parçasıdır. Ekran için yatay ve düşey tarama zamanlama sinyalleri oluştur, eş zamanlı olarak da görüntü belleği adres sayacını bir artırır. Görüntüleme sistemi, bu adres değerlerini alarak buralardaki kodları okur ve çözümleyerek monitöre sinyaller şeklinde gönderir. Görüntü yönetimi tamamen CRT denetleyicisinde olduğu için biz programımızdan buna müdahele edemeyiz. Programımızdan OpenGL’in çizim için kullandığı bellek bölgesine yeni şekiller eklemek yoluyla değişiklikler gönderdiğimizde, bu değişiklikler anında monitöre yansıtılır. Bu durum, çizimde performansı düşürücü sonuçlara sebep olur. Devamlı ekrandaki görüntünün yenilenmesi ekranda yanıp sönme gibi sonuçlara da yol açabilir. Bu problemi çözmek için izleyebileceğimiz birkaç teknik vardır. Bu tekniklerde temel mantık, ekrandaki görüntünün sürekli yenilenmesi değil, değişikliklerin hepsinin tamamlandıktan sonra bir defada yenilenmesidir. Bu tekniği kullandığımız zaman, çizim işlemlerini başka bir bellek bölgesinde yapıp sonra ekran bölgesine aktarırırz. Buna "double buffering) denilmektedir. ekranın yenilenmiş görüntüsü arka planda hazırlanırken, kullanıcının gördüğü ekranda hiçbir değişiklik yapılmaz. Tüm değişiklikler tamamlandıktan sonra, programımızdan glSwapBuffers() fonksiyonunu çağırırız ve arka planda hazırlanan değişiklikler kullanıcıya yansıtılır. Ekranın yenilenmesi sırasındaki titremeler, yanıp sönme vb problemler bu şekilde önlenebilir.

Şimdi uygulamamıza geçelim. İlk olarak tüm kodları kendimiz yazacağız, yukarıda bahsettiğimiz GLUT araç kitini veya sihirbazı kullanmayacağız. Bu araçların nasıl kullanılacağına dair bilgileri de ileride ele alırız. OpenGL kütüphanesini kullanacağımız için öncelikle derleyiciye bu kütüphanelere .lib dosyalarını göstermeliyiz. Bu dosyalar derleme işlemi yapan programlarda, proje ayarları bölümünde belirtilir. Microsoft Visual C++ 6.0 derleyicisinde bu işlem şöyle yapılır : Proje açıldıktan sonra "Project" menüsünden "Settings" seçeneği seçilerek ayarlar penceresine ulaşılır. Bu pencerede "Link" sekmesinde "Object/library modules" kısmına ilgili dosyaların isimleri yazılır. MinGW’de de aynı yol izlenir. Microsoft Visual C++ 7.0 (.NET)’da ise "Project" menüsünden "Properties" seçilerek özellikler penceresi açılır. Bu pencerede soldaki menüden "Linker" seçilir ve "Input" sekmesine gelinir. Burada "Additional Dependencies" kısmına ilhili dosyaların isimleri yazılır.

    

  Yukarıda solda görülen resim, Microsoft Visual C++ 6.0’da, yukarıda sağda görülen resim Microsoft Visual C++ 7.0’da (.NET’te) ve yandaki resim de MinGW’de kütüphane dosyalarının belirtilmesini göstermektedir. Kaynak kod dosyamızda "include" etmemiz gereken başlık dosyasının adı ise gl\glu.h’dır. Projeyi oluştururken, proje seçeneği olarak "Win32 Application" seçilmelidir. Projeye .c uzantılı bir kaynak kod dosyası ekledikten sonra kullanacağımız kütüphane dosyalarını proje ayarları menülerinde yukarda bahsedildiği gibi belirtiriz. Artık kodlamaya geçebiliriz.

Programımız WinMain fonksiyonu ile başlayacak. Bu fonksiyon içerisinde pencere oluşturan "CreateWindow" ya da "CreateWindowEx" fonksiyonu yerine kendi yazacağımız bir pencere fonksiyonunu çağıracağız. Bizim yazacağımız bu fonksiyon da CraeteWindow ya da CraeteWindowEx fonksiyonunu çağıracak, ancak biz ilaveten başka işlemler de yapacağız ve sonuçta oluşan pencere bir openGL penceresi olacak. Önce WinMain fonksiyonumuzu yazalım. OpenGL penceresini oluşturan fonksiyonu, WinMain içerisinde çağıracağımız için bu fonksiyonun prototip bildirimine bir bakalım. Tanımlamasını WinMain’i oluşturduktan sonra yapalım.

BOOL CreateGLWindow(char * title, int width, int height, int bits)
Fonksiyonun geri dönüş değeri türü olarak yazılan BOOL, kaynak kod dosyasının başında #define önişlemci komutu ile int olarak tanımlanmıştır. Fonksiyonun ilk parametresi oluşturulacak pencerenin başlığı, ikinci ve üçüncü parametreleri pencerenin genişliği ve yüksekliği, son parametresi ise bir seçeneğini belirtmektedir. Geri dönüş değeri başarı durumunu belirtmektedir.


WinMain içerisinden çağırdığımız diğer fonksiyonlar da, oluşturduğumuz OpenGL penceresini yok eden KillGLWindow isimli fonksiyon ve boş bir pencere çizen DrawGLSceen isimli fonksiyondur. CreateGLWindow fonksiyonundan çağrılan fonksiyonlar da, penceremiz için başlangıç işlemlerini yapan InitializeGL fonksiyonu ve penceremizin boyutlarını ayarlayan ResizeGLScene fonksiyonudur. Bu fonksiyonların prototip bildirimleri ise şu şekildedir :

GLvoid KillGLWindow(GLvoid);
BOOL DrawGLScene(GLvoid);
int InitializeGL(GLvoid);
GLvoid ReSizeGLScene(GLsizei width, GLsizei height);
GLvoid, OpenGL kütüphanesinde tanımlanmış bir sembolik sabittir. C dilinde kullandığımız void anahtar sözcüğü ile eşdeğer anlama sahiptir. Yani KillGLWindow isimli fonksiyonumuz parametre değişkeni almamaktadır ve geri dönüş değeri de yoktur. Yaptığı iş ise, oluşturulan pencereyi yok etmektir. DrawGLScene isimli fonksiyon ise çizim işlemlerinin yapılacağı fonksiyondur. Bu uygulamada herhangi bir çizim yapmayacağımız için aslında bu fonksiyon sadece ekranı temizleyecektir. Yazı dizimizin ileriki bölümlerinde bu fonksiyon içerisinde çizim işlemleri de yapacağız. Kaynak kod dosyamızın başında tüm program boyunca kullanacağımız birtakım global deişkenler tanımladık. Bu değişkenlere de bir göz atalım :

HGLRC hRC = NULL;
HDC hDC = NULL;
HWND hWnd = NULL;
HINSTANCE hInstance;
OpenGL programları bir Rendering Context’e (RC) bağlıdırlar. RC, OpenGL programından yapılan çağrıları Device Context’e (DC) iletir. Bir pencereye çizim yapabilmek için bir DC oluşturmamız gereklidir. DC, penceremiz ile GDI (Graphics Device Interface) arasında bağlantı kurar. RC ve DC nesnelerini global alanda hRC ve hDC isimleri ile tanımladık. HWND türünden tanımladığımız hWnd isimli nesne ise, oluşturacağımız pencereye Windows sistemi tarafından atanacak handle değerini tutacaktır. HISNTANCE türünden tanımladığımız hInstance ise, uygulamanın belleğe yüklenme adresini tutacaktır. Bu nesnelerin hepsini yazdığımız fonksiyonlarda kulanacağımız için global alanda tanımladık.

Şimdi WinMain fonksiyonumuza geçelim :

int WINAPI WinMain(HINSTANCE hInstance, //.exe dosya formatının belleğe yüklenme adresi
                           HINSTANCE hPrevInstance, //Önceki kopya
                           LPSTR lpCmdLine, //Komut satırı argümanları
                           int nCmdShow) //Ana pencerenin görüntülenme özelliğini belirten parametre
{
    WPARAM wParam;
    MSG msg;
    BOOL end = FALSE;     if (!CreateGLWindow("C ve OpenGL : OpenGL Penceresi Oluşturmak",640,480,16))
       return -1;

    while(!end)
    {
       if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
          if (msg.message == WM_QUIT)
             end = TRUE;
          else {
             TranslateMessage(&msg);
             DispatchMessage(&msg);
          }
       }
       else {
          DrawGLScene();
          SwapBuffers(hDC);
       }
    }

    KillGLWindow();
    return (msg.wParam);
}
Windows GUI programlama’dan ve API programlamadan hatırlanacağı üzere WINAPI sembolik sabiti, fonksiyonun çağrılma biçimini belirler. "_stdcall" olarak tanımlanmıştır. Fonksiyonun başında tanımlanan wParam değişkeni ve msg yapısı, mesajlarla ilgili birtakım bilgileri tutmaktadır. "done" isimli değişken ise TRUE veya FALSE değerlerini alabilir ve programın akışını kontrol eder. Bu değişken 0 değerine sahip olduğunda while döngüsünden çıkılarak program da sonlandırılacaktır. WinMain’in başında değişkenler tanımlandıktan sonra, CreateGLWindow fonksiyonu çağrılarak pencerenin oluşturulması sağlanıyor. Eğer pencere oluşturma işlemi başarısızlıkla sonuçlanırsa sıfırdan farklı bir değere geri dönülerek fonksiyonun başarısız olduğu bilgisi gönderiliyor. Eğer pencere başarılı bir şekilde oluşturulmuşsa while döngüsüne giriliyor. Döngü içerisinde, İşlenmesi gereken mesaj olup olmadığı PeekMessage fonksiyonu ile sorgulanıyor. Eğer işlenmesi gereken bir mesaj varsa PeekMessage fonksiyonu içerisinde bu mesaj ile ilgili bilgiler fonksiyona parametre olarak geçilen msg nesnesine yazılır. Blok içerisinde de msg nesnesinin içeriği kontrol edilerek işlenmesi gereken mesajlara ilişkin işlemler yapılıyor. Eğer VM_QUIT mesajı gönderilmişse "end" değişkeni TRUE yapılıyor ve while döngüsünden çıkılarak program sonlandırılıyor. VM_QUIT mesajı programın sonlandırılması istendiğinde gönderilir. Eğer çıkma mesajı gelmemişse gelen mesaj TranslateMessage ve DispatchMessage fonksiyonları ile sistemin işleyebileceği bir hale getirilir. Herhangi bir mesajın alınmaması durumunda ise DrawGLScene fonksiyonu çağrılarak çizim işlemi yapılıyor ve SwapBuffers fonksiyonu çağrılarak arka planda depolanan çizim değişikliklerinin ekrana yansıtılması sağlanıyor. Şimdi başlangıç fonksiyonumuzda çağırdığımız diğer fonksiyonların tanımlamalarına geçebiliriz. İlk olarak pencereyi oluşturan CreateGLScene fonksiyonunu ele alalım :

BOOL CreateGLWindow(char * title, int width, int height, int bits)
{
    GLuint PixelFormat;
    WNDCLASS wc;     DWORD dwExStyle;
    DWORD dwStyle;

    static PIXELFORMATDESCRIPTOR pfd;

    RECT WindowRect;
    WindowRect.left = (long)0;
    WindowRect.right = (long)width;
    WindowRect.top = (long)0;
    WindowRect.bottom = (long)height;

    hInstance = GetModuleHandle(NULL);
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = (WNDPROC) WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "OpenGL";

    if (!RegisterClass(&wc)) {
       MessageBox(NULL, "Pencere sınıfı tanıtımı işleminde başarısız olundu.", "HATA", MB_OK|MB_ICONEXCLAMATION);
       return FALSE;
    }

    dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    dwStyle = WS_OVERLAPPEDWINDOW;

    AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

    if (!(hWnd = CreateWindowEx(dwExStyle,
                     "COpenGL",
                     title,
                     WS_CLIPSIBLINGS |
                     WS_CLIPCHILDREN | //OpenGL penceresinin doğru bir şekilde gösterilmesini sağlar.
                     dwStyle,
                     0, 0,
                     WindowRect.right - WindowRect.left,
                     WindowRect.bottom - WindowRect.top,
                     NULL,
                     NULL, //no menu
                     hInstance,
                     NULL)))
    {
       KillGLWindow();
       MessageBox(NULL, "Pencere oluştururken hata oluştu", "HATA", MB_OK|MB_ICONEXCLAMATION);
       return 0;
   }

    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = bits;
    pfd.cRedBits = 0;
    pfd.cRedShift = 0;
    pfd.cGreenBits = 0;
    pfd.cGreenShift = 0;
    pfd.cBlueBits = 0;
    pfd.cBlueShift = 0;
    pfd.cAlphaBits = 0;
    pfd.cAlphaShift = 0;
    pfd.cAccumBits = 0;
    pfd.cAccumRedBits = 0;
    pfd.cAccumGreenBits = 0;
    pfd.cAccumBlueBits = 0;
    pfd.cAccumAlphaBits = 0;
    pfd.cDepthBits = 16;
    pfd.cStencilBits = 0;
    pfd.cAuxBuffers = 0;
    pfd.iLayerType = PFD_MAIN_PLANE;
    pfd.bReserved = 0;
    pfd.dwLayerMask = 0;
    pfd.dwVisibleMask = 0;
    pfd.dwDamageMask = 0;

    if (!(hDC = GetDC(hWnd)))
    {
       KillGLWindow();
       MessageBox(NULL, "OpenGL Device Context oluşturulamadı","HATA", MB_OK|MB_ICONEXCLAMATION);
       return FALSE;
    }

    if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) {
       KillGLWindow();
       MessageBox(NULL,"Uygun pixel formatı bulunamadı","HATA", MB_OK|MB_ICONEXCLAMATION);
       return FALSE;
    }

    if (!SetPixelFormat(hDC, PixelFormat, &pfd)) {
       KillGLWindow();
       MessageBox(NULL,"Pixel formatı set edilemedi","HATA", MB_OK|MB_ICONEXCLAMATION);
       return FALSE;
    }

    if (!(hRC = wglCreateContext(hDC))) {
       KillGLWindow();
       MessageBox(NULL,"OpenGL rendering context alınamadı","HATA", MB_OK|MB_ICONEXCLAMATION);
       return FALSE;
    }

    if (!wglMakeCurrent(hDC,hRC)) {
       KillGLWindow();
       MessageBox(NULL,"Rendering context aktif hale getirilemedi","HATA", MB_OK|MB_ICONEXCLAMATION);
       return FALSE;
    }

    ShowWindow(hWnd,SW_SHOW);
    SetForegroundWindow(hWnd);
    SetFocus(hWnd);
    ReSizeGLScene(width,height);

    if (!InitializeGL()) {
       KillGLWindow();
       MessageBox(NULL,"Initilaize başarısız oldu","HATA",MB_OK|MB_ICONEXCLAMATION);
       return FALSE;
    }

    return TRUE;
}

OpenGL penceresini oluşturan CreateGLWindow fonksiyonumuzu adım adım inceleyelim. Oluşturacağımız pencere ilişkin özellik bilgilerini tutacağımız WNDCLASS türünden wc isimli bir yapı nesnesi tanımlıyoruz. Penceremize ilşkin bilgileri bu yapı nesnesinin veri elemanlarına atıyoruz. Pencereyi oluşturmadan önce RegisterClass fonksiyonu ile sisteme tanıtıyoruz. PixelFormat nesnesi ile işletim sistemine kullanmak istediğimiz piksel formatını belirteceğiz. Eğer böyle bir format bulunursa sistem bize o formata ilişkin bir değeri döndürecek. dwExStyle ve dwStyle nesneleri de pencereye ilişkin stil bilgilerini tutacak. RECT yapısı türünden tanımladığımız WindowRect yapı nesnesini ise pencerenin genişlik ve yükseklik değerlerini belirtirken kullanacağız. WS_EX_WINDOWEDGE, pencereye biraz daha üç boyutlu bir görünüm kazandırır. WS_OVERLAPPEDWINDOW ise başlık çubuğu, minimize/maximize tuşları ve pencere menüsü olan bir pencereyi ifade eder. AdjustWindowRectEx fonksiyonu ile pencerenin stil ve özellik bilgileri ayarlanır. Eğer pencere oluşturulamamışsa !hWnd koşul bloğuna girilir, KillGLWindow fonksiyonu çağrılarak pencere yok edilir ve program sonlandırılır. PIXELFORMATDESCRIPTOR yapısı türünden tanımladığımız pfd isimli yapı nesnesi kullanmak istediğimiz piksel formatına ilişkin bilgileri tutacak. OpenGL uygulaması yapıtığımız için kullanacağımız formatın OpenGL’i desteklemesi gereklidir. Ayrıca yukarıda bahsettiğimiz "double buffering" tekniğini kullanacağımız için formatın bu tekniği de desteklemesi gereklidir. pfd yapı nesnesinin veri elemanlarına istediğimiz teknikleri destekleyen formata ilişkin bilgileri atadık. GetDC fonksiyonu ile, DeviceContext’e erişmeye çalışıyoruz. Eğer bu işlem başarılı olamazsa program sonlandırılmalıdır. Başarılı olursa buradan gelen değeri hDC isimli global nesnemize atıyoruz. DC başarıyla alındıktan sonra kullanmak istediğimiz piksel formatını sisteme belirtiyoruz. Eğer sistemimiz kullanmak istediğimiz piksel formatını desteklemiyorsa yine program sonlandırılmalıdır. Eğer kullanmak istediğimiz piksel formatı sistemimiz tarafından destekleniyorsa, bu formatı kullanmak istediğimizi SetPikselFormat fonksiyonu ile belirtiyoruz. Bu işlemin başarısız olması durumunda yine program sonlandırılıyor. wglCreateContext fonksiyonu ile RC’yi oluşturmaya çalışıyoruz. Bu işlem başarılı olursa geri dönen değer hRC isimli nesneye atanıyor. Başarısız olursa program sonlandırılıyor. Tüm bu işlemler başarıyla gerçekleşmişse RC ve DC’yi kullanmak için önce bunları wglMakeCurrent fonksiyonu ile aktif hale getirmeye çalışıyoruz. Başarısızlık durumunda yine program sonlandırılıyor. Bu işlemlerin herhangi birisinin başarısız olması durumunda programı sonlandırırken KillGLWindow isimli fonksiyonu çağırıyoruz. Eğer tüm bu adımlar geçilmişse, artık elimizde bir OpenGL penceresi vardır. Şimdi ShowWindow fonksiyonunu çağırarak bu pencereyi gösterebiliriz. SetForegroundWindow fonksiyonu pencerenin yüksek öncelikle gösterilmesini sağlar. SetFocus fonksiyonu ile, oluşturulan pencereye odaklanma sağlanır. ReSizeGLScene fonksiyonu ile penceremizi istediğimiz genişlik ve yüksekliğe sahip hale getiriyoruz. InıtGL fonksiyonu, OpenGL penceremiz oluştuğu zaman yapmak istediklerimizi belirttiğimiz bir fonksiyondur. Örneğin eğer biz OpenGL penceremizde çizeceğimiz şekillere bir doku oluşturmak istiyorsak (şekil yüzeylerini fotoğraflarla kaplama) bu işlem için kullanacağımız resimleri yüklememiz gereklidir. Bu işlemi de InitializeGL fonksiyonunda yapıyoruz. InitializeGL fonksiyonu OpenGL penceremiz için bir başlangıç fonksiyonu olarak düşünülebilir. Penceremizin genel özelliklerini, resim, doku döşeme gibi işlemler, görüntüye belli bir derinlik değeri verme gibi ayarlamalar bu fonksiyonda yapılarak başarısı kontrol edilir. Eğer bu işlemlerden birisinde başarısız olunmuşsa program sonlandırılır. Başlangıç için yapmak istediğimiz herhangi bir özel işlem yoksa bu fonksiyonu yazmayabiliriz.

Şimdi WinMain fonksiyonu içerisinden çağırdığımız fonksiyonların tanımlamalarına geçelim :

GLvoid KillGLWindow(GLvoid)
{
    if (hRC) {
       if (!wglMakeCurrent(NULL,NULL))
          MessageBox(NULL,"DC ve RC kapatılması sırasında hata oluştu","KAPATMA HATASI",MB_OK | MB_ICONINFORMATION);

       if (!wglDeleteContext(hRC))
          MessageBox(NULL,"Rendering Context kapatılamadı","KAPATMA HATASI",MB_OK | MB_ICONINFORMATION);

       hRC = NULL;
    }     if (hDC && !ReleaseDC(hWnd,hDC)) {
       MessageBox(NULL,"Device Context kapatılamadı","KAPATMA HATASI",MB_OK | MB_ICONINFORMATION);
       hDC = NULL;
    }

    if (hWnd && !DestroyWindow(hWnd)) {
       MessageBox(NULL,"hWnd kapatılamadı","KAPATMA HATASI",MB_OK | MB_ICONINFORMATION);
       hWnd = NULL;
    }

    if (!UnregisterClass("OpenGL",hInstance)) {
       MessageBox(NULL,"Class Unregister işlemi yapılamadı","KAPATMA HATASI",MB_OK | MB_ICONINFORMATION);
       hInstance = NULL;
    }
}

KillGLWindow isimli fonksiyon, oluşturulan pencereyi yok eden ve program sonlandırılmadan önce yapılması gereken birtakım ayarlamaları yapan fonksiyonumuzdur. Fonksiyon hiçbir parametre değişkeni almıyor ve geri dönüş değeri de yok. WinMain fonksiyonunda oluşturulan RC ve DC nesneleri program sonlandırılmadan önce yok edilmelidir. KillGLWindow fonksiyonunun ilk iki adımında bu işlemler yapılıyor. Tabii bu işlemleri yapmadan önce bu nesnelerin başarıyla oluştuurlup oluşturulmadığını if ifadeleri ile kontrol ediyoruz. wglMakeCurrent fonksiyonu ile RC ve DC nesnelerinin serbest bırakılıpğ bırakılamayacağı kontrol ediliyor. Eğer serbest bırakılabiliyorsa, wglDeleteContext fonksiyonu ile RC nesnesi siliniyor ve bu nesneyi gösteren hRC göstericisine NULL değeri atanıyor. İkinci adımda DC nesnesinin serbest bırakılıp bırakılamayacağı sorgulanıyor. Daha sonra pencereyi yok eden DestroyWindow fonksiyonu çağrılıyor ve sisteme RegisterClass fonksiyonu ile tanıttığımız pencere sınıfını unregisterClass fonksiyonu ile yok ediyoruz. Tüm bu işlemlerden sonra programımız artık başarıyla sonlanabilir.

BOOL DrawGLScene(GLvoid)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    return TRUE;
}
DrawGLScene fonksiyonunda, çizim işlemlerini yapan kodlar yer alır. Bu uygulamada herhangi bir çizim yapmıyoruz, sadece siyah ve boş bir OpenGL penceresi oluşturuyoruz. O nedenle bu fonksiyonda ekranı temizleyen glClear fonksiyonunu ve yapılan dönüşümleri geri alan glLoadIdentity fonksiyonunu çağırarak fonksiyondan TRUE değeri ile geri dönüyoruz.

int InitializeGL(GLvoid)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    return TRUE;
}
InitializeGL fonksiyonunda, penceremiz için başlangıç ayarlarını yapacağız. (Örneğin çizeceğimiz şekillere doku döşemek isteseydik kullanacağımız resimleri bu fonksiyonda yükleyebilir ve başarısını kontrol edebilirdik.) glClearColor fonksiyonu ile arka planı siyah renk yapıyoruz. glHint fonksiyonu ile pencerenin perspektifi ile ilgili bir takım düzeltmeler yapılıyor. Bu sayede görünümün kalitesi artıyor. Ancak bu işlem bir miktar performans düşüşüne sebep olabilir. Bu uygulama için bu performans düşmesi önemli boyutlarda değildir. InitializeGL fonksiyonunda bu ayarlamalardan başka birtakım derinlik ayarlamaları da yapılabilir. Bu uygulama için gerekli olmadığından bu konuyu ileriki bölümlere bırakıyoruz.

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)
{
    if (height == 0)
       height = 1;
    gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f);
}
ReSizeGLScene fonksiyonu ile pencerenin boyutları verilen değerlere göre ayarlanır. width parametre değişkeni pencerenin yeni genişlik değerini, height parametre değişkeni ise pencerenin yeni yükseklik değerini belirtir. Fonksiyonu başında height parametre değişkeninin değerinin sıfır olup olmadığı kontrol ediliyor, çünkü fonksiyonun ilerleyen adımlarında height değeri bölen olarak kullanılacak. gluPerspective fonksiyonu ile yeniden boyutlandırılan OpenGL penceresine bakış açısı ayarlanıyor. Pencerenin genişlik ve yükseklik değerlerine bağlı olarak 45 derecelik bir açıdan bakış sağlanıyor. Fonksiyonun son iki parametresi ile, pencereye ne kadar derinlikte çizim yapabileceğimizi belirten başlangıç ve bitiş noktası veriliyor. Pencereye olan bakışın değiştirilmesi işleminin daha ayrıntılı noktaları da vardır. Bu ayarlamaların hepsi daha iyi bir görünüm sağlamak içindir.

Son olarak uygulamamıza, gelen sistem mesajlarının işlenmesi işlemini yapan WndProc fonksiyonunu da ekliyoruz :

LRESULT CALLBACK WndProc (HWND hWnd,
                                        UINT uMsg,
                                        WPARAM wParam,
                                        LPARAM lParam)
{
    switch (uMsg) {
       case WM_CLOSE : {
          PostQuitMessage(0);
          return 0;
       }
    }
    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

WndProc fonksiyonunda, uMsg parametre değişkeninin değerini kontrol eden bir switch-case ifadesi oluşturduk. Bu parametre değişkeni gelen mesajlara ilişkin bir takım sembolik sabit değerlerine sahip olabilir. WndProc içerisinde işlemek istediğimiz mesajları bu şekilde yakalayabiliriz. Örneğin bir tuşa basıldığında ekran görünümüne ilişkin bir ayarlamayı değiştirmek istiyorsak, bu tuşa basılma mesajını bu fonksiyon içerisinde yakalayıp işleyebiliriz. Bu uygulamada biz sadece programdan çıkış mesajı verilip verilmediğini takip ediyoruz. Eğer programdan çıkış mesajı verilmişse uMsg nesnesi "WM_CLOSE" değerine sahip olacaktır. Bu durumda PostQuitMessage fonksiyonu ile çıkış mesajı gönderiyoruz. Bu fonksiyonda başka herhangi bir mesajı işlemek istemediğimiz için diğer mesajlar için DefWindowProc fonksiyonunu çağırıyoruz ve bu mesajların işlenmesini sisteme bırakıyoruz.

Tüm bu işlemlerden sonra uygulamamızı çalıştırdığımızda karşımıza siyah bir OpenGL penceresi çıkacaktır.



Penceremiz artık çizim işlemleri için bizi bekliyor. Bu işlemi başka bir yazımıza bırakıyorum ve mutlu günler diliyorum. Makaleye ait uygulamayı buradan indirebilirsiniz. (Örnek uygulama Microsoft Visual C++ 6.0’da hazırlanmıştır.)

* KAYNAKLAR :
  www.nehe.gamedev.net

Makale:
C ve OpenGL : Bir OpenGL Penceresi Oluşturmak C ve Sistem Programlama Çiğdem Çavdaroğlu
  • Yazılan Yorumlar
  • Yorum Yaz
Bu konu hakkında yayınlanan yorum bulunmamaktadır.
"Yorum Yaz" tabını kullanarak sizde yorumlarınızı yazabilirsiniz.
Yorum yazabilmek için üye girişi yapmalısınız. Üye girişi için tıklayın.
Üye değilseniz Üyel Ol linkine tıklayarak üyeliğinizi hemen başlatabilirisniz.
 
  • Bu Konuda Son 10
  • Eklenen Son 10
  • Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor