background image

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)

background image
background image

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)

background image

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

]

;

[

...

]