OpenGL Oyunlarına giriş -1

bir üçgen çizip boyayarak OpenGl oyunlarına giriş yapıyoruz.

Untitled Document

OpenGL Oyunlarına giriş

Üç boyutlu oyun programlamaya girerken OpenGL ile başlamak daha mantıklı olacaktır. Çünkü DirectX ile oyun yazabilmek için öncelikle microsoft.com sitesinden DirectX SDK adı verilen ve genellikle 400-500 Mb boyutlarında olan dosyayı indirmek gerekiyor. OpenGL ise birçok dersleyici ile hazır gelmektedir. Ayrıca hazırladığınız OpenGL tabanlı oyun birkaç değişiklik ile daha sonra DirectX ile de kullanılabilmektedir. Bu sebeple ilk aşamada oyunlarımızı OpenGL ile yazacağız. OpenGL nin sorunu ise eski ekran kartları ile çalışmamasıdır, mutlaka 3D özelliği olan bir kart gerekir(En azından TNT veya Geforce ekran kartı olmalıdır) . DirectX ise çok eski kartlarla da çalışmasına rağmen siz programı hangi versiyonda yazdıysanız kullanıcı da directX'in o versiyonunu bilgisayarına yüklemek zorundadır. bu sebeple birçok oyun CDsi içersinde DirectX'in son versiyonu CD içinde verilir.

Buradaki Derse başlamadan önce ilk dersleri iyice inceleyin ve C programlama hakkında bilgi edinin, aksi halde buradan da birşey anlamazsınız.

Ayrıca MFC programcılığında uzman olsanız bile herşeye tam anlamı ile hakim olduğunuz bir oyun yazmak için MFC programlarını da bir kenara bırakmalısınız. (internette MFC ile yazılmış oyun örnekleri bulabilirsiniz ama ben MFC kullanmadan yazmanızı tavsiye ederim)

Başlık Dosyaları:

openGL ile program yazmak için gerekli olan dosyalar Visual C kurulu olan dizinde include/gl dizini içindedir. buradaki komutları programınızda kullanabilmek için #include komutu ile iki dosyayı programam dahil etmelisiniz.

#include <gl/gl.h>
#include <gl/glu.h>

bunun yanında library(kütüphane) dosyalarını da projeye link(bağlama) işlemini de unutmamalısınız. opengl32.lib ve glu32.lib dosyalarını
Project>>Settings>>Link kısmından eklemelisiniz. Bunları openGL komutlarını kullanabilmek için yapmak zorundasınız.

OpenGL ile işlemleri yaparken hdc=GetDC(hwnd); komutu ile çizim işlemlerin başlayacağız. daha önceki derslerde bu komutu incelemiştik. işlemler bittikten sonra ReleaseDC(hwnd,hdc); komutu ile kapatacağız.

Not: bazı ekran kartlarında penceretipi(wc) isimli değişken içersine CS_OWNDC parametresini girmek gerekmektedir. ( Ati Rage Pro, RivaTNT gibi.)
penceretipi.style =CS_OWNDC ;
veya bazı örneklerde şöyle yazmıştık wc.style

Pixel Format

Oyunu kodlamaya geçmeden önce tanımlamak zorunda olduğumuz bir değişken daha var. PixelFormat bu değişken az önce aldığımız DC'yi nasıl kullanacağımızı sisteme bildiriyor. Oyun hastaları şu özellikleri belki dumuştur; double buffering, z-buffer, color format alpha buffer... bu tip özellikleri bu değişken ile tanımlıyoruz.

PIXELFORMATDESCRIPTOR pixelbilgisi; //değişken tanımlama
ZeroMemory( &pixelbilgisi, sizeof( pixelbilgisi ) );

pixelbilgisi.nSize = sizeof( pixelbilgisi ); //değişken içersine bilgilerin doldurulması
pixelbilgisi.nVersion = 1;
pixelbilgisi.dwFlags =PDF_DRAW_TO_WINDOW | PDF_SUPPORT_OPENGL | PDF_DOUBLEBUFFER;
pixelbilgisi.iPixelType = PDF_TYPE_RGBA;
pixelbilgisi.cColorBits = 24;
pixelbilgisi.cDepthBits = 16;
pixelbilgisi.iLayerType = PDF_MAIN_PLANE;

int iFormat = ChoosePixelFormat( hdc, &pixelbilgisi ); //hdc için en uygun pixel formatı belirleniyor
SetPixelFormat( hDC, iFormat, &pixelbilgisi );

Double Buffer

Double buffer kullandığımızda Dc içersine çizdiğimiz herşey hafızada görünür olmayan bir bölgeye gider. PDF_DOUBLEBUFFER ile bunu yukarıda tanımlamış oluyoruz. Biz programa göster diyene kadar pencere üzerinde gösterilmez. Böylece sahneyi görünmeden değiştirip render işlemleri yapılır ve işlemlerin son hali kullanıcıya gösterilir.bunu tek bir fonksiyonla yaparız SwapBuffers(hdc); böylece son görüntü pencere üzerine aktarılır.

Render Context

Oyun programına geçmeden önce öğreneceğimiz çok az fonksiyon kaldı. Sıradaki işlem renderContext hazırlamak (RC). win32 ortamındaki herşey gibi bunu da handle ile kontrol edeceğiz. biraz farklı olarak HGLRC tipinde bir değişken ile tanımlanıyor. Bu değişken OpenGL ile programımız arasında köprü vazifesi yapacaktır.

HGLRC hrc;
hrc = wglCreateContext(hdc);

Bu komut ile önceden aldığımız hdc ile ilişkili bir RC oluşturuluyor. RC ile işimiz bittikten sonra program kapanırken hafızadan silmek için wglDeleteContext komutunu kullanıyoruz.

wglDeleteContext(hrc);

aynı anda birden fazla OpenGL uygulaması çalışıyor olabilir bu durumda kullandığımız programdaki RC'y iaktif yapmak için wglMakeCurrent komutu kullanılıyor.

wglMakeCurrent(hdc,hrc);

OpenGl ile ilgili tüm işlemleri ve fonksiyonları kullandıktan sonra Render Context değişkenini diğerlerinin de kullanabilmesi için wglMakeCurrent komutunu tekrar kullanıyoruz. tabii bu sefer parametresi boş olmalı.

wglMakeCurrent(NULL,NULL);

Eğer bizim programdan önce OpenGL render işlemini hangi programın kullandığını bilmek isterseniz. wglCurrentDC ve wglCurrentContext komutları ile o andaki bilgileri eskiDC ve eskiRC isimli iki değişken içersinde saklayabilirsiniz.

wglGetCurrentContext.
HDC eskihDC = wglGetCurrentDC();
HGLRC eskihRC = wglGetCurrentContext();
wglMakeCurrent( hdc, hrc );
//RC yi ele geçirdiğimize göre komutları buraya yazıyoruz

işimiz bittikten sonra OpenGL sistemini bir önceki uygulamaya geri vermek için yine makeCurrent komutunu kullanıp az önce aldığımız eski isimli değişkenleri kullanıyoruz.

wglMakeCurrent(eskihDC,eskihRC);

bu sisteme aynı anda birden fazla openGL içeren sistem kullanıyorsanız ihtiyacınız olacak. aksi halde NULL diyip gerisine karışmazsınız. örnek olarak aynı anda ekranda iki oyuncunun da kendi arabasını yarım ekranda sürdüğü görüntüyü veren NeedForSpeed oyununu verebiliriz.

Hepsini Toparlayalım

Öğrendiğimiz tüm değişkenleri kullanarak pencerede OpenGL renderini aktif etmek ve iptal etmek için iki fonksiyon yazabiliriz.

void OpenGL_aktif(HWND hWnd, HDC * hDC, HGLRC * hRC)
{
PIXELFORMATDESCRIPTOR pixelbilgisi;
int iFormat;

// pencerenin çizim alanı yakalanıyor
*hDC = GetDC( hWnd );

// pixel bilgisi değişkeni tanımlanıyor
ZeroMemory( &pixelbilgisi, sizeof( pixelbilgisi ) );
pixelbilgisi.nSize = sizeof( pixelbilgisi );
pixelbilgisi.nVersion = 1;
pixelbilgisi.dwFlags = PDF_DRAW_TO_WINDOW | PDF_SUPPORT_OPENGL | PDF_DOUBLEBUFFER;
pixelbilgisi.iPixelType = PDF_TYPE_RGBA;
pixelbilgisi.cColorBits = 24;
pixelbilgisi.cDepthBits = 16;
pixelbilgisi.iLayerType = PDF_MAIN_PLANE;
iFormat = ChoosePixelFormat( *hDC, &pixelbilgisi );
SetPixelFormat( *hDC, iFormat, &pixelbilgisi );

// RC oluşturulup aktif render atanıyor
*hRC = wglCreateContext( *hDC );
wglMakeCurrent( *hDC, *hRC );
}

 
//openGL renderini iptal eden fonksiyon
void OpenGL_iptal(HWND hWnd, HDC hDC, HGLRC hRC)
{
wglMakeCurrent( NULL, NULL );
wglDeleteContext( hRC );
ReleaseDC( hWnd, hDC );
}

Böylece OpenGL_aktif ve OpenGL_iptal isimli iki fonksiyon arasına istediğimiz tüm GL fonksiyonlarını yazabiliriz. DoubleBuffer sistemini hatırlayın. SwapBuffers komutunu da kullanmamız gerekiyor. Aşağıdaki uygulama ile öğrendiklerimizi kullanalım.

 

 

#define WIN32_LEAN_AND_MEAN //kütüphaneleri temizler // bunları include etmek zorunlu #include #include #include #include //yardımcı fonksiyonlar //render fonksiyonu tanımlaması void RenderScene(); //döndürme için kullanacağımız açı float angle = 0.0f; // genel DC tanımlaması HDC HDC hdc; // pixel formatını tanımlamak için fonksiyon kullanabiliriz. void SetupPixelFormat(HDC hDC) { int nPixelFormat; static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // yapının boyu 1, // herzaman 1 PFD_DRAW_TO_WINDOW | // pencere desteği PFD_SUPPORT_OPENGL | // OpenGl desteği PFD_DOUBLEBUFFER, // Double Buffer desteği PFD_TYPE_RGBA, // RGBA desteği 32, // 32 bit renk modu 0, 0, 0, 0, 0, 0, // renk bit'lerini önemseme 0, // alpha buffer yok 0, // değişen bit'leri önemseme 0, // yığılma buffer yok 0, 0, 0, 0, // yığılma bit'lerini önemseme 16, // depth buffer bit sayısı 0, // stencil buffer bit sayısı 0, // auxiliary buffer yok PFD_MAIN_PLANE, // Ana çizim yüzeyi 0, // ayrılmış alan 0, 0, 0 }; // layer maskeleri önemsenmiyor // en uygun pixel formatını seç nPixelFormat = ChoosePixelFormat(hDC, &pfd); // hdc ile pixel formatını ilişkilendir. SetPixelFormat(hDC, nPixelFormat, &pfd); // Buradaki değerlerin hepsini bilmek zorunda değilsiniz. //sadece programa bu fonksiyonu ekleyin yeter } // Burada pencere hazırlanması var, bunları önceden öğrendiğiniz için açıklama yazmıyorum LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HGLRC hRC; // Render context. hrc static HDC hDC; // Device context. hdc int width, height; //pencere eni ve boyu switch(message) { case WM_CREATE: // pencere oluşturuluyor hDC = GetDC(hwnd); // pencereden çizim alanı çağrılıyor hdc = hDC; // genel dc ile eşitleniyor SetupPixelFormat(hDC); // yukardaki pixelFormat fonksiyonuna yollanıyor hRC = wglCreateContext(hDC); // Render Context oluşturuluyor wglMakeCurrent(hDC, hRC); // RC geçerli pencere oalrak atanıyor return 0; break; case WM_SIZE: // pencere boyu değişiyor height = HIWORD(lParam); // pencere boyunu al width = LOWORD(lParam); // pencere enini al if(height==0) // boy =0 olursa boy=1 yap { height = 1; } glViewport(0, 0, width, height); // görünümü yeni boya göre ayarla glMatrixMode(GL_PROJECTION); // çizim alanını ayarla glLoadIdentity(); // görünüm alanını sıfırla // pencere görünümünü hesapla gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 1.0f, 1000.0f); glMatrixMode(GL_MODELVIEW); // çizim alanını ayarla glLoadIdentity(); // görünüm alanını sıfırla return 0; break; case WM_CLOSE: // pencere kapanıyor mesajı wglMakeCurrent(hDC, NULL); wglDeleteContext(hRC); // render context siliniyor PostQuitMessage(0); // pencereyi imha etme mesajı atılıyor return 0; break; default: break; } // kullanılmayan mesajları olduğu gibi geri yolla return (DefWindowProc(hwnd, message, wParam, lParam)); } // an döngü ve pencere oluşturma int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { MSG msg; // mesaj değişkeni WNDCLASSEX penceretipi; // pencere tipini belirlerken kullanılacak değşken HWND hwnd; // pencere için kulp(tutamaç,sap) bool bittiMi; // program bitti penceretipi.cbSize = sizeof(WNDCLASSEX); penceretipi.style = CS_HREDRAW | CS_VREDRAW; penceretipi.lpfnWndProc = WndProc; penceretipi.cbClsExtra = 0; penceretipi.cbWndExtra = 0; penceretipi.hInstance = hInstance; penceretipi.hIcon = LoadIcon(NULL, IDI_APPLICATION);//pencere ikonu penceretipi.hCursor = LoadCursor(NULL, IDC_ARROW);//fare ok işareti tipi penceretipi.hbrBackground = NULL; penceretipi.lpszMenuName = NULL; penceretipi.lpszClassName = "tr3d"; // değişkenin sınıf adı penceretipi.hIconSm = LoadIcon(NULL, IDI_APPLICATION);//minik ikon if(!RegisterClassEx(&penceretipi)) return 0; hwnd = CreateWindowEx(NULL, "tr3d", //pencere tipini belirten sınıf ; yukardaki "ilk openGL programı",// pencere başlığı WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 100, 100, 400, 400, NULL, NULL, hInstance, NULL); // pencere oluşturmada hata varsa, programı kapat if(!hwnd) return 0; ShowWindow(hwnd, SW_SHOW); // oluşturulan pencereyi göster UpdateWindow(hwnd); // boyama işlemi yap bittiMi = false; while(!bittiMi) //program hala çalışıyor { PeekMessage(&msg, hwnd, NULL, NULL, PM_REMOVE); if(msg.message == WM_QUIT) // program kapatma mesajı verirse { bittiMi = true; // programı sonlandır } else // aksi halde render işlemi yap { RenderScene(); //render işlemleri için fonksiyon, aşağıda TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } // render işlemi : saniyede 25 veya daha fazla kez çalışıyor //böylece yeniden yeniden çizilen şekiller haraketli gibi görünüyor void RenderScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // öncelikle ekranı temizliyoruz glLoadIdentity(); //model görünümünü sıfırlıyoruz angle = angle + 0.1f; // bu kullanacağımız açı değişkeni // ekrana gelen her karede bir artıyor if(angle >= 360.0f) // açı 360 dan büyükse angle = 0.0f; // sıfıra dön glTranslatef(0.0f, 0.0f, -5.0f); // çizime başlamadan önce 5 birim arkaya git glRotatef(angle, 0.0f, 0.0f, 1.0f); // angle isimli değişken kadar çevirme işlemi yap // üçgen çizmek için üç nokta gerekiyor // glBegin/glEnd() arasına yazdıklarımız çizim olarak karşımıza çıkıyor glBegin(GL_TRIANGLES); // üçgen çizmeye başla glColor3f(0.0f, 0.0f, 1.0f); // ilk nokta rengi (mavi) glVertex3f(0.0f, 1.0f, 0.0f); // ilk noktanın yeri glColor3f(1.0f, 0.0f, 0.0f); // ikinci nokta (kırmızı) glVertex3f(1.0f, 0.0f, 0.0f); // ikinci noktanın yeri glColor3f(1.0f, 1.0f, 1.0f); // üçüncü nokta (beyaz) glVertex3f(-1.0f, 0.0f, 0.0f); // üçüncü noktanın yeri glEnd(); // çizim işlemini bitir SwapBuffers(hdc); //buffer'leri çevirip çizdiklerimizi ekranda göster } // OpenGL ilk başta zor görülebilir fakat devamlı kullanılan özelliklerine alıştıktan sonra // eğlenceli programlar yazmaya başlayacaksınız

Konuyu anladık mı ?
Uygulama sorusu: Üçgenin boyunu büyütüp rengini tamamen sarı yapmayı deneyin, dönme hızını beş kat arttırın.

 

Ekleyen: drekon

Kapalı

Topic closed automatically because it`s been more than a year!