38
Programowanie
grafiki
www.sdjournal.org
Software Developer’s Journal 6/2006
OpenGL ES – programowanie
grafiki dla urządzeń mobilnych
I
lekroć pojawia się temat grafiki komputerowej,
mowa jest także o ogromnym postępie, jaki doko-
nuje się na tym polu. Za taki stan rzeczy w głów-
nej mierze odpowiedzialny jest przemysł komputero-
wej rozrywki, wdzierający się w każdy kąt za sprawą
niewielkich przenośnych urządzeń elektronicznych.
I bynajmniej nie są to już tylko dedykowane systemy
pokroju kieszonkowych konsol Nintendo, ale prak-
tycznie każde programowalne urządzenie, wyposa-
żone w przyzwoity wyświetlacz.
W punkcie wspólnym tych dwóch tendencji zro-
dziła się potrzeba opracowania biblioteki standaryzu-
jącej i wspomagającej proces tworzenia aplikacji 3D
dla tzw. handheldó'w.
Gwałtowny rozwój istniejącej od niedawna biblio-
teki OpenGL ES, powstałej jako skromny podzbiór
OpenGL, zawdzięczamy w głównej mierze przemy-
słowi telefonii komórkowej, pragnącemu poszerzyć
grono klientów o amatorów trójwymiarowych gier,
zdającemu sobie sprawę z niedoskonałości specy-
fikacji JSR-184. Obecnie OpenGL ES jest „jedynym
słusznym” interfejsem programowania na polu gra-
fiki trójwymiarowej i urządzeń przenośnych, a każ-
dy z „wielkich graczy” na rynku procesorów graficz-
nych posiada co najmniej jeden „mobilny” odpowied-
nik (np. Imageon 2388, GoForce4800).
Gry są specyficzną formą oprogramowania, przy
którym nadrzędnym celem jest wydajność przy jed-
noczesnej maksymalizacji efektów wizualnych. Za-
programowanie przyzwoicie wyglądającej sceny 3D
jest bowiem względnie łatwym zadaniem (internet
wszak huczy od rozmaitych przykładów); sztuką jest
dopiero uczynić ją przy tym wszystkim interaktywną.
Idąc dalej tym tropem, nie sposób ukryć, że w przy-
padku dzisiejszych mini-konsolek mamy do czynienia
z ograniczeniami, które będą starały się oddalać nas
od uzyskania przyzwoitego współczynnika framerate,
co spróbuję zasygnalizować w tym artykule, dotyczą-
cym najuboższej i na dzień dzisiejszy najbardziej rozpo-
wszechnionej wersji biblioteki: OpenGL ES 1.0.
Środowisko programistyczne
Uruchomienie własnego kodu na odpowiadającym
urządzeniu i obserwacja jego działania przynależą
do największych przyjemności programisty; w na-
szym przypadku należało by się do tego celu zaopa-
trzyć w któryś z „palmofonów” tudzież „kieszonkowy”
PC. Żaden nie należy jednak do najtańszych urzą-
dzeń, a ponadto okaże się, że na naszym rynku nie-
podzielnie króluje hardware w najlepszym przypadku
bogaty w specyfikację JSR-184, i to z software'ową
implementacją. Sensownym wyjściem jest natenczas
zadowolić się środowiskiem uruchomieniowym na
PC, oczekując na popularyzację telefonów tzw. trze-
ciej generacji, wyposażanych w chipsety 3D.
Aby skonfigurować takie środowisko, można wy-
korzystać Handheld SDK firmy Nvidia; do stworzenia
prostej aplikacji OpenGL ES wystarczy posłużyć się
dołączoną doń wtyczką-wizardem do Visual Studio.
Bardziej cierpliwym proponuję zainstalowanie Po-
werVR Mobile SDK; zawartość do eksploracji będzie
o niebo bardziej ciekawa niż w przypadku SDK'a Nvi-
dii, choć rzeczywista użyteczność tego pakietu objawi
się dopiero w konfrontacji z odpowiednim sprzętem.
Poza PC runtime environment znajdziemy kilka
implementacji OpenGL ES (np. Hybrid Rasteroid, In-
tel, czy wymieniony PowerVR); szczęśliwcy dyspo-
Łukasz Grządka
Autor jest studentem IV roku Informatyki na Politechnice
Gdańskiej. Obecnie pracuje jako programista dla firmy
Frontline Studios, Inc.
Kontakt z autorem: rac@nawiagames.com
Rysunek 1:
Gizmondo, konsola wykorzystująca
OpenGL|ES
Slownik pojęć
• OpenGL ES – OpenGL for Embedded System – od-
miana OpenGL rozwiajana pod kątem wykorzystania
w ograniczonym środowisku;
• framerate – liczna klatek wyświetlanych w jednej se-
kundzie;
• JSR-184 – Java3D Mobile API – specyfikacja pozwa-
lająca na realizację aplikacji wykorzystujących prostą
grafikę 3D, głównie w telefonach komórkowych;
• software implementation – tu: implementacja oparta
na głównych zasobach sprzętu (CPU);
• hardware implementation – tu: implementacja opar-
ta na dedykowanym sprzęcie (GPU)
40
Programowanie
grafiki
www.sdjournal.org
Software Developer’s Journal 6/2006
nujący urządzeniem wspomagającym ową bibliotekę bez tru-
du dobiorą odpowiadającą implementację. W przypadku plat-
form opartych na Windows CE i pochodnych będą oni w za-
sadzie skazani na używanie Microsoft Embedded Visual C++.
Tutaj mam dobrą wiadomość dla zwolenników języka C: jest
wielce prawdopodobne,że podobna aplikacja „prawdziwych
programistów” (C++) będzie działać wolniej niż Wasza. Dla-
czego? Urządzeniom zasilanym bateriami często towarzy-
szy procesor ARM, łączący w sobie wydajność z niskim pobo-
rem mocy (to drugie oznacza oczywiście dłuższą pracę urzą-
dzenia), a bieżąca wersja Embedded Visuala (4.0) zwyczajnie
„nie potrafi” wygenerować optymalnego kodu dla tego proce-
sora ze źródeł bazujących na złożonych klasach.
OpenGL ES a OpenGL
Biblioteka OpenGL ES w wersji 1.0 spotyka specyfikację
OpenGL 1.3, z której usunięto funkcje nadmiarowe, pozosta-
wiając tylko te najbardziej adekwatne dla ograniczonego ob-
szaru działania OpenGL ES. Czytelnik bez trudu odnajdzie ta-
belę bądź listę różnicującą oba interfejsy, stąd skupimy się tu-
taj tylko na najważniejszych.
Pierwszym istotnym ograniczeniem, z którym przyjdzie
nam się zderzyć programując urządzenie mobilne będzie
potencjalny brak wsparcia dla liczb zmiennoprzecinkowych;
emulowanie liczb zmiennopozycyjnych jest będzie ekstre-
malnie wolne, stąd najlepiej wystrzegać się ich stosowania
na szerszą skalę. Naprzeciw temu wychodzi zmiana pod-
stawowego typu danych: GLfloat został tu zastąpiony przez
GLfixed, całkowitą reprezentację liczby zmiennoprzecinko-
wej. Z punktu widzenia kompilatora GLfixed jest daną ty-
pu integer. Ponieważ nie będziemy na razie liczyć załamań
światła na liczbach wysokiej precyzji, „przestawienie się” na
fixed nie będzie trudne; uważać trzeba jedynie na przepeł-
nienia.
W dalszej kolejności zauważymy brak charaktery-
stycznych dla
OpenGL funcji glBegin() i glEnd();
odtąd
w każdym przypadku będziemy mieć do czynienia z ta-
blicami wierzchołków i paskami trójkątów oraz funkcjami:
glEnableClientState()/glDisableClientState().
Istnieje wie-
le teorii na temat generowania pasków trójkątów; w kontek-
ście OpenGL ES 1.0 trzeba pamiętać, aby obrany przez nas
algorytm generujący skupiał się raczej na wielokrotnym wy-
korzystaniu vertexów aniżeli na długości paska. Jeżeli przy-
zwyczailiśmy się do OpenGL i towarzyszącej jej
OpenGL Uti-
lity Library (glu)
, może brakować nam funkcji odpowie-
dzialnej za rzut perspektywiczny, nic nie stoi jednak na
przeszkodzie wygenerowania prostego substytutu:
OpenGL ES 1.0 jest częściowo tylko zanurzona w sprzę-
cie, dopiero wersja 1.1 bazuje na dedykowanych rozwiąza-
niach; za część transformacji odpowiedzialny będzie proce-
sor główny. Faktyczny paralelizm obliczeniowy jest stosunko-
wo trudny do uzyskania i wymaga pewnej wprawy oraz znajo-
mości architektury danego hardware'u.
W przypadku rotacji wypadało by się posłużyć nie-zmienno-
przecinkowymi odpowiednikami funkcji OpenGL, albo też ręcznie
Listing 1:
Przykładowe funkcje obrazujące działania na
typie Glfixed
glfixed
int2fixed
(
int
value
)
{
return
value
<<
16
;
}
;
glfixed
float2fixed
(
float
value
)
{
return
static_cast
<
glfixed
>
(
value
*
static_cast
<
float
>
(
0xFFFF
))
;
}
;
glfixed
mulFixed
(
glfixed
base
,
glfixed
factor
)
{
int64
result
=
base
*
factor
;
return
(
glfixed
)(
result
>>
16
)
;
}
;
glfixed
divFixed
(
glfixed
base
,
glfixed
divisor
)
{
int64
base64
=
(
int64
)
base
<<
16
;
int64
result
=
base64
/
(
int64
)
divisor
;
return
(
glfixed
)
result
;
}
;
Listing 2.
Fragment kodu renderującego „druciany” pasek
trójkątów
GLfixed
some_coords
[
count
]
;
[
...
]
glEnableClientState
(
GL_VERTEX_ARRAY
)
;
glVertexPointer
(
3
,
GL_FIXED
sizeof
(
GLfixed
)
*
3
,
some_coords
)
;
glDrawArrays
(
GL_LINE_STRIP
,
offset
,
strip_length
)
;
Listing 3:
Ładowanie macierzy perspektywy
#
define
Znear
0.1f
;
//wartosci perspektywy
#
define
Zfar
2000.0f
;
#
define
width
240
/
10
;
#
define
height
320
/
10
GLfixed
proj_matrix
[
4
][
4
]
;
//macierz rzutowania
memset
(
mat
,
0
,
sizeof
(
proj_matrix
))
;
proj_matrix
[
0
][
0
]
=
(
GLfixed
)(
0xFFFF
*
height
)
;
proj_matrix
[
1
][
1
]
=
(
GLfixed
)(
0xFFFF
*
width
)
;
proj_matrix
[
2
][
2
]
=
(
GLfixed
)(
0xFFFF
*
(
Zfar
/
(
Zfar
-
Znear
)))
;
proj_matrix
[
2
][
3
]
=
(
GLfixed
)(
0xFFFF
*
1.0f
)
;
proj_matrix
[
3
][
2
]
=
(
GLfixed
)(
0xFFFF
*
(
(
-
Zfar
*
Znear
)
/
(
Zfar
-
Znear
)))
;
glMatrixMode
(
GL_PROJECTION
)
;
glLoadMatrixx
(
&
proj_matrix
[
0
][
0
])
;
Rysunek 2.
Oświetlenie per vertex (z lewej) i za pomocą mapy
światła (z prawej)
41
OpenGL ES
www.sdjournal.org
Software Developer’s Journal 6/2006
preparować macierze transformacji tuż przed wywołaniem funk-
cji renderującej, podobnie jak w przypadku perspektywy:
//OpenGL
glRotatef(GLfloat angleX, GLfloat angleY, GLfloat angleZ);
//OpenGL ES
glRotatex(GLfixed angleX, GLFixed angleY, Glfixed angleZ);
Generalnie wszystkie ważniejsze „floatowe” funkcje OpenGL
mają swój „ograniczony” odpowiednik.
Tekstury
Ponieważ większość spotykanych w urządzeniach mo-
bilnych matryc LCD/TFT jest 16-bitowych (podobnie jak
wsparcie ze strony GPU), a ograniczenia pamięciowe dość
wyraźne, dedykowanym rozmiarem teksela jest 16 bitów;
bądź w formacie (565), bądź (4444) z kanałem przezroczy-
stości. Sugerowanym typem tekstury będzie typ skompre-
sowany, niestety w niektórych implementacjach nie obsłu-
guje on kanału alpha.
OpenGL ES wspiera co prawda tylko textury 2D, lecz jed-
nowymiarowe można łatwo zaemulować parametrem
texture _
height=1
. Stosowanie trójwymiarowych tekstur na dzień dzisiej-
szy nie miało by sensu ze względu na ich objętość. W przypad-
ku oświetlenia, należało by się cofnąć do czasów sprzed GeFor-
ca256, bo z reguły będzie ono realizowane po stronie software-
owej. Zamiast zastanawiać się, jak to optymalnie zaimplemento-
wać, o wiele lepiej jest zastosować popularną niegdyś sztuczkę
z mapą światła i multiteksturowanie. Niestety, w przypadku spo-
rej częsci obecnego sprzętu nawet pojedyńczy przebieg będzie
bardzo kosztowny – w takiej sytuacji jedynym wyjściem jest „wy-
palenie” mapy światła na teksturze podstawowej.
Rozsądnie jest też przygotować się na sytuację, w któ-
rej operacje 2D zostaną ubogo zaimplementowane w sprzę-
cie, albo nie zostaną zaimplementowane w ogóle. Tak jest
np. w przypadku chipsetu GoForce3D 4500 Nvidii, choć już
np. chipset Intel 2700G ograniczenia tego typu nie posiada.
Najlepiej wszak zawczasu wybić sobie z głowy bezpośredni
dostęp do bufora ramki.
Na szczęście łatwo jest zaemulować blitting teksturami –
jedyne na co trzeba uważać to wielkie płaty grafiki w jednym
przebiegu renderującym; jeżeli okaże się, że nie wszystkie
zmieszczą się w pamięci podręcznej GPU i trzeba będzie do-
ładowywać je z pamięci konwencjonalnej, zauważymy wyraź-
ny spadek wydajności.
Obierzmy za nasze płótno źródłowe teksturę 256x256.
Aby 'wykroić' z niego np. sprite'a o wymiarach 128x128 i nary-
sować na pozycji (10,30) ekranu, najprościej wykonać nastę-
pujące operacje:
Aby sprite nie „zginął” w scenie, zamiast zerować bu-
for głębokości można też wysterować współrzędną Z płótna
viewporu przy wyłączonej perspektywie.
Korzystając z tego sposobu można pokusić się o emulację
znanego np. z „komórek” dwuwymiarowego trybu „kafelkowe-
go” i zrobienie gry zupełnie 2D, trzeba tylko tylko pamiętać
o dobraniu odpowiednich współczynników skali dla viewpor-
tów różnego rozmiaru.
Podsumowanie
Sprzęt, w którym osadza się OpenGL ES jest z reguły wielo-
funkcyjny, obliczenia związane z grafiką trójwymiarową pozo-
stają kosztowne w kontekście zużycia baterii – trudno bylo by
zaakceptować konieczność ładowania telefonu po każdej par-
tii w „mobilnego” Quake'a 3, notabene zaprezentowanego na
sprzęcie Powered by ATI już 2 lata temu. Jest to jeden z głów-
nych powodów, dla którego tzw. handheldy 3D pozostają na
górnych półkach.
Nie przeszkadza to oczywiście rozwijać się specyfikacji:
w połowie zeszłego roku biblioteka OpenGL ES doczekała się
wersji 2.0, niosąc ze sobą programowalność mobilnych GPU
znaną ze 'stacjonarnych' chipsetów graficznych. Doom 3 w te-
lefonie zdaje się być tylko kwestią czasu.
Nieuczciwie było by utrzymywać, że OpenGL ES po-
wstała tylko z myślą o grach komputerowych, wszak dosko-
nałym polem działania dla tego API jest wizualizacja aplika-
cji Systemów Informacji Przestrzennej, pozostających w bli-
skim związku z łącznością bezprzewodową, a zatem i tele-
fonią komórkową. n
Listing 5.
Pseudo-blitting
//'kwadratowy' triangle strip, {0,0,128,128}
const
GLfixed
tex
[]
=
{
0
,
0xFFFF
>>
1
,
0xFFFF
>>
1
,
0xFFFF
>>
1
,
0
,
0
,
0xFFFF
>>
1
,
0
}
;
//lewa-górna ćwiartka viewportu 256x256
const
Glfixed
screen
=
{
-
0xFFFF
,
0xFFFF
,
0
,
0
,
0xFFFF
,
0
,
-
0xFFFF
,
0
,
0
,
0
,
0
,
0
}
;
[
...
]
//skala 1:1 , pozycja 10x30
glViewport
(
10
,
30
,
256
,
256
)
;
glClear
(
GL_DEPTH_BUFFER_BIT
)
;
glVertexPointer
(
3
,
GL_FIXED
,
sizeof
(
GLfixed
)
*
3
,
screen
)
;
glTexCoordPointer
(
2
,
GL_FIXED
,
sizeof
(
GLfixed
)
*
2
,
tex
)
;
glDrawArrays
(
GL_TRIANGLE_STRIP
,
0
,
4
)
;
W Sieci
• Specyfikacja OpenGLES
http://www.khronos.org/opengles/spec/
• Nvidia Handheld SDK
http://developer.nvidia.com/object/hhsdk_home.html
• The PowerVR Mobile SDK
http://www.pvrdev.com/Pub/MBX/
Literatura
• Dave Astle, Dave Durnil, OpenGL ES Game Development, Pre-
mier Press 2004;
• Lars M.Bishop, Feeding the Beast: How to Satiate Your Go-
Force While Differentiating Your Game, GDC Europe 2005
Listing 4:
Fragment kodu generujacego generującego
teksturę RGB565
GLuint
mytexture
;
unsigned
short
buffer
[
size
]
;
[
...
]