OpenGL'de Bezier Curve Yapımı
OpenGL'de Bezier Curve Yapımı
OpenGL_Ders_4.h
C/C++ kodu:
#ifndef INC_DERS4_H #define INC_DERS4_H #include "resource.h" #include <windows.h> #include <stdlib.h> #include <math.h> // matematik fonksiyonları için başlık dosyası #include <glgl.h> // OpenGL32 kütüphanesi icin baslik dosyası #include <glglu.h> // GLU32 kütüphanesi icin baslik dosyası #include <vector> #define EkranGenis 800 // Pixel bazında ana pencerenin Genisligi #define EkranYuksek 600 // Pixel bazından ana pencerenin yüksekliği #define EkranDerinlik 16 // her pixel icin kullanacagımız bit sayısı #define OpenGL_GENISLIK 800 // OpenGL in kendi boyutundaki genişliği #define OpenGL_YUKSEKLIK 600 // OpenGL in kendi boyutundaki yüksekliği // gerekli acıklama struct Vektor2f { float xy[2]; Vektor2f operator + (const Vektor2f °isken); Vektor2f operator - (const Vektor2f °isken); Vektor2f operator * (const Vektor2f °isken); }; extern HINSTANCE hInst; extern HDC hdc; extern HWND hWnd; extern HGLRC hrc; extern TCHAR szWindowClass[100]; extern RECT winRect; extern int TamEkran; extern POINT farePoz; extern Vektor2f KontNokta1; extern Vektor2f KontNokta2; extern bool AktifKntNokta; // Render fonksiyonun prototipi void Render(); // init ve deinit icin kullanılacak fonksiyonların prototipleri void EkranModunuDegistir(); bool PixelFormatAyarla(HDC hdc); void OpenGLEkranAyarla(int gen, int yuk); void OpenGLINIT(int gen, int yuk); void OpenGLDEINIT(); #endif
Render.h
C/C++ kodu:
#ifndef INC_RENDER_H #define INC_RENDER_H // bezier curve icin gerekli olan seyler 4 tane noktadan ibarettir. // 4 doğru ile eğri çizdirmek için birebir bir yöntem Vektor2f BasNokta; Vektor2f BitNokta; Vektor2f KontNokta1; Vektor2f KontNokta2; Vektor2f TmpnNokta; Vektor2f TmpnNokta1; POINT farePoz ; bool AktifKntNokta; std::vector <Vektor2f> Noktalar; #endif
Init.cpp
C/C++ kodu:
/* burda Başlangıçta kullanılacak fonksiyonlar ve yapılar mevcut olacaktır. NOT : Burdaki fonksiyonlar nadir olarak değişen fonksiyonlar olup bir çok tutorial da aynı formatta olacaktır.Eğer değişim olacaksa bu sayfanın başında değişiklik belli edilecektir. Init.cpp adındanda anlaşılacağı üzere initialize kısmıdır. Opengl için tüm initialize lar burda yapılıyor ve dediğim nadir olarak değişecektir. ******** Tüm Derslerde bu format kullanılacaktır. */ #include "Opengl_ders_4.h" // Ekran modunu, tam ekran seçilmiş ise değiştirmek için kullanılacak fonksiyon. void EkranModunuDegistir() { DEVMODE dmSettings; // Device Mode degiskeni memset(&dmSettings, 0, sizeof(dmSettings)); // Device Mode degiskenin hafizasını sıfırla // Su an geçerli olan ekran ayarları bilgisini dmSettings yapısına doldur,sırala. // ekran ayarlarından kasıt, ekran genişligi ,yüksekliği , monitor refresh rate vs. if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmSettings)) { // Eger fonksiyon başarısız ise MessageBox(NULL, "Ekran ayarları sıralanamadı", "Error", MB_OK); return; } // Belirledigimiz pencerenin genişlik ve yükseligini dmSettings yapısına ata dmSettings.dmPelsWidth = EkranGenis; dmSettings.dmPelsHeight = EkranYuksek; // Görüntü ayarlarını yeni atamış oldugumuz degerlerle birlikte degistir int result = ChangeDisplaySettings(&dmSettings, CDS_FULLSCREEN); if (result != DISP_CHANGE_SUCCESSFUL) { // Eger fonksiyon başarısız ise MessageBox(NULL, "Display Mode Not Compatible", "Error", MB_OK); PostQuitMessage(0); } } // Pixel formatını ayarlamak icin kullanılacak fonksiyon, device context icin bir pixel format secmemiz gerekiyor // önce pfd yapısını dolduruyoruz ve SetPixelFormat ile device contexte doldurdugumuz pixel formatı veriyoruz. bool PixelFormatAyarla(HDC hdc) { PIXELFORMATDESCRIPTOR pfd = { 0 }; int pixelformat; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; // her zaman 1 olacak pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; // Opengl icin default ayarlar pfd.dwLayerMask = PFD_MAIN_PLANE; // standart maskeleme icin, önemi yok pfd.iPixelType = PFD_TYPE_RGBA; // pixellerimiz Red Green Blue ve Alpha kanallı olacak pfd.cColorBits = EkranDerinlik; // Ekran derinligi,colorbits pfd.cDepthBits = 0; // RGBA oldugunda derinlik biti gözardı edilir pfd.cAccumBits = 0; pfd.cStencilBits = 0; // bizim belirrtiklerimze en uygun bi pixel format seç ChoosePixelFormat ile if ((pixelformat = ChoosePixelFormat(hdc, &pfd)) == FALSE) { MessageBox(NULL, "ChoosePixelFormat basarisiz", "Error", MB_OK); return FALSE; } // Yukarda uygun seçilen formatı hdc ile ilişkilendir if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE) { MessageBox(NULL, "SetPixelFormat basarisiz", "Error", MB_OK); return FALSE; } return TRUE; } // Opengl penceresinin boyutunun ayarlancagı fonksiyon void OpenGLEkranAyarla(int gen, int yuk) { if (yuk == 0) yuk = 1; glViewport(0, 0, gen, yuk); //viewport u ayarla glMatrixMode(GL_PROJECTION); // projection matriks i aktif matriks yap glLoadIdentity(); // aktif matriks i identity yap yani resetle glOrtho(0, gen, yuk, 0, 0, 1); // Clip olacak koordinatları belirle glMatrixMode(GL_MODELVIEW); // modelview matriksi aktif matriks yap glLoadIdentity(); // modelview matriks i resetle } void OpenGLINIT(int gen, int yuk) { hdc = GetDC(hWnd); // Device context kaynaklarda varsa al // bu Handle to Device Context i yani hdc i programın canlı oldugu sürece bırakmıyoruz. if (!PixelFormatAyarla(hdc)) // Pixel formatı ayarla PostQuitMessage(0); // error varsa, çık // Opengl için birde Rendering Context(hrc) gerekiyor , bunuda wglCreateContext ile yaratıyoruz. hrc = wglCreateContext(hdc); wglMakeCurrent(hdc, hrc); // Bu fonksyion ile su anki akif context i yarattigimiz hrc yap OpenGLEkranAyarla(gen, yuk); // Opengl icin gerekli olan ekran ayarlarını yap, viewport ve glOrtho } // Opengl ile ilgili sistemden aldıgımız kaynakların geri verilmesi void OpenGLDEINIT() { if (hrc) { wglMakeCurrent(NULL, NULL); wglDeleteContext(hrc); // Opengl in rendering context i sisteme geri kazandır } if (hdc) ReleaseDC(hWnd, hdc); // device context i sisteme geri kazandır ShowCursor(TRUE); // Mouse un Cursor u göster (önceden gizli ise) UnregisterClass(szWindowClass, hInst); // pencere classını bosalt PostQuitMessage(0); // pencereye quit mesajı at }
OpenGL_Ders_4.cpp
C/C++ kodu:
/* NOT : Bu sayfadaki fonksiyonları ve ana sistemi anlamak için win32 programlamaya giriş i okumanızı tavsiye ederiz. */ #include "stdafx.h" #include "Opengl_ders_4.h" ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); // Global olarak kullanılacak Değişkenler TCHAR szTitle[100]; // Başlık barındaki yazı TCHAR szWindowClass[100]; // ana pencernin class ismi //global nadir olarak değişen ve pencere ile alakalı değişkenler HINSTANCE hInst; HDC hdc; // Handle Device Context HWND hWnd;// Handle Window; HGLRC hrc; // Handle Rendering Context int TamEkran = 0;// Tam ekran mı değilmi, sadece init te kullanılmak üzere RECT winRect;// Pencerenin boyutlarını barındıran değişken, her pencerenin boyutu değiştiğinde bu değişkende güncellenir #pragma comment(lib, "opengl32.lib") // gerekli opengl32 kütüphanesini include ediyoruz #pragma comment(lib, "glu32.lib")// gerekli glu32 kütüphanesini include ediyoruz // winmain fonksiyonu int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { LoadString(hInstance, IDS_APP_TITLE, szTitle, 100); LoadString(hInstance, IDC_OPENGL_DERS_4, szWindowClass, 100); MyRegisterClass(hInstance); if (TamEkran)// tam ekransa ekran modunu baslangickodu.h da belirledigimiz degerlere dönüştür EkranModunuDegistir(); if (!InitInstance(hInstance, nCmdShow)) { return FALSE; } // pencerenin client bölügesinin koordinatlarını winRect rectangele ına doldur GetClientRect(hWnd, &winRect); // opengl ile alakalı tüm initialize lar burda OpenGLINIT(EkranGenis, EkranYuksek); MSG msg; // Ana oyun döngümüz, bu bölüm win32 e giriş dersinde anlatılmıştır. while (1) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message != WM_QUIT) { TranslateMessage(&msg); DispatchMessage(&msg); } else break; } else { Render(); } } return (int)msg.wParam; } // Pencereyi register etmek için kullanılan fonksiyon // .Net ortamında win32 project i olusturunca kendiliginden yaratılan fonksyion ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; hInst = hInstance; ZeroMemory(&wcex, sizeof(wcex)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_OPENGL_DERS_4); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = (LPCTSTR)IDC_OPENGL_DERS_4; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex); } // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // instance handle ını global olarak tanımladıgımız hInst degiskenine ata DWORD dwstyle; if (TamEkran) // eger tam ekransa pencerenin style nı assagidaki gibi ayarla, pop up window olsun dwstyle = WS_POPUPWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; else // eger tam ekran değilse pencerenin style nı overlapped window yap dwstyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // Ana pencereyi Yarat, belirttigimiz dwStyle ı burda kullanıcaz, 3. parametre // 1. parametre : pencerenin class ismi // 2.parametre : başlık olarak gözükecek olan char array // 3.parametre : bi önceki if durumunda belirttiğimiz dwStyle // 4.parametre : pencerenin Sol üst pozisyonun X koordinatı // 5.parametre : pencerenin Sol üst pozisyonun Y koordinatı // 6.parametre : pencrenin genişliği // 7.parametre : pencerenin yüksekliği hWnd = CreateWindow(szWindowClass, szTitle, dwstyle, 0, 0, EkranGenis, EkranYuksek, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); //pencereyi görülebilir yap UpdateWindow(hWnd); // pencereyi güncelle SetFocus(hWnd); // Klavye focusunu pencereye ver return TRUE; } // WndProc // //---------- // // Görev : Ana pencere için gelecek mesajları işlemek // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; LONG lRet = 0; POINT nokta; switch (message) { case WM_CREATE: // pencere yaratıldığında gelen mesajı burda yakalıyoruz MessageBox(NULL, "Pencere yaratıldı", "OK", NULL); break; case WM_SIZE: // pencerenin boyutu değiştiğinde ve ilk pencere yaratıldığında gelen mesaj // çünkü her pencere yaratılırken bir kere resize olur // Pencerenin yeni boyutunu winRect değişkenine doldur // Yalnız pencerenin client kısmını yani pencerenin ortasındaki boş alanı (Bu örnekte menünün altındaki alan) // Böylece herhangi bir çizim yaptıgımızda Y eksenini 0 aldıgımızda, menünün altındaki pixelden çizim başlıyacak. //yani menünün altındaki ilk pixel 0 koordinatına denk gelecektir. GetClientRect(hWnd, &winRect); break; case WM_MOUSEMOVE: // fare hareket ettiginde gelen mesajdır. // farenin pozsiyonunu al farePoz.x = LOWORD(lParam); farePoz.y = HIWORD(lParam); break; case WM_KEYDOWN: // Keyboard da herhangi bir tusa basınca gelen mesaj. switch (wParam) // wParam parametresi hangi tuşa basıldı ise o tuşun virtual key ini veriyor { case VK_SPACE:// isimden anlasıldıgı üzere space tuşuna basılınca burası işlicek // Her space tusuna basıldıgında AktifKntNokta degiskeni false ise true , true ise false olacak AktifKntNokta = !AktifKntNokta; // Bi sonrak kontrol noktasına zıplaması için if (AktifKntNokta) { farePoz.x = KontNokta1.xy[0]; farePoz.y = KontNokta1.xy[1]; } else { farePoz.x = KontNokta2.xy[0]; farePoz.y = KontNokta2.xy[1]; } // Farenin imlecini secilen kontrol noktasına koyuyoruz SetCursorPos(farePoz.x, farePoz.y); break; } break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case IDM_ABOUT: // önceden belirlenen About barına basınca yapılacaklar // Dialogue box yaratır, böylece hangi ders olduğunu okuyabilirsiniz. DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_EXIT: // programdan cıktıgımız zaman DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: // pencerenin tekrardan çizilmeye ihtiyacı duyulduğunda gelen mesaj hdc = BeginPaint(hWnd, &ps); // GDI kullanılarak yapılacak çizim işlemleri buraya EndPaint(hWnd, &ps); break; case WM_DESTROY: // pencere kapanmak üzere iken gelen mesaj OpenGLDEINIT(); // opengl in ayırdığı kaynakları geri verme zamanı break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } //Hakkinda kutusu icin mesaj işleyici, bi yukardaki ana pencereninki ile aynı formattadır // yukardaki hatırlıyacagımız üzere ana pencerenin mesaj işleyicisidir, bu ise sadece hakkında penceresi içindir. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; }
Render.cpp
C/C++ kodu:
#include "Opengl_ders_4.h" #include "Render.h" /* Opengl_ders_4.h dosyasında tanımladıgımız Vektor2f yapısının overloaded operatorlerini burda tnaımladık yani vektör toplama cıkarma ve carpma yapmak artık daha kolay olacak ÖRNEGIN : Vektor2f vek1 ; Vektor2f vek2 ; Vektor2f vek3 ; vek3.xy[0] = vek1.xy[0] + vek2.xy[0]; vek3.xy[1] = vek1.xy[1] + vek2.xy[1]; Yukardaki yerine alttaki cok daha kullanıslı ve aynı işi görecek vek3 = vek1 + vek2; */ Vektor2f Vektor2f::operator + (const Vektor2f °isken) { Vektor2f tmp; tmp.xy[0] = xy[0] + degisken.xy[0]; tmp.xy[1] = xy[1] + degisken.xy[1]; return tmp; } Vektor2f Vektor2f::operator - (const Vektor2f °isken) { Vektor2f tmp; tmp.xy[0] = xy[0] - degisken.xy[0]; tmp.xy[1] = xy[1] - degisken.xy[1]; return tmp; } Vektor2f Vektor2f::operator * (const Vektor2f °isken) { Vektor2f tmp; tmp.xy[0] = xy[0] * degisken.xy[0]; tmp.xy[1] = xy[1] * degisken.xy[1]; return tmp; } void BezierEgriDoldur(Vektor2f &Baslangic, Vektor2f &Bitis, Vektor2f &KontNokta1, Vektor2f &KontNokta2) { float cx = 3.0f * (KontNokta1.xy[0] - BasNokta.xy[0]); float bx = 3.0f * (KontNokta2.xy[0] - KontNokta1.xy[0]) - cx; float ax = BitNokta.xy[0] - BasNokta.xy[0] - cx - bx; float cy = 3.0f * (KontNokta1.xy[1] - BasNokta.xy[1]); float by = 3.0f * (KontNokta2.xy[1] - KontNokta1.xy[1]) - cy; float ay = BitNokta.xy[1] - BasNokta.xy[1] - cy - by; for (float i = 0.01f; i < 1.0f; i += 0.01f) { TmpnNokta.xy[0] = ax * i * i * i + bx * i * i + cx * i + BasNokta.xy[0]; TmpnNokta.xy[1] = ay * i * i * i + by * i * i + cy * i + BasNokta.xy[1]; Noktalar.push_back(TmpnNokta); } } // RENDER // //----------// /* GÖREV : Tüm çizim fonksiyonlarını barındıran ana fonksiyon NOT :Bundan sonra tüm OPENGL Derslerinde bu kod yapısı kullanılacak yani yeni şeyler bu yapının üstüne inşa edilecektir. */ void Render() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Ekran ve derinlik bufferını sıfırla glLoadIdentity(); // Aktif olan matrixi identity yap, yani resetle //Opengl icin ekranı sildigi renk, yani boyanmamıs alanın renk //kırmızı//yeşil//mavi//alfa glClearColor(0.0f, 0.0f, 1.0f, 0.0f); // Kontrol noktalarını görülebilecek boyuta getir glPointSize(8.0f); if (!TamEkran) // eger tam ekran ise her render da ortho ve viewport u güncellememize gerek yok // fakat pencere modda, pencerenin boyutu her degistiginde viewport ve glortho fonksiyonlarını //cagırmamız gerekiyor yoksa pencereyi genişlettigimizde çizimlerde genişler.Istenilen bir //durum değil tabiki Fakat şimdilik karısmasın diye her render da glOrtho ve glViewport fonksiyonlarını kullandım { OpenGLEkranAyarla(winRect.right, winRect.bottom); } // Egrinin baslangıc noktasının x ini pencerenin uzunlugunun 3 te birinden baslat // ve Baslangıc noktasının Y si ise pencerenin yüksekliginin yarısı kadar BasNokta.xy[0] = (float)(winRect.right / 3); BasNokta.xy[1] = (float)(winRect.bottom / 2); // Egrinin bitis noktasının x inide pencerenin 3 te 2 si kısmında bitir // Egrinin bitis noktasının y sini ise gene pencerenin yarısı kadar yap BitNokta.xy[0] = (float)(winRect.right / 3 * 2); BitNokta.xy[1] = (float)(winRect.bottom / 2); // Eger space tusuna basıldı ise AktifKntNokta degiskeni true ise false, false ise true olur // böylece her space tusuna basılınca diger aktif noktaya geçilir. if (AktifKntNokta) { KontNokta1.xy[0] = (float)farePoz.x; KontNokta1.xy[1] = (float)farePoz.y; } else { KontNokta2.xy[0] = (float)farePoz.x; KontNokta2.xy[1] = (float)farePoz.y; } BezierEgriDoldur(BasNokta, BitNokta, KontNokta1, KontNokta2); // Içinde bezier egrisine ait noktaları içeren Noktalar yapısını çizdir // Sırf noktalar icerdiginden line strip le cizmek mantıklı olur. glBegin(GL_LINE_STRIP); for (int i = 0; i < 100; i++) { glVertex2f(Noktalar[i].xy[0], Noktalar[i].xy[1]); } glEnd(); // Kontrol noktalarını cizdir glBegin(GL_POINTS); glVertex2f(KontNokta1.xy[0], KontNokta1.xy[1]); glVertex2f(KontNokta2.xy[0], KontNokta2.xy[1]); glEnd(); Noktalar.clear(); SwapBuffers(hdc); // arka bufferı öndeki ile degistiriyoruz, şu ana kadar arka buffer a yazdık //sonra onu ön buffer yapıp render ediyoruz, o render olurken arkadakini tekrar aynı //sekilde dolduruyoruz ve işlem böyle devam ediyor } /* Bezier eğrisi ismini Pierre Bezier tarafından almıştır. Şahısın 1970 yılında cad cam işlemleri için geliştirdigi basit bir kübik eşitliktir. Bu eşitlik sayesinde 4 nokta ile bir egri olusturabilioruz. 2 nokta baslangıc ve bitis noktası diger 2 nokta ise kontrol noktaları. Programda bu noktalarla egrinin nasıl yön degstirdigini görebilirsiniz. */
http://www.mediafire.com/download/imrlcrhimg5yjh9/OpenGL_Ders_4.rar
Levent A. Sevgili
Yorumlar
Yorum Gönder