Janusz Ganczarski
OpenGL
Definiowanie sceny 3D
Spis treści
Spis treści . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
1. Definiowanie sceny 3D . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
Obszar renderingu . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
Plik kwadrat2.cpp . . . . . . . . . . . . . . . . . . . . . . . .
2
Rzutowanie prostokątne . . . . . . . . . . . . . . . . . . . . . . . . .
4
Plik szescian1.cpp . . . . . . . . . . . . . . . . . . . . . . . .
7
Rzutowanie perspektywiczne . . . . . . . . . . . . . . . . . . . . . .
10
Plik szescian2.cpp . . . . . . . . . . . . . . . . . . . . . . . .
13
Plik szescian3.cpp . . . . . . . . . . . . . . . . . . . . . . . .
16
Położenie obserwatora . . . . . . . . . . . . . . . . . . . . . . . . . .
19
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 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
(funkcja Reshape) 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 funkcji Menu została użyta do tej pory nieopisana funkcja z biblioteki
GLUT:
int glutGet (GLenum type)
O tym jakiego rodzaju informacje zwróci funkcja glutGet decyduje para-
metr type. W przykładowym programie są to szerokość i wysokość okna, co
odpowiada parametrom opisanym stałymi GLUT WINDOW WIDTH i GLUT WIN-
DOW HEIGHT.
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 . h g . p l
J a n u s z G @ e n t e r . n e t . p l
∗/
#i n c l u d e <GL/ g l u t . h>
#i n c l u d e < 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
o k n o
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
A s p e c t = 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
g l E n d
( ) ;
//
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
( ) ;
//
z a m i a n a
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 ( ) ;
}
//
z m i a n a
w i e l k o ś c i
o k n a
void
R esh ape
( 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
( A s p e c t == ASPECT 1 1 )
{
//
s z e r o k o ś ć
o k n a
w i ę k s z a
od
w y s o k o ś c i
o k n a
i f
( w i d t h > h e i g h t )
g l V i e w p o r t
( ( w i d t h − 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 ś ć
o k n a
w i ę k s z a
od
w y s o k o ś c i
o k n a
i f
( w i d t h < h e i g h t )
g l V i e w p o r t
( 0 , ( h e i g h t − w i d t h ) / 2 , width , w i d t h ) ;
}
e l s e
//
o b s z a r
r e n d e r i n g u − c a ł e
o k n o
( 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
o k n o
c a s e FULL WINDOW :
A s p e c t = FULL WINDOW ;
R esha pe
( 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
c a s e ASPECT 1 1 :
A s p e c t = ASPECT 1 1 ;
R esha pe
( 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
c a s e EXIT :
e x i t
( 0 ) ;
}
}
i n t
main
( i n t
a r g c ,
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
o k n a
p r o g r a m u
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
o k n a
p r o g r a m u
g l u t C r e a t e W i n d o w
( ” 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
o k n a
g l u t R e s h a p e F u n c
( Re sha pe ) ;
//
u t w o r z e n i e
menu
p o d r ę c z n e g o
g l u t C r e a t e M e n u
( 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
( ” O b s z a r
r e n d e r i n g u − c a ł e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” O b s z a r
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
( ” O b s z a r
r e n d e r i n g u − c a l e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” O b s z a r
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 ) ;
#e n d i f
//
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
g l u t A t t a c h M e n u
(GLUT RIGHT BUTTON ) ;
//
w p r o w a d z e n i e
p r o g r a m u
do
o b s ł u g i
p ę t l i
k o m u n i k a t ó w
g l u t M a i n L o o p
( ) ;
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 = lef t
y = top
y = bottm
z = −near
z = −f ar
Obszar renderingu zawiera się w płaszczyźnie o równaniu z = −near.
Położenie poszczególnych płaszczyzn tworzących bryłę odcinania przedsta-
wia rysunek 3. Trzeba jednak wyraźnie 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.
bottom
right
left
top
-near
-far
X
Y
Z
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 funkcji glOrtho (-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.
Funkcja glOrtho tworzy macierz rzutu prostokątnego:
2
right−lef t
0
0
right+lef t
right−lef t
0
2
top−bottom
0
top+bottom
top−bottom
0
0
−2
f ar−near
f ar+near
f ar−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 parametr mode może przyjąć jedną z wartości:
— GL MODELVIEW - macierz modelowania,
— GL PROJECTION - macierz rzutowania,
— GL TEXTURE - macierz tekstury (omówiona później).
Ponieważ początkowa wartość wybranej macierzy jest nieokreślona, przed
wywołaniem glOrtho należ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 (plik szescian1.cpp) w funkcji Display macierzy
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 parametry left, right, bottom i top odpowiadają 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 . h g . p l
J a n u s z G @ e n t e r . n e t . p l
∗/
#i n c l u d e <GL/ g l u t . h>
#i n c l u d e < 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
o k n o
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
A s p e c t = 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 ) ;
//
w y b ó r
m a c i e r z y
m o d e l o w a n i a
g l M a t r i x M o d e
(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
g l E n d
( ) ;
//
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
( ) ;
//
z a m i a n a
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 ( ) ;
}
//
z m i a n a
w i e l k o ś c i
o k n a
1. Definiowanie sceny 3D
9
void
R esh ape
( 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
o k n o
g l V i e w p o r t
( 0 , 0 , width , h e i g h t ) ;
//
w y b ó r
m a c i e r z y
r z u t o w a n i a
g l M a t r i x M o d e
(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
( A s p e c t == ASPECT 1 1 )
{
//
w y s o k o ś ć
o k n a
w i ę k s z a
od
w y s o k o ś c i
o k n a
i f
( w i d t h < h e i g h t && w i d t h > 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 ś ć
o k n a
w i ę k s z a
l u b
równa
w y s o k o ś c i
o k n a
i f
( w i d t h >= h e i g h t && h e i g h t > 0 )
g l O r t h o
( − 2 . 0 ∗ w i d t h / h e i g h t , 2 . 0 ∗ w i d t h / 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
o k n o
c a s e FULL WINDOW :
A s p e c t = FULL WINDOW ;
R esha pe
( 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
c a s e ASPECT 1 1 :
A s p e c t = ASPECT 1 1 ;
R esha pe
( 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
c a s e EXIT :
e x i t
( 0 ) ;
}
}
i n t
main
( i n t
a r g c ,
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
o k n a
p r o g r a m u
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
o k n a
p r o g r a m u
#i f d e f WIN32
g l u t C r e a t e W i n d o w
( ” S z e ś c i a n
1 ” ) ;
#e l s e
g l u t C r e a t e W i n d o w
( ” S z e s c i a n
1 ” ) ;
#e n d i f
//
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
o k n a
g l u t R e s h a p e F u n c
( Re sha pe ) ;
//
u t w o r z e n i e
menu
p o d r ę c z n e g o
g l u t C r e a t e M e n u
( 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
( ” A s p e k t
o b r a z u − c a ł e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” A s p e k t
o b r a z u
1 : 1 ” , ASPECT 1 1 ) ;
glutAddMenuEntry
( ” W y j ś c i e ” , EXIT ) ;
#e l s e
glutAddMenuEntry
( ” A s p e k t
o b r a z u − c a l e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” A s p e k t
o b r a z u
1 : 1 ” , ASPECT 1 1 ) ;
glutAddMenuEntry
( ” W y j s c i e ” , EXIT ) ;
#e n d i f
//
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
g l u t A t t a c h M e n u
(GLUT RIGHT BUTTON ) ;
//
w p r o w a d z e n i e
p r o g r a m u
do
o b s ł u g i
p ę t l i
k o m u n i k a t ó w
g l u t M a i n L o o p
( ) ;
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)
Parametry left, right, bottom i top wyznaczają rozmiary górnej pod-
stawy bryły odcinania (jest to obszar bezpośrednio odwzorowywany na ob-
szar renderingu), a near i far wyznaczają 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 = −f ar.
Parametry near i far muszą mieć wartości dodatnie.
Macierz rzutowania perspektywicznego, tworzona przez funkcję glFrus-
tum i mnożona przez aktualnie wybraną macierz, ma postać:
2near
right−lef t
0
right+lef t
right−lef t
0
0
2near
top−bottom
top+bottom
top−bottom
0
0
0
f ar+near
f ar−near
2f ar·near
f ar−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ów near i far:
r =
f ar
near
1. Definiowanie sceny 3D
11
bottom
right
left
top
-near
-far
X
Y
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ście near nigdy 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łaszczyźnie YZ (
6
(top, 0, bottom)), a aspect jest stosunkiem szero-
kości do wysokości przedniej płaszczyzny odcinania, czyli górnej podstawy
ostrosłupa ograniczającego scenę 3D. Parametry zNear i zFar odpowiadają
parametrom near i far funkcji glFrustum.
1. Definiowanie sceny 3D
12
Macierz rzutowania perspektywicznego, tworzona przez funkcję gluPer-
spective i mnożona przez aktualnie wybraną macierz, ma postać:
ctg
f ovy
2
aspect
0
0
0
0
ctg
f ovy
2
0
0
0
0
zF ar+zN ear
zN ear−zF ar
2·zF ar·zN ear
zN ear−zF ar
0
0
−1
0
Wewnętrznie funkcja gluPerspective wykorzystuje do ustawienia ma-
cierzy rzutowania perspektywicznego funkcję glFrustum. Oto wzory prze-
kształcenia parametrów funkcji gluPerspective na parametry glFrustum:
lef t = −aspect · zN ear · tg
π
f ovy
360
right = aspect · zN ear · tg
π
f ovy
360
bottom = −zN ear · tg
π
f ovy
360
top = zN ear · tg
π
f ovy
360
W kolejnym przykładowym programie (plik szescian2.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 funkcja glFrustum.
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 funkcja glTranslatef, która wywoływana jest bezpośred-
nio po zainicjowaniu macierzy modelowania macierzą jednostkową (patrz
funkcja Display). 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 . h g . p l
J a n u s z G @ e n t e r . n e t . p l
∗/
#i n c l u d e <GL/ g l u t . h>
#i n c l u d e < 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
o k n o
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
A s p e c t = 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 ) ;
//
w y b ó r
m a c i e r z y
m o d e l o w a n i a
g l M a t r i x M o d e
(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
g l E n d
( ) ;
//
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
( ) ;
//
z a m i a n a
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 ( ) ;
}
//
z m i a n a
w i e l k o ś c i
o k n a
void
R esh ape
( 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
o k n o
g l V i e w p o r t
( 0 , 0 , width , h e i g h t ) ;
//
w y b ó r
m a c i e r z y
r z u t o w a n i a
g l M a t r i x M o d e
(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
( A s p e c t == ASPECT 1 1 )
{
//
w y s o k o ś ć
o k n a
w i ę k s z a
od
w y s o k o ś c i
o k n a
i f
( w i d t h < h e i g h t && w i d t h > 0 )
g l F r u s t u m
( − 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 ś ć
o k n a
w i ę k s z a
l u b
równa
w y s o k o ś c i
o k n a
i f
( w i d t h >= h e i g h t && h e i g h t > 0 )
g l F r u s t u m
( − 2 . 0 ∗ w i d t h / h e i g h t , 2 . 0 ∗ w i d t h / h e i g h t , − 2 . 0 , 2 . 0 , 1 . 0 , 5 . 0 ) ;
}
e l s e
g l F r u s t u m
( − 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
o k n o
c a s e FULL WINDOW :
A s p e c t = FULL WINDOW ;
R esha pe
( 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
c a s e ASPECT 1 1 :
A s p e c t = ASPECT 1 1 ;
R esha pe
( 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
c a s e EXIT :
e x i t
( 0 ) ;
}
}
i n t
main
( i n t
a r g c ,
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
o k n a
p r o g r a m u
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
o k n a
p r o g r a m u
#i f d e f WIN32
g l u t C r e a t e W i n d o w
( ” S z e ś c i a n
2 ” ) ;
#e l s e
g l u t C r e a t e W i n d o w
( ” S z e ś c i a n
2 ” ) ;
#e n d i f
//
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
o k n a
g l u t R e s h a p e F u n c
( Re sha pe ) ;
//
u t w o r z e n i e
menu
p o d r ę c z n e g o
g l u t C r e a t e M e n u
( 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
( ” A s p e k t
o b r a z u − c a ł e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” A s p e k t
o b 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
( ” A s p e k t
o b r a z u − c a l e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” A s p e k t
o b r a z u
1 : 1 ” , ASPECT 1 1 ) ;
glutAddMenuEntry
( ” W y j s c i e ” , EXIT ) ;
#e n d i f
//
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
g l u t A t t a c h M e n u
(GLUT RIGHT BUTTON ) ;
//
w p r o w a d z e n i e
p r o g r a m u
do
o b s ł u g i
p ę t l i
k o m u n i k a t ó w
g l u t M a i n L o o p
( ) ;
return
0 ;
}
Drugi program przedstawiający rzutowanie perspektywiczne (plik szes-
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 (parametr fovy funkcji
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 . h g . p l
J a n u s z G @ e n t e r . n e t . p l
∗/
#i n c l u d e <GL/ g l u t . h>
#i n c l u d e < 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 ) ;
//
w y b ó r
m a c i e r z y
m o d e l o w a n i a
g l M a t r i x M o d e
(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
g l E n d
( ) ;
//
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
( ) ;
//
z a m i a n a
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 ( ) ;
}
//
z m i a n a
w i e l k o ś c i
o k n a
void
R esh ape
( 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
o k n o
g l V i e w p o r t
( 0 , 0 , width , h e i g h t ) ;
//
w y b ó r
m a c i e r z y
r z u t o w a n i a
g l M a t r i x M o d e
(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 = w i d t h / ( 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
( f o v y , 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
( k e y == ’+ ’ && f o v y < 1 8 0 )
f o v y ++;
e l s e
//
k l a w i s z −
i f
( k e y == ’− ’ && f o v y > 0 )
f o v y −−;
//
o d r y s o w a n i e
o k n a
R esh ape
( 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
c a s e EXIT :
e x i t
( 0 ) ;
}
}
i n t
main
( i n t
a r g c ,
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
o k n a
p r o g r a m u
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
o k n a
p r o g r a m u
#i f d e f WIN32
g l u t C r e a t e W i n d o w
( ” S z e ś c i a n
3 ” ) ;
#e l s e
g l u t C r e a t e W i n d o w
( ” S z e s c i a n
3 ” ) ;
#e n d i f
//
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
o k n a
g l u t R e s h a p e F u n c
( Re sha pe ) ;
//
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
g l u t K e y b o a r d F u n c
( Keyboard ) ;
//
u t w o r z e n i e
menu
p o d r ę c z n e g o
g l u t C r e a t e M e n u
( 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 ) ;
#e n d i f
//
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
g l u t A t t a c h M e n u
(GLUT RIGHT BUTTON ) ;
//
w p r o w a d z e n i e
p r o g r a m u
do
o b s ł u g i
p ę t l i
k o m u n i k a t ó w
g l u t M a i n L o o p
( ) ;
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 funkcji gluLookAt oznaczają:
— 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 (plik szescian4.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 funkcjach Keyboard
(przyciski „+” i „-”) oraz SpecialKeys (klawisze kursora). Warto zauważyć,
że zmiany współrzędnych obserwatora, które reprezentują zmienne eyex,
eyey i eyez, są odwrotne niż można by się spodziewać. Przykładowo na-
ciśnięcie strzałki w dół powoduje zwiększenie o 0,1 zmiennej eyey, która
określa współrzędną Y położenia obserwatora. Jest to spowodowane tym,
że macierz modelowania, modyfikowana przy wywołaniu funkcji gluLookAt,
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 (funkcja Keyboard).
Funkcja obsługująca klawisze kursora oraz przyciski funkcyjne (w przykła-
dowym programie jest to funkcja SpecialKeys) ma trzy parametry:
— key - kod przycisku; zwracana jest jedna z poniższych wartości:
— GLUT KEY F1 - przycisk F1,
— GLUT KEY F2 - przycisk F2,
— GLUT KEY F3 - przycisk F3,
1. Definiowanie sceny 3D
21
— GLUT KEY F4 - przycisk F4,
— GLUT KEY F5 - przycisk F5,
— GLUT KEY F6 - przycisk F6,
— GLUT KEY F7 - przycisk F7,
— GLUT KEY F8 - przycisk F8,
— GLUT KEY F9 - przycisk F9,
— GLUT KEY F10 - przycisk F10,
— GLUT KEY F11 - przycisk F11,
— GLUT KEY F12 - przycisk F12,
— GLUT KEY LEFT - kursor w lewo,
— GLUT KEY UP - kursor do góry,
— GLUT KEY RIGHT - kursor w prawo,
— GLUT KEY DOWN - kursor w dół,
— GLUT KEY PAGE UP - przycisk Page Up
— GLUT KEY PAGE DOWN - przycisk Page Down,
— GLUT KEY HOME - przycisk Home,
— GLUT KEY END - przycisk End,
— GLUT KEY INSERT - 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 źró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 . h g . p l
J a n u s z G @ e n t e r . n e t . p l
∗/
#i n c l u d e <GL/ g l u t . h>
#i n c l u d e < 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
o k n o
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
A s p e c t = 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
e y e x = 0 ;
GLdouble
e y e y = 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 ) ;
//
w y b ó r
m a c i e r z y
m o d e l o w a n i a
g l M a t r i x M o d e
(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
g l u L o o k A t
( e y e x , e y e y , e y e z , 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
g l E n d
( ) ;
//
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
( ) ;
//
z a m i a n a
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 ( ) ;
}
//
z m i a n a
w i e l k o ś c i
o k n a
void
R esh ape
( 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
o k n o
g l V i e w p o r t
( 0 , 0 , width , h e i g h t ) ;
//
w y b ó r
m a c i e r z y
r z u t o w a n i a
g l M a t r i x M o d e
(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
( A s p e c t == ASPECT 1 1 )
{
//
w y s o k o ś ć
o k n a
w i ę k s z a
od
w y s o k o ś c i
o k n a
i f
( w i d t h < h e i g h t && w i d t h > 0 )
g l F r u s t u m
( − 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 ś ć
o k n a
w i ę k s z a
l u b
równa
w y s o k o ś c i
o k n a
i f
( w i d t h >= h e i g h t && h e i g h t > 0 )
g l F r u s t u m
( − 2 . 0 ∗ w i d t h / h e i g h t , 2 . 0 ∗ w i d t h / h e i g h t , − 2 . 0 , 2 . 0 , 1 . 0 , 5 . 0 ) ;
}
e l s e
g l F r u s t u m
( − 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
( k e y == ’+ ’ )
e y e z −= 0 . 1 ;
e l s e
//
k l a w i s z −
i f
( k e y == ’− ’ )
e y e z += 0 . 1 ;
//
o d r y s o w a n i e
o k n a
R esh ape
( 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
( k e y )
{
//
k u r s o r w l e w o
c a s e GLUT KEY LEFT :
e y e x += 0 . 1 ;
break ;
//
k u r s o r w g ó r ę
c a s e GLUT KEY UP :
e y e y −= 0 . 1 ;
break ;
//
k u r s o r w p r a w o
c a s e GLUT KEY RIGHT :
e y e x −= 0 . 1 ;
break ;
//
k u r s o r w d ó ł
c a s e GLUT KEY DOWN :
e y e y += 0 . 1 ;
break ;
}
//
o d r y s o w a n i e
o k n a
R esh ape
( 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
o k n o
c a s e FULL WINDOW :
A s p e c t = FULL WINDOW ;
R esha pe
( 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
c a s e ASPECT 1 1 :
A s p e c t = ASPECT 1 1 ;
R esha pe
( 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
c a s e EXIT :
e x i t
( 0 ) ;
}
}
i n t
main
( i n t
a r g c ,
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
o k n a
p r o g r a m u
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
o k n a
p r o g r a m u
#i f d e f WIN32
g l u t C r e a t e W i n d o w
( ” S z e ś c i a n
4 ” ) ;
#e l s e
g l u t C r e a t e W i n d o w
( ” S z e s c i a n
4 ” ) ;
#e n d i f
//
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
o k n a
g l u t R e s h a p e F u n c
( Re sha pe ) ;
//
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
g l u t K e y b o a r d F u n c
( 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
g l u t C r e a t e M e n u
( 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
( ” A s p e k t
o b r a z u − c a ł e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” A s p e k t
o b r a z u
1 : 1 ” , ASPECT 1 1 ) ;
glutAddMenuEntry
( ” W y j ś c i e ” , EXIT ) ;
#e l s e
glutAddMenuEntry
( ” A s p e k t
o b r a z u − c a l e
okno ” ,FULL WINDOW ) ;
glutAddMenuEntry
( ” A s p e k t
o b r a z u
1 : 1 ” , ASPECT 1 1 ) ;
glutAddMenuEntry
( ” W y j s c i e ” , EXIT ) ;
#e n d i f
//
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
g l u t A t t a c h M e n u
(GLUT RIGHT BUTTON ) ;
//
w p r o w a d z e n i e
p r o g r a m u
do
o b s ł u g i
p ę t l i
k o m u n i k a t ó w
g l u t M a i n L o o p
( ) ;
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,
[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