Janusz Ganczarski OpenGL Definiowanie sceny 3D Spis treÅ›ci Spis treÅ›ci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1. Definiowanie sceny 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1. Obszar renderingu . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.1. Plik kwadrat2.cpp . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2. Rzutowanie prostokÄ…tne . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.1. Plik szescian1.cpp . . . . . . . . . . . . . . . . . . . . . . . . 7 1.3. Rzutowanie perspektywiczne . . . . . . . . . . . . . . . . . . . . . . 10 1.3.1. Plik szescian2.cpp . . . . . . . . . . . . . . . . . . . . . . . . 13 1.3.2. Plik szescian3.cpp . . . . . . . . . . . . . . . . . . . . . . . . 16 1.4. PoÅ‚ożenie obserwatora . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.1. Plik szescian4.cpp . . . . . . . . . . . . . . . . . . . . . . . . 21 Literatura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 1. Definiowanie sceny 3D W kolejnych programach korzystajÄ…cych z biblioteki OpenGL zajmie- my siÄ™ podstawowymi zagadnieniami, zwiÄ…zanymi ze scenÄ… 3D: obszarem renderingu, rzutowaniem i poÅ‚ożeniem obserwatora. Informacje tu zawarte stanowiÄ… podstawÄ™ do wszystkich nastÄ™pnych programów. 1.1. Obszar renderingu W pierwszym programie obszar renderingu, który zajmowaÅ‚ poczÄ…tko- wo caÅ‚e okno, nie byÅ‚ modyfikowany podczas zmiany rozmiarów tego okna. W efekcie jedyny element sceny 3D - kwadrat - zawsze znajdowaÅ‚ siÄ™ w tym samym miejscu wzglÄ™dem lewego dolnego narożnika okna. W aplikacjach pracujÄ…cych w systemach okienkowych problem zmiany rozmiaru okna jest jednak tak powszechny, że wymaga specjalnego potrak- towania. Jednym z możliwych sposobów jego rozwiÄ…zania jest dynamiczna modyfikacja modyfikacja obszaru renderingu. SÅ‚uży to tego funkcja: void glViewport (GLint x, GLint y, GLsizei width, GLsizei height) której parametry oznaczajÄ…: x, y- współrzÄ™dne lewego dolnego narożnika obszaru renderingu wzglÄ™- dem lewego dolnego narożnika okna, width- szerokość okna renderingu, height- wysokość okna renderingu. DomyÅ›lnie obszar renderingu zajmuje caÅ‚e okno udostÄ™pnione dla aplika- cji OpenGL. W naszym drugim programie w trakcie zmiany rozmiaru okna (funkcjaReshape) bÄ™dziemy modyfikować obszar renderingu na dwa spo- soby. Pierwszy polega na objÄ™ciu obszarem renderingu caÅ‚ego dostÄ™pnego okna, drugi na takim wyborze okna renderingu aby okno zachowaÅ‚o pier- wotny aspekt obrazu, czyli stosunek szerokoÅ›ci do wysokoÅ›ci. OczywiÅ›cie przy zastosowaniu pierwszej metody kwadrat bÄ™dzie zazwyczaj zdeformo- wany (patrz rysunki 1 i 2). Zmiany sposobu definiowania okna renderin- gu można dokonać w dowolnym momencie, poprzez wybranie odpowiedniej opcji w menu podrÄ™cznym. W funkcjiMenuzostaÅ‚a użyta do tej pory nieopisana funkcja z biblioteki GLUT: int glutGet (GLenum type) O tym jakiego rodzaju informacje zwróci funkcjaglutGetdecyduje para- metrtype. W przykÅ‚adowym programie sÄ… to szerokość i wysokość okna, co odpowiada parametrom opisanym staÅ‚ymiGLUTWINDOWWIDTHiGLUTWIN- DOWHEIGHT. 1. Definiowanie sceny 3D 2 Warto jeszcze kilka słów poÅ›wiÄ™cić zagadnieniu aspektu obrazu. Typowe monitory komputerowe posiadajÄ… aspekt 4:3, który jest zgodny z wiÄ™kszoÅ›ciÄ… popularnych rozdzielczoÅ›ci roboczych (np. 640 × 480, 800 × 600, 1.024 × 768, 1.600 × 1.200), ale inna popularna rozdzielczość 1.280 × 1.024 pikseli odpowiada aspektowi 5:4. Rysunek 1. Programu Kwadrat 2 - rendering na caÅ‚ym oknie Rysunek 2. Programu Kwadrat 2 - rendering z zachowaniem aspektu 1:1 1.1.1. Plik kwadrat2.cpp /" ( c ) J a n u s z G a n c z a r s k i h t t p : / / www . j a n u s z g . hg . p l J a nu s zG @ e nter . n e t . p l "/ #include #include < s t d l i b . h> // s t a Å‚ e do o b s Å‚ u g i menu p o d r Ä™ c z n e g o enum { FULL WINDOW, // o b s z a r r e n d e r i n g u - c a Å‚ e okno ASPECT 1 1 , // o b s z a r r e n d e r i n g u - a s p e k t 1 : 1 1. Definiowanie sceny 3D 3 EXIT // w y j Å› c i e } ; // a s p e k t o b r a z u i n t Aspe ct = FULL WINDOW; // f u n k c j a g e n e r u j Ä… c a s c e n Ä™ 3D void D i s p l a y ( ) { // k o l o r t Å‚ a - z a w a r t o Å› ć b u f o r a k o l o r u g l C l e a r C o l o r ( 1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ; // c z y s z c z e n i e b u f o r a k o l o r u g l C l e a r (GL COLOR BUFFER BIT ) ; // k o l o r k w a d r a t u g l C o l o r 3 f ( 1 . 0 , 0 . 0 , 0 . 0 ) ; // p o c z Ä… t e k d e f i n i c j i w i e l o k Ä… t a g l B e g i n (GL POLYGON ) ; // k o l e j n e w i e r z c h o Å‚ k i w i e l o k Ä… t a g l V e r t e x 3 f ( 0 . 0 , 0 . 0 , 0 . 0 ) ; g l V e r t e x 3 f ( 0 . 0 , 1 . 0 , 0 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 0 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 0 . 0 , 0 . 0 ) ; // k o n i e c d e f i n i c j i p r y m i t y w u glEnd ( ) ; // s k i e r o w a n i e p o l e c e Å„ do w y k o n a n i a g l F l u s h ( ) ; // zamiana b u f o r ó w k o l o r u g l u t S w a p B u f f e r s ( ) ; } // zmiana w i e l k o Å› c i okna void Reshape ( i n t width , i n t h e i g h t ) { // o b s z a r r e n d e r i n g u - a s p e k t 1 : 1 i f ( Aspect == ASPECT 1 1 ) { // s z e r o k o Å› ć okna w i Ä™ k s z a od w y s o k o Å› c i okna i f ( width > h e i g h t ) g l V i e w p o r t ( ( width - h e i g h t ) / 2 , 0 , h e i g h t , h e i g h t ) ; e l s e // w y s o k o Å› ć okna w i Ä™ k s z a od w y s o k o Å› c i okna i f ( width < h e i g h t ) g l V i e w p o r t ( 0 , ( h e i g h t - width ) / 2 , width , width ) ; } e l s e // o b s z a r r e n d e r i n g u - c a Å‚ e okno ( t a k ż e , g d y a s p e k t w y n o s i 1 : 1 ) g l V i e w p o r t ( 0 , 0 , width , h e i g h t ) ; // g e n e r o w a n i e s c e n y 3D D i s p l a y ( ) ; } // o b s Å‚ u g a menu p o d r Ä™ c z n e g o void Menu ( i n t v a l u e ) { switch ( v a l u e ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno case FULL WINDOW: Aspect = FULL WINDOW; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; // o b s z a r r e n d e r i n g u - a s p e k t 1 : 1 case ASPECT 1 1 : Aspect = ASPECT 1 1 ; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; 1. Definiowanie sceny 3D 4 // w y j Å› c i e case EXIT : e x i t ( 0 ) ; } } i n t main ( i n t a r gc , char " a r g v [ ] ) { // i n i c j a l i z a c j a b i b l i o t e k i GLUT g l u t I n i t (& argc , a r g v ) ; // i n i c j a l i z a c j a b u f o r a r a m k i g l u t I n i t D i s p l a y M o d e (GLUT DOUBLE | GLUT RGB ) ; // r o z m i a r y g Å‚ ó w n e g o okna programu g l u t I n i t W i n d o w S i z e ( 4 0 0 , 4 0 0 ) ; // u t w o r z e n i e g Å‚ ó w n e g o okna programu glutCreateWindow ( Kwadrat 2 ) ; // d o Å‚ Ä… c z e n i e f u n k c j i g e n e r u j Ä… c e j s c e n Ä™ 3D g l u t D i s p l a y F u n c ( D i s p l a y ) ; // d o Å‚ Ä… c z e n i e f u n k c j i w y w o Å‚ y w a n e j p r z y z m i a n i e r o z m i a r u okna glutRe shapeFunc ( Reshape ) ; // u t w o r z e n i e menu p o d r Ä™ c z n e g o glutCreateMenu ( Menu ) ; // d o d a n i e p o z y c j i do menu p o d r Ä™ c z n e g o #i f d e f WIN32 glutAddMenuEntry ( Obszar r e n d e r i n g u - c a Å‚ e okno ,FULL WINDOW ) ; glutAddMenuEntry ( Obszar r e n d e r i n g u - a s p e k t 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j Å› c i e ,EXIT ) ; #e l s e glutAddMenuEntry ( Obszar r e n d e r i n g u - c a l e okno ,FULL WINDOW ) ; glutAddMenuEntry ( Obszar r e n d e r i n g u - a s p e k t 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j s c i e ,EXIT ) ; #end if // o k r e Å› l e n i e p r z y c i s k u m y s z k i o b s Å‚ u g u j Ä… c e j menu p o d r Ä™ c z n e glutAttachMenu (GLUT RIGHT BUTTON ) ; // w p r o w a d z e n i e programu do o b s Å‚ u g i p Ä™ t l i k o m un i kat ów glutMainLoop ( ) ; return 0 ; } 1.2. Rzutowanie prostokÄ…tne Rzutowaniem okreÅ›lamy odwzorowanie zawartoÅ›ci trójwymiarowej sceny graficznej na pÅ‚askim ekranie monitora. Biblioteka OpenGL oferuje standar- dowo dwie metody rzutowania: rzutowanie prostokÄ…tne i rzutowanie perspek- tywiczne. DomyÅ›lnie stosowane jest rzutowanie prostokÄ…tne. W rzutowaniu prostokÄ…tnym (lub ortogonalnym) proste rzutowania sÄ… prostopadÅ‚e do rzutni, która jest reprezentowana przez obszar renderingu. Z rzutowaniem prostokÄ…tnym Å›ciÅ›le zwiÄ…zane jest pojÄ™cie bryÅ‚y odcinania - prostopadÅ‚oÅ›cianu, który stanowi ograniczenie sceny 3D. Obiekty znajdujÄ…ce siÄ™ poza bryÅ‚Ä… odcinania nie sÄ… rysowane, a obiekty jÄ… przecinajÄ…ce rysowane sÄ… tylko częściowo. 1. Definiowanie sceny 3D 5 Rozmiar bryÅ‚y odcinania w rzutowaniu prostokÄ…tnym okreÅ›la funkcja: void glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) której parametry okreÅ›lajÄ… współrzÄ™dne punktów przeciÄ™cia pÅ‚aszczyzn two- rzÄ…cych bryÅ‚Ä™ odcinania z osiami ukÅ‚adu współrzÄ™dnych kartezjaÅ„skich. PÅ‚asz- czyzny te opisane sÄ… nastÄ™pujÄ…cymi równaniami: x = right x = left y = top y = bottm z = -near z = -far Obszar renderingu zawiera siÄ™ w pÅ‚aszczyznie o równaniu z = -near. PoÅ‚ożenie poszczególnych pÅ‚aszczyzn tworzÄ…cych bryÅ‚Ä™ odcinania przedsta- wia rysunek 3. Trzeba jednak wyraznie zwrócić uwagÄ™, że poczÄ…tek ukÅ‚adu współrzÄ™dnych nie musi znajdować siÄ™ wewnÄ…trz bryÅ‚y odcinania - rozmiary i poÅ‚ożenie bryÅ‚y ograniczone sÄ… jedynie zakresem stosowanych liczb. Y top -far left right X -near Z bottom Rysunek 3. PoÅ‚ożenie pÅ‚aszczyzn bryÅ‚y odcinania w rzutowaniu prostokÄ…t- nym 1. Definiowanie sceny 3D 6 DomyÅ›lnie bryÅ‚a odcinania ma postać szeÅ›cianu o bokach równych 2, któ- rego Å›rodek pokrywa siÄ™ z poczÄ…tkiem ukÅ‚adu współrzÄ™dnych, co odpowia- da wywoÅ‚aniu funkcjiglOrtho (-1,1,-1,1,-1,1). OÅ› OZ jest prostopadÅ‚a do pÅ‚aszczyzny obszaru renderingu i przechodzi przez Å›rodek tego obsza- ru. Dlatego rysowany w pierwszym i drugim programie kwadrat zajmowaÅ‚ poczÄ…tkowo czwartÄ… część okna. FunkcjaglOrthotworzy macierz rzutu prostokÄ…tnego: ëÅ‚ öÅ‚ right+left 2 0 0 right-left right-left ìÅ‚ ÷Å‚ top+bottom 2 ìÅ‚ ÷Å‚ 0 0 ìÅ‚ top-bottom top-bottom ÷Å‚ ìÅ‚ ÷Å‚ far+near -2 0 0 íÅ‚ Å‚Å‚ far-near far-near 0 0 0 1 która jest nastÄ™pnie mnożona przez bieżącÄ… macierz i umieszczona na szczy- cie stosu z bieżącÄ… macierzÄ…. OpenGL zawiera kilka stosów macierzy, z któ- rych w przykÅ‚adowym programie wykorzystamy stos macierzy rzutowania oraz stos macierzy modelowania. Wybór bieżącej macierzy umożliwia funk- cja: void glMatrixMode (GLenum mode) gdzie parametrmodemoże przyjąć jednÄ… z wartoÅ›ci: GLMODELVIEW- macierz modelowania, GLPROJECTION- macierz rzutowania, GLTEXTURE- macierz tekstury (omówiona pózniej). Ponieważ poczÄ…tkowa wartość wybranej macierzy jest nieokreÅ›lona, przed wywoÅ‚aniemglOrthonależy bieżącej macierzy przyporzÄ…dkować macierz jednostkowÄ…. NajÅ‚atwiej można to zrobić używajÄ…c funkcji: void glLoadIdentity (void) Analogiczne postÄ™powanie dotyczy macierzy modelowania. Po jej wyborze przykÅ‚adowym programie (plikszescian1.cpp) w funkcjiDisplaymacierzy modelowania także przyporzÄ…dkowywana jest macierz jednostkowa. Jeżeli renderowana scena jest dwuwymiarowa, do ustawienia parametrów bryÅ‚y odcinania w rzutowaniu prostokÄ…tnym można użyć funkcji z biblioteki GLU: void gluOrtho2D (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top) której parametryleft,right,bottomitopodpowiadajÄ… parametrom funk- cji glOrtho, a przednia (near) i tylna (far) pÅ‚aszczyzna obcinania majÄ… wartoÅ›ci odpowiednio -1 i 1. 1. Definiowanie sceny 3D 7 W przykÅ‚adowym programie poczÄ…tkowa bryÅ‚a odcinania ma postać sze- Å›cianu o krawÄ™dziach dÅ‚ugoÅ›ci 4, a rysowana figura - także szeÅ›cian - ma krawÄ™dzie o dÅ‚ugoÅ›ci 2. Centralne umieszczenie rysowanego szeÅ›cianu w po- Å‚Ä…czeniu z zastosowanym rzutowaniem prostokÄ…tnym daje w efekcie kwadrat (patrz rysunek 4). Podobnie jak w poprzednim programie możliwy jest wy- bór, czy scena ma być rysowana z zachowaniem poczÄ…tkowego aspektu ob- razu czy też bez zachowania tej proporcji. Jednak w tym przypadku nie jest modyfikowany obszar renderingu ale współrzÄ™dne bryÅ‚y odcinania (funkcja Reshape). Rysunek 4. PoczÄ…tkowe okno programu SzeÅ›cian 1 1.2.1. Plik szescian1.cpp /" ( c ) J a n u s z G a n c z a r s k i h t t p : / / www . j a n u s z g . hg . p l J a nu s zG @ e nter . n e t . p l "/ #include #include < s t d l i b . h> // s t a Å‚ e do o b s Å‚ u g i menu p o d r Ä™ c z n e g o enum { 1. Definiowanie sceny 3D 8 FULL WINDOW, // a s p e k t o b r a z u - c a Å‚ e okno ASPECT 1 1 , // a s p e k t o b r a z u 1 : 1 EXIT // w y j Å› c i e } ; // a s p e k t o b r a z u i n t Aspe ct = FULL WINDOW; // f u n k c j a g e n e r u j Ä… c a s c e n Ä™ 3D void D i s p l a y ( ) { // k o l o r t Å‚ a - z a w a r t o Å› ć b u f o r a k o l o r u g l C l e a r C o l o r ( 1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ; // c z y s z c z e n i e b u f o r a k o l o r u g l C l e a r (GL COLOR BUFFER BIT ) ; // wybó r m a c i e r z y m o d e l o w a n i a glMatrixMode (GL MODELVIEW ) ; // m a c i e r z m o d e l o w a n i a = m a c i e r z j e d n o s t k o w a g l L o a d I d e n t i t y ( ) ; // k o l o r k r a w Ä™ d z i s z e Å› c i a n u g l C o l o r 3 f ( 0 . 0 , 0 . 0 , 0 . 0 ) ; // p o c z Ä… t e k d e f i n i c j i k r a w Ä™ d z i s z e Å› c i a n u g l B e g i n ( GL LINES ) ; // w s p ó Å‚ r z Ä™ d n e k o l e j n y c h k r a w Ä™ d z i s z e Å› c i a n u g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; // k o n i e c d e f i n i c j i p r y m i t y w u glEnd ( ) ; // s k i e r o w a n i e p o l e c e Å„ do w y k o n a n i a g l F l u s h ( ) ; // zamiana b u f o r ó w k o l o r u g l u t S w a p B u f f e r s ( ) ; } // zmiana w i e l k o Å› c i okna 1. Definiowanie sceny 3D 9 void Reshape ( i n t width , i n t h e i g h t ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno g l V i e w p o r t ( 0 , 0 , width , h e i g h t ) ; // wybó r m a c i e r z y r z u t o w a n i a glMatrixMode (GL PROJECTION ) ; // m a c i e r z r z u t o w a n i a = m a c i e r z j e d n o s t k o w a g l L o a d I d e n t i t y ( ) ; // p a r a m e t r y b r y Å‚ y o b c i n a n i a i f ( Aspect == ASPECT 1 1 ) { // w y s o k o Å› ć okna w i Ä™ k s z a od w y s o k o Å› c i okna i f ( width < h e i g h t && width > 0 ) g l O r t h o ( - 2 . 0 , 2 . 0 , - 2 . 0 " h e i g h t / width , 2 . 0 " h e i g h t / width , - 2 . 0 , 2 . 0 ) ; e l s e // s z e r o k o Å› ć okna w i Ä™ k s z a l u b równa w y s o k o Å› c i okna i f ( width >= h e i g h t && h e i g h t > 0 ) g l O r t h o ( -2.0" width / h e i g h t , 2 . 0 " width / h e i g h t , - 2 . 0 , 2 . 0 , - 2 . 0 , 2 . 0 ) ; } e l s e g l O r t h o ( - 2 . 0 , 2 . 0 , - 2 . 0 , 2 . 0 , - 2 . 0 , 2 . 0 ) ; // g e n e r o w a n i e s c e n y 3D D i s p l a y ( ) ; } // o b s Å‚ u g a menu p o d r Ä™ c z n e g o void Menu ( i n t v a l u e ) { switch ( v a l u e ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno case FULL WINDOW: Aspect = FULL WINDOW; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; // o b s z a r r e n d e r i n g u - a s p e k t 1 : 1 case ASPECT 1 1 : Aspect = ASPECT 1 1 ; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; // w y j Å› c i e case EXIT : e x i t ( 0 ) ; } } i n t main ( i n t a r gc , char " a r g v [ ] ) { // i n i c j a l i z a c j a b i b l i o t e k i GLUT g l u t I n i t (& a r g c , a r g v ) ; // i n i c j a l i z a c j a b u f o r a r a m k i g l u t I n i t D i s p l a y M o d e (GLUT DOUBLE | GLUT RGB ) ; // r o z m i a r y g Å‚ ó w n e g o okna programu g l u t I n i t W i n d o w S i z e ( 4 0 0 , 4 0 0 ) ; // u t w o r z e n i e g Å‚ ó w n e g o okna programu #i f d e f WIN32 glutCreateWindow ( S z e Å› c i a n 1 ) ; #e l s e glutCreateWindow ( S z e s c i a n 1 ) ; #end if // d o Å‚ Ä… c z e n i e f u n k c j i g e n e r u j Ä… c e j s c e n Ä™ 3D g l u t D i s p l a y F u n c ( D i s p l a y ) ; // d o Å‚ Ä… c z e n i e f u n k c j i w y w o Å‚ y w a n e j p r z y z m i a n i e r o z m i a r u okna glutR eshape Func ( Reshape ) ; // u t w o r z e n i e menu p o d r Ä™ c z n e g o glutCreateMenu ( Menu ) ; 1. Definiowanie sceny 3D 10 // d o d a n i e p o z y c j i do menu p o d r Ä™ c z n e g o #i f d e f WIN32 glutAddMenuEntry ( Aspekt o br azu - c a Å‚ e okno ,FULL WINDOW ) ; glutAddMenuEntry ( Aspekt o br azu 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j Å› c i e ,EXIT ) ; #e l s e glutAddMenuEntry ( Aspekt o br azu - c a l e okno ,FULL WINDOW ) ; glutAddMenuEntry ( Aspekt o br azu 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j s c i e ,EXIT ) ; #end if // o k r e Å› l e n i e p r z y c i s k u m y s z k i o b s Å‚ u g u j Ä… c e j menu p o d r Ä™ c z n e glutAttachMenu (GLUT RIGHT BUTTON ) ; // w p r o w a d z e n i e programu do o b s Å‚ u g i p Ä™ t l i k o m u n ika tó w glutMainLoop ( ) ; return 0 ; } 1.3. Rzutowanie perspektywiczne Rzutowanie perspektywiczne daje bardziej realistyczne efekty niż rzuto- wanie prostokÄ…tne, stÄ…d jest szeroko stosowane np. w grach. Parametry bryÅ‚y odcinania, która przy rzutowaniu perspektywicznym ma postać ostrosÅ‚upa Å›ciÄ™tego o wierzchoÅ‚ku znajdujÄ…cym siÄ™ w poczÄ…tku ukÅ‚adu współrzÄ™dnych (patrz rysunek 5), okreÅ›la funkcja: void glFrustum (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) Parametryleft,right,bottomitopwyznaczajÄ… rozmiary górnej pod- stawy bryÅ‚y odcinania (jest to obszar bezpoÅ›rednio odwzorowywany na ob- szar renderingu), anearifarwyznaczajÄ… poÅ‚ożenie odpowiednio górnej i dolnej podstawy ostrosÅ‚upa (przedniej i tylnej pÅ‚aszczyzny odcinania), które zawierajÄ… siÄ™ w pÅ‚aszczyznach o równaniach: z = -near i z = -far. ParametrynearifarmuszÄ… mieć wartoÅ›ci dodatnie. Macierz rzutowania perspektywicznego, tworzona przez funkcjÄ™glFrus- tumi mnożona przez aktualnie wybranÄ… macierz, ma postać: ëÅ‚ öÅ‚ right+left 2near 0 0 right-left right-left ìÅ‚ ÷Å‚ top+bottom 2near ìÅ‚ ÷Å‚ 0 0 ìÅ‚ top-bottom top-bottom ÷Å‚ ìÅ‚ ÷Å‚ far+near 2far·near 0 0 íÅ‚ Å‚Å‚ far-near far-near 0 0 -1 0 Warto zauważyć, że precyzja dziaÅ‚ania jeszcze przez nas nieużywanego z-bufora, zależy od wartoÅ›ci stosunku parametrównearifar: far r = near 1. Definiowanie sceny 3D 11 Y top -far left right -near bottom X Z Rysunek 5. PoÅ‚ożenie pÅ‚aszczyzn bryÅ‚y odcinania w rzutowaniu perspekty- wicznym Im wiÄ™ksza wartość r, tym mniej efektywne jest dziaÅ‚anie z-bufora. Oczy- wiÅ›cienearnigdy nie może przyjąć wartoÅ›ci równej 0, bowiem przednia pÅ‚aszczyzna odcinania przechodziÅ‚a by wówczas przez Å›rodek perspektywy. Alternatywny sposób okreÅ›lania rzutu perspektywicznego umożliwia funk- cja z biblioteki GLU: void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) gdzie parametr fovy okreÅ›la w stopniach kÄ…t widzenia obserwatora zawar- ty w pÅ‚aszczyznie YZ ( (top, 0, bottom)), aaspectjest stosunkiem szero- koÅ›ci do wysokoÅ›ci przedniej pÅ‚aszczyzny odcinania, czyli górnej podstawy ostrosÅ‚upa ograniczajÄ…cego scenÄ™ 3D. ParametryzNearizFarodpowiadajÄ… parametromnearifarfunkcjiglFrustum. 1. Definiowanie sceny 3D 12 Macierz rzutowania perspektywicznego, tworzona przez funkcjÄ™gluPer- spectivei mnożona przez aktualnie wybranÄ… macierz, ma postać: ëÅ‚ öÅ‚ f ovy ctg 2 0 0 0 aspect ìÅ‚ ÷Å‚ ìÅ‚ fovy ÷Å‚ 0 ctg 0 0 ìÅ‚ ÷Å‚ 2 ìÅ‚ ÷Å‚ zF ar+zNear 2·zF ar·zNear íÅ‚ Å‚Å‚ 0 0 zNear-zF ar zNear-zF ar 0 0 -1 0 WewnÄ™trznie funkcjagluPerspectivewykorzystuje do ustawienia ma- cierzy rzutowania perspektywicznego funkcjÄ™glFrustum. Oto wzory prze- ksztaÅ‚cenia parametrów funkcjigluPerspectivena parametryglFrustum:
fovy top = zNear · tg Ä„ 360 W kolejnym przykÅ‚adowym programie (plikszescian2.cpp) do utworze- nia macierzy rzutowania perspektywicznego wykorzystamy funkcjÄ™glFrus- tum. Przednia pÅ‚aszczyzna odcinania bÄ™dzie miaÅ‚a takie same rozmiary jak w poprzednim programie. Zmianie ulegnÄ… natomiast współrzÄ™dne przedniej i tylnej pÅ‚aszczyzny obcinania - poprzednio jedna z tych pÅ‚aszczyzn miaÅ‚a wartość ujemnÄ…, której nie akceptuje funkcjaglFrustum. Rysowanym obiektem ponownie bÄ™dzie szeÅ›cian ale próba narysowania go w tym samym miejscu jak w poprzednim programie spowoduje, że bÄ™dzie widoczna tylko jedna jego Å›ciana. Wszystko dlatego, że pozostaÅ‚e Å›ciany szeÅ›cianu znajdujÄ… siÄ™ poza obszarem bryÅ‚y odcinania. Możliwe sÄ… trzy spo- soby rozwiÄ…zania tego problemu. Pierwszy polega na zmianie współrzÄ™dnych wierzchoÅ‚ków szeÅ›cianu w taki sposób, aby szeÅ›cian zmieÅ›ciÅ‚ siÄ™ w zmienio- nej bryle obcinania. RozwiÄ…zanie to ma jednÄ… zasadniczÄ… wadÄ™ - wierzchoÅ‚ki szeÅ›cianu trzeba bÄ™dzie modyfikować przy każdej zmianie parametrów sceny 3D. W przypadku jednego obiektu nie stanowi to specjalnego problemu, ale czyni pomysÅ‚ niewykonalnym przy każdej bardziej skomplikowanej scenie 3D. Drugie, zastosowane w programie rozwiÄ…zanie, polega na przesuniÄ™ciu wierzchoÅ‚ków szeÅ›cianu o wektor [0, 0, -3], czyli o -3 jednostki wzdÅ‚uż osi OZ. Realizuje to funkcjaglTranslatef, która wywoÅ‚ywana jest bezpoÅ›red- nio po zainicjowaniu macierzy modelowania macierzÄ… jednostkowÄ… (patrz funkcjaDisplay). W efekcie otrzymamy szeÅ›cian przedstawiony na rysun- ku 6. Należy dodać, że taka metoda jest czÄ™sto stosowanÄ… praktykÄ…. Obiekty 3D definiowane sÄ… z różnymi współrzÄ™dnymi, a nastÄ™pnie odpowiednio trans- formowane do docelowego poÅ‚ożenia w scenie 3D. Funkcje umożliwiajÄ…ce 1. Definiowanie sceny 3D 13 takie przeksztaÅ‚cenia poznamy bliżej w nastÄ™pnym odcinku kursu. TrzeciÄ… metodÄ… jest modyfikacja poÅ‚ożenia obserwatora sceny 3D - zapoznamy siÄ™ z tÄ… technikÄ… jeszcze w tym odcinku. Rysunek 6. PoczÄ…tkowe okno programu SzeÅ›cian 2 1.3.1. Plik szescian2.cpp /" ( c ) J a n u s z G a n c z a r s k i h t t p : / / www . j a n u s z g . hg . p l J a nu s zG @ e nter . n e t . p l "/ #include #include < s t d l i b . h> // s t a Å‚ e do o b s Å‚ u g i menu p o d r Ä™ c z n e g o enum { FULL WINDOW, // a s p e k t o b r a z u - c a Å‚ e okno ASPECT 1 1 , // a s p e k t o b r a z u 1 : 1 EXIT // w y j Å› c i e } ; // a s p e k t o b r a z u i n t Aspe ct = FULL WINDOW; // f u n k c j a g e n e r u j Ä… c a s c e n Ä™ 3D void D i s p l a y ( ) 1. Definiowanie sceny 3D 14 { // k o l o r t Å‚ a - z a w a r t o Å› ć b u f o r a k o l o r u g l C l e a r C o l o r ( 1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ; // c z y s z c z e n i e b u f o r a k o l o r u g l C l e a r (GL COLOR BUFFER BIT ) ; // wyb ór m a c i e r z y m o d e l o w a n i a glMatrixMode (GL MODELVIEW ) ; // m a c i e r z m o d e l o w a n i a = m a c i e r z j e d n o s t k o w a g l L o a d I d e n t i t y ( ) ; // p r z e s u n i Ä™ c i e o b i e k t u o w e k t o r [ 0 , 0 , - 3 ] g l T r a n s l a t e f ( 0 , 0 , - 3 . 0 ) ; // k o l o r k r a w Ä™ d z i s z e Å› c i a n u g l C o l o r 3 f ( 0 . 0 , 0 . 0 , 0 . 0 ) ; // p o c z Ä… t e k d e f i n i c j i k r a w Ä™ d z i s z e Å› c i a n u g l B e g i n ( GL LINES ) ; // w s p ó l r z Ä™ d n e k o l e j n y c h k r a w Ä™ d z i s z e Å› c i a n u g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; // k o n i e c d e f i n i c j i p r y m i t y w u glEnd ( ) ; // s k i e r o w a n i e p o l e c e Å„ do w y k o n a n i a g l F l u s h ( ) ; // zamiana b u f o r ó w k o l o r u g l u t S w a p B u f f e r s ( ) ; } // zmiana w i e l k o Å› c i okna void Reshape ( i n t width , i n t h e i g h t ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno g l V i e w p o r t ( 0 , 0 , width , h e i g h t ) ; // wyb ór m a c i e r z y r z u t o w a n i a glMatrixMode (GL PROJECTION ) ; // m a c i e r z r z u t o w a n i a = m a c i e r z j e d n o s t k o w a 1. Definiowanie sceny 3D 15 g l L o a d I d e n t i t y ( ) ; // p a r a m e t r y b r y Å‚ y o b c i n a n i a i f ( Aspect == ASPECT 1 1 ) { // w y s o k o Å› ć okna w i Ä™ k s z a od w y s o k o Å› c i okna i f ( width < h e i g h t && width > 0 ) glFrustum ( - 2 . 0 , 2 . 0 , - 2 . 0 " h e i g h t / width , 2 . 0 " h e i g h t / width , 1 . 0 , 5 . 0 ) ; e l s e // s z e r o k o Å› ć okna w i Ä™ k s z a l u b równa w y s o k o Å› c i okna i f ( width >= h e i g h t && h e i g h t > 0 ) glFrustum ( -2.0" width / h e i g h t , 2 . 0 " width / h e i g h t , - 2 . 0 , 2 . 0 , 1 . 0 , 5 . 0 ) ; } e l s e glFrustum ( - 2 . 0 , 2 . 0 , - 2 . 0 , 2 . 0 , 1 . 0 , 5 . 0 ) ; // g e n e r o w a n i e s c e n y 3D D i s p l a y ( ) ; } // o b s Å‚ u g a menu p o d r Ä™ c z n e g o void Menu ( i n t v a l u e ) { switch ( v a l u e ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno case FULL WINDOW: Aspect = FULL WINDOW; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; // o b s z a r r e n d e r i n g u - a s p e k t 1 : 1 case ASPECT 1 1 : Aspect = ASPECT 1 1 ; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; // w y j Å› c i e case EXIT : e x i t ( 0 ) ; } } i n t main ( i n t a r gc , char " a r g v [ ] ) { // i n i c j a l i z a c j a b i b l i o t e k i GLUT g l u t I n i t (& argc , a r g v ) ; // i n i c j a l i z a c j a b u f o r a r a m k i g l u t I n i t D i s p l a y M o d e (GLUT DOUBLE | GLUT RGB ) ; // r o z m i a r y g Å‚ ó w n e g o okna programu g l u t I n i t W i n d o w S i z e ( 4 0 0 , 4 0 0 ) ; // u t w o r z e n i e g Å‚ ó w n e g o okna programu #i f d e f WIN32 glutCreateWindow ( S z e Å› c i a n 2 ) ; #e l s e glutCreateWindow ( S z e Å› c i a n 2 ) ; #end if // d o Å‚ Ä… c z e n i e f u n k c j i g e n e r u j Ä… c e j s c e n Ä™ 3D g l u t D i s p l a y F u n c ( D i s p l a y ) ; // d o Å‚ Ä… c z e n i e f u n k c j i w y w o Å‚ y w a n e j p r z y z m i a n i e r o z m i a r u okna glutRe shapeFunc ( Reshape ) ; // u t w o r z e n i e menu p o d r Ä™ c z n e g o glutCreateMenu ( Menu ) ; // d o d a n i e p o z y c j i do menu p o d r Ä™ c z n e g o #i f d e f WIN32 glutAddMenuEntry ( Aspekt ob r a z u - c a Å‚ e okno ,FULL WINDOW) ; glutAddMenuEntry ( Aspekt ob r a z u 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j Å› c i e ,EXIT ) ; 1. Definiowanie sceny 3D 16 #e l s e glutAddMenuEntry ( Aspekt ob r a z u - c a l e okno ,FULL WINDOW) ; glutAddMenuEntry ( Aspekt ob r a z u 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j s c i e ,EXIT ) ; #end if // o k r e Å› l e n i e p r z y c i s k u m y s z k i o b s Å‚ u g u j Ä… c e j menu p o d r Ä™ c z n e glutAttachMenu (GLUT RIGHT BUTTON ) ; // w p r o w a d z e n i e programu do o b s Å‚ u g i p Ä™ t l i k o m un i kat ów glutMainLoop ( ) ; return 0 ; } Drugi program przedstawiajÄ…cy rzutowanie perspektywiczne (plikszes- cian3.cpp) stosuje funkcjÄ™gluPerspective. Aby jednak nie powielać roz- wiÄ…zaÅ„ z poprzedniego programu dodamy mechanizm pokazujÄ…cy jaki wpÅ‚yw na wyglÄ…d obiektów 3D ma zmiana poÅ‚ożenie Å›rodka perspektywy, realizo- wana poprzez zmianÄ™ kÄ…ta widzenia obserwatora (parametrfovyfunkcji gluPerspective). W tym celu potrzebna jest obsÅ‚uga klawiatury. Podsta- wowa funkcja obsÅ‚ugi klawiatury (w przykÅ‚adowym programie jest to funkcja Keyboard) ma trzy parametry: key- kod ASCII klawisza, x,y- współrzÄ™dne kursora myszki w chwili naciÅ›niÄ™cia przycisku klawia- tury. Aby obsÅ‚uga klawiatury dziaÅ‚aÅ‚a, w części głównej programu należy wÅ‚Ä…- czyć funkcjÄ™ obsÅ‚ugi klawiatury wywoÅ‚ujÄ…c funkcjÄ™: void glutKeyboardFunc (void (*func)(unsigned char key, int x, int y)) PoczÄ…tkowe okno programu SzeÅ›cian 3 zawiera rysunek 7. PrzyciskajÄ…c klawisze + i - można modyfikować kÄ…t patrzenia obserwatora, który poczÄ…tkowo wynosi 90ć%. 1.3.2. Plik szescian3.cpp /" ( c ) J a n u s z G a n c z a r s k i h t t p : / / www . j a n u s z g . hg . p l J a nu s zG @ e nter . n e t . p l "/ #include #include < s t d l i b . h> // s t a Å‚ a do o b s Å‚ u g i menu p o d r Ä™ c z n e g o enum { EXIT // w y j Å› c i e } ; // p i o n o w y k Ä… t p o l a w i d z e n i a GLdouble f o v y = 9 0 ; // f u n k c j a g e n e r u j Ä… c a s c e n Ä™ 3D void D i s p l a y ( ) { 1. Definiowanie sceny 3D 17 Rysunek 7. PoczÄ…tkowe okno programu SzeÅ›cian 3 // k o l o r t Å‚ a - z a w a r t o Å› ć b u f o r a k o l o r u g l C l e a r C o l o r ( 1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ; // c z y s z c z e n i e b u f o r a k o l o r u g l C l e a r (GL COLOR BUFFER BIT ) ; // wyb ór m a c i e r z y m o d e l o w a n i a glMatrixMode (GL MODELVIEW ) ; // m a c i e r z m o d e l o w a n i a = m a c i e r z j e d n o s t k o w a g l L o a d I d e n t i t y ( ) ; // p r z e s u n i Ä™ c i e o b i e k t u o w e k t o r [ 0 , 0 , - 3 ] g l T r a n s l a t e f ( 0 , 0 , - 3 . 0 ) ; // k o l o r k r a w Ä™ d z i s z e Å› c i a n u g l C o l o r 3 f ( 0 . 0 , 0 . 0 , 0 . 0 ) ; // p o c z Ä… t e k d e f i n i c j i k r a w Ä™ d z i s z e Å› c i a n u g l B e g i n ( GL LINES ) ; // w s p ó l r z Ä™ d n e k o l e j n y c h k r a w Ä™ d z i s z e Å› c i a n u g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; 1. Definiowanie sceny 3D 18 g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; // k o n i e c d e f i n i c j i p r y m i t y w u glEnd ( ) ; // s k i e r o w a n i e p o l e c e Å„ do w y k o n a n i a g l F l u s h ( ) ; // zamiana b u f o r ó w k o l o r u g l u t S w a p B u f f e r s ( ) ; } // zmiana w i e l k o Å› c i okna void Reshape ( i n t width , i n t h e i g h t ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno g l V i e w p o r t ( 0 , 0 , width , h e i g h t ) ; // wyb ór m a c i e r z y r z u t o w a n i a glMatrixMode (GL PROJECTION ) ; // m a c i e r z r z u t o w a n i a = m a c i e r z j e d n o s t k o w a g l L o a d I d e n t i t y ( ) ; // o b l i c z e n i e a s p e k t u o b r a z u z u w z g l Ä™ d n i e n i e m // p r z y p a d k u , g d y w y s o k o Å› ć o b r a z u w y n o s i 0 GLdouble a s p e c t = 1 ; i f ( h e i g h t > 0 ) a s p e c t = width / ( GLdouble ) h e i g h t ; // r z u t o w a n i e p e r s p e k t y w i c z n e g l u P e r s p e c t i v e ( fovy , a s p e c t , 1 . 0 , 5 . 0 ) ; // g e n e r o w a n i e s c e n y 3D D i s p l a y ( ) ; } // o b s Å‚ u g a k l a w i a t u r y void Keyboard ( unsigned char key , i n t x , i n t y ) { // k l a w i s z + i f ( key == + && f o v y < 1 8 0 ) f o v y ++; e l s e // k l a w i s z - i f ( key == - && f o v y > 0 ) fovy --; // o d r y s o w a n i e okna Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; } // o b s Å‚ u g a menu p o d r Ä™ c z n e g o void Menu ( i n t v a l u e ) { 1. Definiowanie sceny 3D 19 switch ( v a l u e ) { // w y j Å› c i e case EXIT : e x i t ( 0 ) ; } } i n t main ( i n t a r gc , char " a r g v [ ] ) { // i n i c j a l i z a c j a b i b l i o t e k i GLUT g l u t I n i t (& argc , a r g v ) ; // i n i c j a l i z a c j a b u f o r a r a m k i g l u t I n i t D i s p l a y M o d e (GLUT DOUBLE | GLUT RGB ) ; // r o z m i a r y g Å‚ ó w n e g o okna programu g l u t I n i t W i n d o w S i z e ( 4 0 0 , 4 0 0 ) ; // u t w o r z e n i e g Å‚ ó w n e g o okna programu #i f d e f WIN32 glutCreateWindow ( S z e Å› c i a n 3 ) ; #e l s e glutCreateWindow ( S z e s c i a n 3 ) ; #end if // d o Å‚ Ä… c z e n i e f u n k c j i g e n e r u j Ä… c e j s c e n Ä™ 3D g l u t D i s p l a y F u n c ( D i s p l a y ) ; // d o Å‚ Ä… c z e n i e f u n k c j i w y w o Å‚ y w a n e j p r z y z m i a n i e r o z m i a r u okna glutRe shapeFunc ( Reshape ) ; // d o Å‚ Ä… c z e n i e f u n k c j i o b s Å‚ u g i k l a w i a t u r y glutKeyboardFunc ( Keyboard ) ; // u t w o r z e n i e menu p o d r Ä™ c z n e g o glutCreateMenu ( Menu ) ; // d o d a n i e p o z y c j i do menu p o d r Ä™ c z n e g o #i f d e f WIN32 glutAddMenuEntry ( W y j Å› c i e ,EXIT ) ; #e l s e glutAddMenuEntry ( W y j s c i e ,EXIT ) ; #end if // o k r e Å› l e n i e p r z y c i s k u m y s z k i o b s Å‚ u g u j Ä… c e j menu p o d r Ä™ c z n e glutAttachMenu (GLUT RIGHT BUTTON ) ; // w p r o w a d z e n i e programu do o b s Å‚ u g i p Ä™ t l i k o m un i kat ów glutMainLoop ( ) ; return 0 ; } 1.4. PoÅ‚ożenie obserwatora Ostatnim z podstawowych elementów wymagajÄ…cych omówienia przy tworzeniu sceny 3D jest poÅ‚ożenie obserwatora, nazywane także poÅ‚ożeniem kamery lub oka . DomyÅ›lnie obserwator w OpenGL poÅ‚ożony jest w po- czÄ…tku ukÅ‚adu współrzÄ™dnych i skierowany jest w stronÄ™ ujemnej półosi OZ. Obserwator jest tak zorientowany w przestrzeni, że kierunek do góry po- krywa siÄ™ z kierunkiem osi OY. Zasadniczo OpenGL nie umożliwia zmiany poÅ‚ożenia obserwatora. Wszystkie przeksztaÅ‚cenia poÅ‚ożenia obserwatora faktycznie realizowane sÄ… jako odpowiednie przeksztaÅ‚cenia ukÅ‚adu współrzÄ™dnych. Aby jednak uÅ‚a- 1. Definiowanie sceny 3D 20 twić prace zwiÄ…zane z definiowaniem tych przeksztaÅ‚ceÅ„, biblioteka GLU zawiera funkcjÄ™ gluLookAt, która pozwala na jednorazowe zdefiniowanie wszystkich parametrów opisujÄ…cych obserwatora: void gluLookAt (GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz) Kolejne trójki parametrów funkcjigluLookAtoznaczajÄ…: eyex,eyey,eyez- współrzÄ™dne poÅ‚ożenia obserwatora, centerx,centery,centerz- współrzÄ™dne punktu, w którego kierunku jest zwrócony obserwator, upx,upy,upz- współrzÄ™dne wektora okreÅ›lajÄ…cego kierunek do góry . DomyÅ›lne poÅ‚ożenie obserwatora odpowiada wywoÅ‚aniu gluLookat (0.0, 0.0, 0.0, 0.0, 0.0, -100.0, 0.0, 1.0, 0.0) W kolejnym przykÅ‚adowym programie (plikszescian4.cpp) bÄ™dziemy modyfikować tylko współrzÄ™dne poÅ‚ożenia obserwatora. Przy niezmiennych współrzÄ™dnych punktu, w którego kierunku patrzy obserwator, daje to cie- kawy efekt obserwacji sceny z pewnej odlegÅ‚oÅ›ci. Zmiana poÅ‚ożenia obserwatora realizowana jest w funkcjachKeyboard (przyciski + i - ) orazSpecialKeys(klawisze kursora). Warto zauważyć, że zmiany współrzÄ™dnych obserwatora, które reprezentujÄ… zmienneeyex, eyeyieyez, sÄ… odwrotne niż można by siÄ™ spodziewać. PrzykÅ‚adowo na- ciÅ›niÄ™cie strzaÅ‚ki w dół powoduje zwiÄ™kszenie o 0,1 zmiennejeyey, która okreÅ›la współrzÄ™dnÄ… Y poÅ‚ożenia obserwatora. Jest to spowodowane tym, że macierz modelowania, modyfikowana przy wywoÅ‚aniu funkcjigluLookAt, odgrywa podwójnÄ… rolÄ™. Z jednej strony umożliwia przeksztaÅ‚cenia współ- rzÄ™dnych obiektu (patrz poprzedni przykÅ‚ad), a z drugiej przeksztaÅ‚cenia współrzÄ™dnych obserwatora. PrzykÅ‚adowo, to co z punktu widzenia obiek- tu jest przesuniÄ™ciem o wektor [1, 0, 0], dla obserwatora jest przesuniÄ™ciem o wektor przeciwny tj. [-1, 0, 0]. Dobre poznanie opisanego mechanizmy wy- maga eksperymentów, do których gorÄ…co zachÄ™cam Czytelników. Do omówienia pozostaÅ‚a wprowadzona w ostatnim przykÅ‚adzie obsÅ‚uga klawiszy kursora. Jest ona realizowana odrÄ™bnie od obsÅ‚ugi przycisków, któ- re reprezentowane sÄ… bezpoÅ›rednio przez kody ASCII (funkcjaKeyboard). Funkcja obsÅ‚ugujÄ…ca klawisze kursora oraz przyciski funkcyjne (w przykÅ‚a- dowym programie jest to funkcjaSpecialKeys) ma trzy parametry: key- kod przycisku; zwracana jest jedna z poniższych wartoÅ›ci: GLUTKEYF1- przycisk F1, GLUTKEYF2- przycisk F2, GLUTKEYF3- przycisk F3, 1. Definiowanie sceny 3D 21 GLUTKEYF4- przycisk F4, GLUTKEYF5- przycisk F5, GLUTKEYF6- przycisk F6, GLUTKEYF7- przycisk F7, GLUTKEYF8- przycisk F8, GLUTKEYF9- przycisk F9, GLUTKEYF10- przycisk F10, GLUTKEYF11- przycisk F11, GLUTKEYF12- przycisk F12, GLUTKEYLEFT- kursor w lewo, GLUTKEYUP- kursor do góry, GLUTKEYRIGHT- kursor w prawo, GLUTKEYDOWN- kursor w dół, GLUTKEYPAGEUP- przycisk Page Up GLUTKEYPAGEDOWN- przycisk Page Down, GLUTKEYHOME- przycisk Home, GLUTKEYEND- przycisk End, GLUTKEYINSERT- przycisk Insert. x,y- współrzÄ™dne kursora myszki w chwili naciÅ›niÄ™cia przycisku klawia- tury. Podobnie jak w przypadku poprzedniej funkcji obsÅ‚ugujÄ…cej klawiaturÄ™, w głównym programie należy wÅ‚Ä…czyć obsÅ‚ugÄ™ klawiszy kursora i klawiszy funkcyjnych wywoÅ‚ujÄ…c funkcjÄ™: void glutSpecialFunc (void (*func)(int key, int x, int y)) Rysunek 8 przedstawia przykÅ‚adowe okno programu SzeÅ›cian 4, którego kod zródÅ‚owy znajduje siÄ™ poniżej. 1.4.1. Plik szescian4.cpp /" ( c ) J a n u s z G a n c z a r s k i h t t p : / / www . j a n u s z g . hg . p l J a nu s zG @ e nter . n e t . p l "/ #include #include < s t d l i b . h> // s t a Å‚ e do o b s Å‚ u g i menu p o d r Ä™ c z n e g o enum { FULL WINDOW, // a s p e k t o b r a z u - c a Å‚ e okno ASPECT 1 1 , // a s p e k t o b r a z u 1 : 1 EXIT // w y j Å› c i e } ; // a s p e k t o b r a z u i n t Aspe ct = FULL WINDOW; // w p ó Å‚ r z Ä™ d n e p o Å‚ o ż e n i a o b s e r w a t o r a 1. Definiowanie sceny 3D 22 Rysunek 8. PrzykÅ‚adowe okno programu SzeÅ›cian 4 GLdouble eyex = 0 ; GLdouble eyey = 0 ; GLdouble e y e z = 3 ; // w s p ó Å‚ r z Ä™ d n e p u n k t u w k t ó r e g o k i e r u n k u j e s t z w r ó c o n y o b s e r w a t o r , GLdouble c e n t e r x = 0 ; GLdouble c e n t e r y = 0 ; GLdouble c e n t e r z = -100; // f u n k c j a g e n e r u j Ä… c a s c e n Ä™ 3D void D i s p l a y ( ) { // k o l o r t Å‚ a - z a w a r t o Å› ć b u f o r a k o l o r u g l C l e a r C o l o r ( 1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ; // c z y s z c z e n i e b u f o r a k o l o r u g l C l e a r (GL COLOR BUFFER BIT ) ; // wyb ór m a c i e r z y m o d e l o w a n i a glMatrixMode (GL MODELVIEW ) ; // m a c i e r z m o d e l o w a n i a = m a c i e r z j e d n o s t k o w a g l L o a d I d e n t i t y ( ) ; // u s t a w i e n i e o b s e r w a t o r a gluLookAt ( eyex , eyey , eyez , c e n t e r x , c e n t e r y , c e n t e r z , 0 , 1 , 0 ) ; // k o l o r k r a w Ä™ d z i s z e Å› c i a n u g l C o l o r 3 f ( 0 . 0 , 0 . 0 , 0 . 0 ) ; // p o c z Ä… t e k d e f i n i c j i k r a w Ä™ d z i s z e Å› c i a n u g l B e g i n ( GL LINES ) ; 1. Definiowanie sceny 3D 23 // w s p ó l r z Ä™ d n e k o l e j n y c h k r a w Ä™ d z i s z e Å› c i a n u g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , -1.0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( -1 . 0 , 1 . 0 , - 1 . 0 ) ; // k o n i e c d e f i n i c j i p r y m i t y w u glEnd ( ) ; // s k i e r o w a n i e p o l e c e Å„ do w y k o n a n i a g l F l u s h ( ) ; // zamiana b u f o r ó w k o l o r u g l u t S w a p B u f f e r s ( ) ; } // zmiana w i e l k o Å› c i okna void Reshape ( i n t width , i n t h e i g h t ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno g l V i e w p o r t ( 0 , 0 , width , h e i g h t ) ; // wyb ór m a c i e r z y r z u t o w a n i a glMatrixMode (GL PROJECTION ) ; // m a c i e r z r z u t o w a n i a = m a c i e r z j e d n o s t k o w a g l L o a d I d e n t i t y ( ) ; // p a r a m e t r y b r y Å‚ y o b c i n a n i a i f ( Aspect == ASPECT 1 1 ) { // w y s o k o Å› ć okna w i Ä™ k s z a od w y s o k o Å› c i okna i f ( width < h e i g h t && width > 0 ) glFrustum ( - 2 . 0 , 2 . 0 , - 2 . 0 " h e i g h t / width , 2 . 0 " h e i g h t / width , 1 . 0 , 5 . 0 ) ; e l s e // s z e r o k o Å› ć okna w i Ä™ k s z a l u b równa w y s o k o Å› c i okna i f ( width >= h e i g h t && h e i g h t > 0 ) glFrustum ( -2.0" width / h e i g h t , 2 . 0 " width / h e i g h t , - 2 . 0 , 2 . 0 , 1 . 0 , 5 . 0 ) ; } e l s e glFrustum ( - 2 . 0 , 2 . 0 , - 2 . 0 , 2 . 0 , 1 . 0 , 5 . 0 ) ; // g e n e r o w a n i e s c e n y 3D D i s p l a y ( ) ; } // o b s Å‚ u g a k l a w i a t u r y 1. Definiowanie sceny 3D 24 void Keyboard ( unsigned char key , i n t x , i n t y ) { // k l a w i s z + i f ( key == + ) e y e z -= 0 . 1 ; e l s e // k l a w i s z - i f ( key == - ) e y e z += 0 . 1 ; // o d r y s o w a n i e okna Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; } // o b s Å‚ u g a k l a w i s z y f u n k c y j n y c h i k l a w i s z y k u r s o r a void S p e c i a l K e y s ( i n t key , i n t x , i n t y ) { switch ( key ) { // k u r s o r w l e w o case GLUT KEY LEFT : eyex += 0 . 1 ; break ; // k u r s o r w g ó r Ä™ case GLUT KEY UP : eyey -= 0 . 1 ; break ; // k u r s o r w prawo case GLUT KEY RIGHT : eyex -= 0 . 1 ; break ; // k u r s o r w d ó Å‚ case GLUT KEY DOWN: eyey += 0 . 1 ; break ; } // o d r y s o w a n i e okna Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; } // o b s Å‚ u g a menu p o d r Ä™ c z n e g o void Menu ( i n t v a l u e ) { switch ( v a l u e ) { // o b s z a r r e n d e r i n g u - c a Å‚ e okno case FULL WINDOW: Aspect = FULL WINDOW; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; // o b s z a r r e n d e r i n g u - a s p e k t 1 : 1 case ASPECT 1 1 : Aspect = ASPECT 1 1 ; Reshape ( g l u t G e t (GLUT WINDOW WIDTH) , g l u t G e t (GLUT WINDOW HEIGHT ) ) ; break ; // w y j Å› c i e case EXIT : e x i t ( 0 ) ; } } i n t main ( i n t a r gc , char " a r g v [ ] ) { // i n i c j a l i z a c j a b i b l i o t e k i GLUT g l u t I n i t (& argc , a r g v ) ; // i n i c j a l i z a c j a b u f o r a r a m k i g l u t I n i t D i s p l a y M o d e (GLUT DOUBLE | GLUT RGB ) ; // r o z m i a r y g Å‚ ó w n e g o okna programu 1. Definiowanie sceny 3D 25 g l u t I n i t W i n d o w S i z e ( 4 0 0 , 4 0 0 ) ; // u t w o r z e n i e g Å‚ ó w n e g o okna programu #i f d e f WIN32 glutCreateWindow ( S z e Å› c i a n 4 ) ; #e l s e glutCreateWindow ( S z e s c i a n 4 ) ; #end if // d o Å‚ Ä… c z e n i e f u n k c j i g e n e r u j Ä… c e j s c e n Ä™ 3D g l u t D i s p l a y F u n c ( D i s p l a y ) ; // d o Å‚ Ä… c z e n i e f u n k c j i w y w o Å‚ y w a n e j p r z y z m i a n i e r o z m i a r u okna glutRe shapeFunc ( Reshape ) ; // d o Å‚ Ä… c z e n i e f u n k c j i o b s Å‚ u g i k l a w i a t u r y glutKeyboardFunc ( Keyboard ) ; // d o Å‚ Ä… c z e n i e f u n k c j i o b s Å‚ u g i k l a w i s z y f u n k c y j n y c h i k l a w i s z y k u r s o r a g l u t S p e c i a l F u n c ( S p e c i a l K e y s ) ; // u t w o r z e n i e menu p o d r Ä™ c z n e g o glutCreateMenu ( Menu ) ; // d o d a n i e p o z y c j i do menu p o d r Ä™ c z n e g o #i f d e f WIN32 glutAddMenuEntry ( Aspekt ob r a z u - c a Å‚ e okno ,FULL WINDOW) ; glutAddMenuEntry ( Aspekt ob r a z u 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j Å› c i e ,EXIT ) ; #e l s e glutAddMenuEntry ( Aspekt ob r a z u - c a l e okno ,FULL WINDOW) ; glutAddMenuEntry ( Aspekt ob r a z u 1 : 1 , ASPECT 1 1 ) ; glutAddMenuEntry ( W y j s c i e ,EXIT ) ; #end if // o k r e Å› l e n i e p r z y c i s k u m y s z k i o b s Å‚ u g u j Ä… c e j menu p o d r Ä™ c z n e glutAttachMenu (GLUT RIGHT BUTTON ) ; // w p r o w a d z e n i e programu do o b s Å‚ u g i p Ä™ t l i k o m un i kat ów glutMainLoop ( ) ; return 0 ; } Literatura 26 Literatura [1] Mark Segal, Kurt Akeley: The OpenGL Graphics System. A Specification Version 2.0 [2] Jackie Neider, Tom Davis, Mason Woo: OpenGL Programming Guide The Red Book [3] Richard S. Wright jr, Michael Sweet: OpenGL KsiÄ™ga eksperta, Helion 1999 [4] Richard S. Wright jr, Michael Sweet: OpenGL KsiÄ™ga eksperta Wydanie III, Helion 2005 [5] The official OpenGL web page,http://www.opengl.org [6] Piotr Andrzejewski, Jakub Kurzak: Wprowadzenie do OpenGL. Programowa- nie zastosowaÅ„ graficznych, Kwantum 2000 [7] Kevin Hawkins, Dave Astle: OpenGL. Programowanie gier, Helion 2003 [8] Mark J. Kilgard: The OpenGL Utility Toolkit (GLUT) Programming Interface API Version 3. Silicon Graphics, Inc. 1996