Sunteți pe pagina 1din 84

PARTEA a II-a : Implementarea de aplicaţii OpenGL

folosind biblioteca GLUT

OpenGL este o interfaţă de programare formată din circa 150 de funcţii care pot fi
folosite pentru modelarea si vizualizarea scenelor 3D. Două aspecte importante au
determinat creşterea continuă a popularităţii sale :
 Funcţiile OpenGL sunt independente de platforma hardware-software

 OpenGL oferă funcţii de sinteză de nivel înalt, care sunt realizate fie software fie

prin echipamente grafice specializate

In prezent există implementări OpenGL pentru sistemele de operare Microsoft Windows,


Unix şi IBM PS/2. Acestea se prezintă sub forma unor biblioteci integrate (sau care pot fi
integrate) în mediile de dezvoltare a aplicaţiilor. Deoarece OpenGL nu conţine funcţii de
gestiune a ferestrelor de afişare şi interacţiune cu utilizatorul, implementările OpenGL
sunt completate cu astfel de funcţii. O asemenea implementare extinsă este şi GLUT
(OpenGL Utilities Toolkit), la a cărei utilizare ne referim în această parte a îndrumarului.

Dintre funcţiile de sinteză de nivel înalt oferite de OpenGL menţionăm:


 Maparea texturilor: aplicarea de imagini pe suprafeţe 3D
 Eliminarea automată din imagini a părţilor nevizibile ale obiectelor prin algoritmul Z-
buffer ;
 Efecte de iluminare a scenelor 3D folosind diferite modele de iluminare şi una sau
mai multe surse de lumină;
 Simularea reflexiei si a transmisiei luminii ţinând cont de proprietăţile materialelor ;
 Transformarea din spaţiul 3D utilizator în spaţiul 2D ecran prin specificarea
matricelor de transformare, posibilitatea de a modifica poziţia şi dimensiunea
obiectelor în spaţiul 3D utilizator.
Exista de asemenea funcţii care permit generarea simplă a unor obiecte solide, curbe şi
suprafeţe de formă liberă precum şi funcţii de lucru cu imagini.

II.1. Convenţii de numire a funcţiilor, constantelor


şi tipurilor de date OpenGL

Numele funcţiilor OpenGL conţin prefixul gl (de exemplu glClearColor) iar


constantele prefixul GL_ (de exemplu GL_COLOR_BUFFER_BIT).
Declaraţiile funcţiilor OpenGL sunt de forma:
void glFunction{nr}{b s i f d}{v}(argumente);
nr - reprezintă numărul de argumente ale funcţiei
b sau s sau i sau f sau d sau v - specifică tipul argumentelor funcţiei, iar v dacă
argumentele funcţiei sunt date sub formă de vector (b – byte, s – short, i – int, f –
float, d – double, v – vector)

Exemple :

glVertex2i(1, 3);
glVertex2f(1.0, 3.0);
Primul apel furnizează coordonatele vârfurilor ca întregi pe 32 biti iar al 2-lea ca
numere reale (în formatul cu virgulă mobilă).

glColor3f(1.0, 0.0, 0.0);


GLfloat color_array[] = {1.0, 0.0, 0.0};
glColor3fv(color_array);
Funcţia glColor3f() setează culoarea de desenare şi are 3 parametri ce corespund
componentelor roşu, verde şi albastru, iar funcţia glColor3fv() setează culoarea de
desenare având ca parametru un vector ce conţine componentele culorii.

Tipuri de date OpenGL


Pentru un acelaşi tip de date în C, diferitele implementări OpenGL pot alege tipuri
diferite. Pentru portabilitate, indiferent de implementarea OpenGL se recomandă
utilizarea tipurilor de date definite în OpenGL. Acestea sunt prezentate in tabelul II.1.
OpenGL defineşte şi tipul GLvoid. Acesta este cel mai adesea folosit în
apelurile de funcţii OpenGL care acceptă pointeri la vectori de valori.

Tipul definit în
Sufix Tipul de date Corespondentul în C
OpenGL
b 8-bit integer signed char GLbyte
s 16-bit integer Short GLshort
i 32-bit integer int sau long GLint, GLsizei
f 32-bit floating-point Float GLfloat, GLclampf
d 64-bit floating-point Double GLdouble, GLclampd
ub 8-bitunsigned integer unsigned char GLubyte, GLboolean
us 16-bit unsigned integer unsigned short GLushort
unsigned int sau unsigned GLuint,GLenum,
ui 32-bit unsigned integer
long GLbitfield
Tabelul II.1. Tipuri de date OpenGL

II.2. Funcţii GLUT de realizare a interfeţei cu utilizatorul

II.2.1. Gestiunea ferestrelor

Iniţializare fereastră

void glutInit(int *argc, char **argv);

Funcţia glutInit() iniţializează variabilele interne ale pachetului de funcţii GLUT şi


procesează argumentele din linia de comandă. Ea trebuie sa fie apelată înaintea oricarei
alte comenzi GLUT. Parametrii funcţiei au aceeaşi semificaţie ca şi parametri funcţiei
main.

Iniţializare mod de afişare

void glutInitDisplayMode(unsigned int mode);

unde mode specifică modul de afişare:


 folosirea modelului RGBA (culoarea se specifică prin componentele sale roşu,
verde, albastru şi transparenţa sau opacitatea) sau a modelului de culoare bazat pe
indecşi de culoare. În general se recomandă folosirea modelului RGBA.
 folosirea unei ferestre cu un singur buffer sau cu buffer dublu, pentru realizarea
animaţiei.
 folosirea bufferului de adâncime pentru algoritmul z-buffer.

De exemplu, dacă se doreşte crearea unei ferestre cu buffer dublu ce foloseşte un model
de culoare RGBA şi buffer pentru algoritmul z-buffer, se va apela:

glutInitDisplayMode(GLUT_DOUBLE |GLUT_RGB |GLUT_DEPTH;


Iniţializare poziţie fereastră

void glutInitWindowPosition(int x, int y) ;


Funcţia glutInitWindowPosition specifică colţul stânga sus al ferestrei în coordonate
relative la colţul stânga sus al ecranului.

Iniţializare dimensiune fereastră

void glutInitWindowSize(int width, int height) ;


Funcţia glutInitWindowSize specifică dimensiunea în pixeli a ferestrei : lăţimea (width)
şi înălţimea (height).

Creare fereastră

int glutCreateWindow(char *string) ;


Funcţia glutCreateWindow creează o fereastra cu un context OpenGL. Ea intoarce un
identificator unic pentru fereastra nou creată. Fereastra nu va fi afişată înainte de apelarea
funcţiei glutMainLoop. Valoarea întoarsă de funcţie reprezintă identificatorul ferestrei,
care este unic.

Creare ferestre copil

int glutCreateSubWindow(int win, int x, int y, int width, int


height);
Funcţia creează o fereastră având ca părinte fereastra identificată de win, unde :
- win - reprezintă identificatorul ferestrei părinte;
- (x, y) - reprezintă colţul stânga sus al ferestrei (x şi y sunt exprimate în pixeli şi
sunt relative la originea ferestrei părinte);
- width - reprezintă lăţimea ferestrei (exprimată în pixeli);
- height - reprezintă înălţimea ferestrei (exprimată în pixeli);
Fereastra nou creată devine fereastra curentă. Funcţia întoarce identificatorul ferestrei
create.

Distrugere fereastră

void glutDestroyWindow(int win) ;


Funcţia distruge fereastra specificată de win. De asemenea, este distrus şi contextul
OpenGL asociat ferestrei. Orice subfereastră a ferestrei distruse va fi de asemenea
distrusă. Dacă win identifică fereastra curentă, atunci ea va deveni invalidă.

Selectarea ferestrei curente

void glutSetWindow(int win) ;


Funcţia selectează fereastra curentă ca fiind cea identificată de parametrul win.

Aflarea ferestrei curente


int glutGetWindow(void) ;
Funcţia întoarce identificatorul ferestrei curente. Funcţia întoarce 0 dacă nu există nici o
fereastră curentă sau fereastra curentă a fost distrusă.

Selectarea cursorului asociat ferestrei curente

void glutSetCursor(int cursor) ;


Funcţia modifică cursorul asociat ferestrei curente transformându-l în cursorul specificat
de parametrul cursor, care poate avea una din următoarele valori:
GLUT_CURSOR_RIGHT_ARROW, GLUT_CURSOR_LEFT_ARROW,
GLUT_CURSOR_WAIT, GLUT_CURSOR_HELP, GLUT_CURSOR_TEXT,
GLUT_CURSOR_CROSSHAIR, GLUT_CURSOR_UP_DOWN,
GLUT_CURSOR_LEFT_RIGHT, GLUT_CURSOR_TOP_SIDE,
GLUT_CURSOR_BOTTOM_SIDE, GLUT_CURSOR_LEFT_SIDE,
GLUT_CURSOR_RIGHT_SIDE, GLUT_CURSOR_TOP_LEFT_CORNER,
GLUT_CURSOR_TOP_RIGHT_CORNER,
GLUT_CURSOR_BOTTOM_RIGHT_CORNER,
GLUT_CURSOR_BOTTOM_LEFT_CORNER, GLUT_CURSOR_NONE,
GLUT_CURSOR_INHERIT (foloseşte cursorul asociat ferestrei părinte).

II.2. 2. Gestiunea meniurilor

Crearea meniurilor
Meniurile create cu ajutorul GLUT-ului sunt meniuri simple, pop-up în cascadă. În timp
ce un meniu este folosit el nu poate fi şters, nu i se pot adăuga alte opţiuni şi nu i se pot
şterge din opţiuni.
int glutCreateMenu(void (*func)(int value)) ;
Funcţia creează un nou meniu pop-up şi întoarce identificatorul său
(întreg unic). Identificatorii meniurilor încep de la valoarea 1. Valorile
lor sunt separate de identificatorii ferestrelor.
- parametrul func specifică o funcţie callback a aplicaţiei, care va
fi apelată de biblioteca GLUT la selectarea unei opţiuni din
meniu. Parametrul transmis funcţiei callback (value) specifică
opţiunea de meniu selectată.

Adăugarea unei opţiuni într-un meniu


void glutAddMenuEntry(char* name, int value) ;
Funcţia adaugă o nouă opţiune meniului curent. Opţiunea este adăugată la sfârşitul listei
de articole a meniului.
- name - specifică textul prin care va fi reprezentată opţiunea introdusă
- value - reprezintă valoarea transmisă funcţiei callback asociată meniului la
selectarea opţiunii respective

Adăugarea unui sbmeniu într-un meniu


void glutAddSubMenu(char* name, int menu) ;

Funcţia adaugă un submeniu la sfârşitul meniului curent.


- name - reprezintă numele submeniului introdus (textul prin care va fi afişat în
meniu)
- menu - reprezintă identificatorul submeniului.

Ştergerea unei opţiuni sau a unui submeniu

void glutRemoveMenuItem(int entry) ;


Funcţia şterge opţiunea sau submeniul identificat de parametrul entry. Opţiunile din
meniu de sub opţiunea ştearsă sunt renumerotate.

Distrugerea unui meniu


void glutDestroyWindow(int menu) ;
Funcţia distruge meniul specificat prin parametru. Distrugerea unui meniu nu are nici un
efect asupra submeniurilor. Dacă meniul distrus este cel curent, atunci meniul curent va
deveni invalid.

Setarea meniului curent


void glutSetMenu(int menu) ;
Funcţia setează meniul curent ca fiind cel identificat de parametrul menu.

Aflarea meniului curent


int glutGetMenu(void) ;
Funcţia întoarce identificatorul meniului curent. Funcţia întoarce 0 dacă nu există meniu
curent sau meniul curent a fost distrus.

Ataşare / detaşare meniu unui buton al mouse-ului


void glutAttachMenu(int button) ;
void glutDetachMenu(int button) ;
Funcţia glutAttachMenu ataşează meniul curent butonului mouse-ului specificat de
parametrul button. Funcţia glutDetachMenu detaşează butonul asociat meniului curent.
Prin ataşarea unui buton al mouse-ului meniului curent, meniul va fi derulat la apăsarea
butonului respectiv în poziţia curentă a cursorului.

II.2.3. Controlul evenimentelor de intrare

In categoria evenimentelor de intrare sunt incluse evenimentele provocate de


apăsarea unei taste sau a unuia dintre butoanele mouse-ului, de deplasarea mouse-ului şi
de redimensionarea ferestrei de afişare de către utilizator. Pentru fiecare dintre aceste
tipuri de evenimente programatorul trebuie să specifice o funcţie callback care va fi
apelată la producerea unui eveniment de tipul respectiv. Funcţiile GLUT menţionate în
continuare servesc acestui scop.

Redimensionarea ferestrei
void glutReshapeFunc(void (*func)(int width, int height)) ;

Parametrul funcţiei glutReshapeFunc indică funcţia callback care va fi apelată la


redimensionarea ferestrei. Parametrii width şi height transmişi funcţiei callback
reprezintă noile valori ale lăţimii şi înălţimii ferestrei.

Apăsarea / eliberarea unei taste


void glutKeyboardFunc(void (*func)(unsigned char key, int x, int
y)) ;
Parametrul func reprezintă funcţia callback care va fi apelată la apăsarea / eliberarea unei
taste care generează un caracter ASCII. Parametrul key al funcţiei callback reprezintă
valoarea ASCII. Parametrii x şi y indică poziţia mouse-ului la apăsarea tastei (poziţie
specificată în coordonate fereastră).

Apăsarea / eliberarea unui buton al mouse-ului


void glutMouseFunc(void (*func)(int button, int state, int x, int
y)) ;
Parametrul func specifică funcţia callback care va fi apelată la apăsarea / eliberarea unui
buton al mouseului.Parametrul button al funcţiei callback poate avea una din următoarele
valori: GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON sau
GLUT_RIGHT_BUTTON. Parametrul state poate fi GLUT_UP sau GLUT_DOWN
după cum butonul mouse-ului a fost apăsat sau eliberat. Parametrii x şi y reprezintă
poziţia mouse-ului la apariţia evenimentului. Valorile x şi y sunt relative la colţul stânga
sus al ferestrei aplicaţiei.

Deplasarea mouse-ului
void glutMotionFunc(void (*func)(int x, int y)) ;
Se specifică funcţia callback care va fi apelată la deplasarea mouse-ului în timp ce un
buton este apăsat. Parametrii x şi y reprezintă poziţia mouse-ului în momentul apăsării
tastei. Valorile x şi y sunt relative la colţul stânga sul al ferestrei aplicaţie.

II.2.4. Afisarea textelor

Pentru afişarea unui caracter se apeleaza functia glutStrokeCharacter :


void glutStrokeCharacter(void *font, int character);
- font specifică fontul vectorial folosit, care poate avea una din următoarele valori:
 GLUT_STROKE_ROMAN
 GLUT_STROKE_MONO_ROMAN
- character specifică caracterul ce va fi afişat

Exemplu
Funcţia output prezentata mai jos realizeaza afişarea unui text cu format, incepand dintr-
o pozitie specificata a ferestrei curente.

void output(GLfloat x, GLfloat y, char *format,...)


{
va_list args;
char buffer[200], *p;

va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
glPushMatrix();
glTranslatef(x, y, 0);
for (p = buffer; *p; p++)
glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
glPopMatrix();
}

II.3. Execuţia aplicaţiei.

Funcţia de afişare callback


void glutDisplayFunc(void (*func)(void)) ;
Parametrul funcţiei glutDisplayFunc specifică funcţia callback a aplicaţiei care va fi
apelată de GLUT pentru afişarea conţinutului iniţial al ferestrei aplicaţiei precum şi ori de
câte ori trebuie refăcut conţinutul ferestrei ca urmare a cererii explicite a aplicaţiei, prin
apelul funcţiei glutPostRedisplay().

Bucla de execuţie a aplicaţiei


void glutMainLoop(void) ;
Aceasta este ultima funcţie care trebuie apelată in funcţia main a aplicaţiei. Ea conţine
bucla de execuţie (infinită) a aplicaţiei în care aplicaţia aşteaptă evenimente. Orice
eveniment este tratat prin rutina callback specificată anterior în funcţia main, prin apelul
funcţiei GLUT specifice tipului de eveniment.

Execuţia unui proces în background


void glutIdleFunc(void (*func)(void)) ;
Parametrul transmis funcţiei glutIdleFunc este o funcţie callback care va fi executată in
perioadele în care nu există evenimente în curs de tratare sau în aşteptarea tratării -
funcţia idle. Dacă argumentul funcţiei glutIdleFunc este NULL atunci funcţia idle
existentă este dezactivată.
Terminarea afişării
O aplicaţie GLUT poate rula pe mai multe maşini. De exemplu, să presupunem
că programul principal este rulat pe o maşină client şi rezultatul procesării (imaginea
afişată) apare pe un terminal sau pe o staţie de lucru (server), care este conectat în reţea.
De obicei clientul adună o colecţie de comenzi într-un singur pachet înainte de a-l trimite
în reţea. Codul de reţea de la client nu permite detectarea momentului în care programul
grafic a terminat desenarea unui cadru sau a unei scene 3D. Astfel, clientul poate aştepta
la infint comenzi de desenare ca să completeze un pachet. Pentru a forţa clientul să
trimită pachetul chiar dacă nu este plin se foloseşte funcţia glFlush .
void glFlush(void);
În cazul în care nu există nici un client şi toate comenzile sunt executate pe server
functia glFlush nu va avea nici un efect. Pentru ca un program să funcţioneze corect atât
în reţea cât şi pe o singură maşina, se va include apelul functieie glFlush la sfârşitul
fiecărei scene. Funcţia glFlush nu aşteaptă terminarea desenării, ci doar forţează
începerea execuţiei desenării. Dacă folosirea funcţiei glFlush nu este suficientă pentru o
aplicaţie, atunci se va folosi şi funcţia glFinish. Aceasta funcţionează ca şi glFlush dar
aşteaptă răspuns de notificare de la echipamentul grafic de indicare a terminări desenarii.
glFinish se va folosi pentru sincronizarea task-urilor.

void glFinish(void);

Observaţie: folosirea excesivă a lui glFinish poate reduce performanţele aplicaţiei mai
ales dacă se lucrează în reţea. Dacă este suficientă folosirea funcţiei glFlush
se va folosi aceasta în locul funcţiei glFinish.

II.4. Crearea unei aplicaţii OpenGL folosind GLUT

Pentru a crea executabilul corespunzător unei aplicaţii OpenGL se va deschide


fişierul sursă *.C cu Microsoft Visual C++. În momentul în care se va compila
programul, mediul Visual C++ va genera automat un proiect în care va fi inclus fişierul
sursă anterior deschis. Pentru editarea legăturilor trebuie adăugate bibliotecile
opengl32.lib, glu32.lib şi glut32.lib. Setările corespunzătoare pentru această operaţie
sunt următoarele:
 Se selectează opţiunea Settings din meniul Project.
 In cutia de dialog afişată se selectează Link
 In zona de editare Object/library modules se adaugă cele trei biblioteci
de mai sus.
Rularea unui program OpenGL necesită de asemenea următoarele biblioteci cu legare
dinamică : opengl32.dll, glu32.dll, glut32.dll.

In fişierele sursă se va include fişierul header glut.h.

Exemplu 1
Programul din fişierul exemplu11.c afişează un dreptunghi centrat în fereastră.

/* fişierul exemplul1.c */

#include "glut.h" /* glut.h se află în directorul curent */

void display(void)
{
/* şterge toţi pixelii */
glClear (GL_COLOR_BUFFER_BIT);

/* afişează un dreptunghi cu interiorul alb avand colţurile în


punctele(0.25, 0.25, 0.0) şi (0.75, 0.75, 0.0) */
glColor3f (1.0, 1.0, 1.0);
glBegin(GL_POLYGON);
glVertex3f (0.25, 0.25, 0.0);
glVertex3f (0.75, 0.25, 0.0);
glVertex3f (0.75, 0.75, 0.0);
glVertex3f (0.25, 0.75, 0.0);
glEnd();

glFlush ();
}

void init (void)


{
/* selectează culoarea de fond */
glClearColor (0.0, 0.0, 0.0, 0.0);

/* iniţializează transformarea de vizualizare */


glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
}
/*
* declară dimensiunea iniţială a ferestrei, poziţia şi
modul de afişare (buffer singular şi RGBA).
* Deschide o fereastră cu titlul "hello".
* Apelează rutinele de iniţializare.
* Înregistrează funcţia callback de refacere a ferestrei.
* Se intră în bucla principală şi se procesează evenimentele.
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (250, 250);
glutInitWindowPosition (100, 100);
glutCreateWindow ("hello");
init ();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}

Exemplu 2
Programul din fişierul exemplu2.c tratează evenimentele de la mouse. Se afişează un
dreptunghi centrat în fereastra de afişare. La apăsarea butonului stânga al mouse-ul
dreptunghiul va fi rotit până la apăsarea butonului drept al mouse-ului.

/* fişierul exemplul2.c */

#include "glut.h" /* glut.h se află în directorul curent */


#include <stdlib.h>

static GLfloat spin = 0.0;

void init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glRotatef(spin, 0.0, 0.0, 1.0);
glColor3f(1.0, 1.0, 1.0);
glRectf(-25.0, -25.0, 25.0, 25.0);
glPopMatrix();
glutSwapBuffers();
}

void spinDisplay(void)
{
spin = spin + 2.0;
if (spin > 360.0)
spin = spin - 360.0;
glutPostRedisplay();
}

void reshape(int w, int h)


{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void mouse(int button, int state, int x, int y)


{
switch (button) {
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN)
glutIdleFunc(spinDisplay);
break;
case GLUT_RIGHT_BUTTON:
if (state == GLUT_DOWN)
glutIdleFunc(NULL);
break;
default:
break;
}
}

int main(int argc, char** argv)


{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize (250, 250);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
}

II.5. Afişarea obiectelor 3D predefinite

GLUT conţine funcţii pentru afişarea următoarelor obiecte 3D:

con icosaedru teapot


cub octaedru tetraedru
dodecaedru sfera tor

Aceste obiecte pot fi afişate prin familii de curbe sau ca obiecte solide.

Exemplu: funcţii de desenare cub, sferă si tor prin două familii de curbe şi ca solide.
Desenare cub de latură size prin două familii de curbe

void glutWireCube(GLdouble size);

Desenare cub solid de latură size

void glutSolidCube(GLdouble size);

Desenare sferă prin două familii de curbe

void glutWireSphere(GLdouble radius, GLint slices, GLint stacks);

Desenare sferă solidă

void glutSolidSphere(GLdouble radius, GLint slices, GLint


stacks);

Desenare tor prin două familii de curbe

void glutWireTorus(GLdouble innerRadius, GLdouble


outerRadius, GLint nsides, GLint rings);

Desenare tor solid

void glutSolidTorus(GLdouble innerRadius, GLdouble


outerRadius, GLint nsides, GLint rings);

Alte funcţii sunt:


void glutWireIcosahedron(void);
void glutSolidIcosahedron(void);
void glutWireOctahedron(void);
void glutSolidOctahedron(void);
void glutWireTetrahedron(void);
void glutSolidTetrahedron(void);
void glutWireDodecahedron(GLdouble radius);
void glutSolidDodecahedron(GLdouble radius);
void glutWireCone( GLdouble radius, GLdouble height, GLint
slices,GLint stacks);
void glutSolidCone(GLdouble radius, GLdouble height, GLint
slices,GLint stacks);
void glutWireTeapot(GLdouble size);
void glutSolidTeapot(GLdouble size);

Toate aceste obiecte sunt desenate centrate în originea sistemului de coordonate real.

În momentul în care se fac modificări asupra unui obiect complex poate apare
efectul de “pâlpâire” a imaginii. Pentru evitarea acestui efect se asociază ferestrei
aplicaţiei un buffer dublu. Astfel, într-un buffer se păstrează imaginea nemodificată
(imaginea ce este afişată pe ecran), iar în cel de-al doilea se construieşte imaginea
modificată. În momentul în care s-a terminat construirea imaginii modificate se
interschimbă buffer-ele (lucrul cu două buffere este asemănator lucrului cu mai multe
pagini video în DOS). Pentru interschimbarea bufferelor se foloseşte funcţia:
glutSwapBuffers :

void glutSwapBuffers(void) ;

II.6. Specificarea culorilor

II.6.1. Culoarea de desenare

Într-un program OpenGL mai întai trebuie setată culoarea de desenare şi apoi se
face desenarea efectivă a obiectelor. Cât timp culoarea de desenare nu se modifică, toate
obiectele vor fi desenate cu acea culoare.
Exemplu:
set_current_color(red);
draw_object(A);
draw_object(B);
set_current_color(green);
set_current_color(blue);
draw_object(C);
La execuţia secvenţei din exemplu, obiectele A şi B vor fi desenate cu roşu iar obiectul C
cu albastru. Comanda set_current_color(green) nu are nici un efect.

Pentru a seta o culoare de desenare se poate apela funcţia glColor3f :

void glColor3f(GLfloat red, GLfloat green, GLfloat blue) ;


Parametrii funcţiei specifică componentele roşu, verde şi albastru ale culorii ce va fi
setată. Ei au valori în intervalul [0.0 , 1.1].
Exemple:
glColor3f(0.0, 0.0, 0.0); /* negru */
glColor3f(1.0, 0.0, 0.0); /* roşu */
glColor3f(0.0, 1.0, 0.0); /* verde */
glColor3f(1.0, 1.0, 0.0); /* galben */
glColor3f(0.0, 0.0, 1.0); /* albastru */
glColor3f(1.0, 0.0, 1.0); /* magenta */
glColor3f(0.0, 1.0, 1.0); /* cyan */
glColor3f(1.0, 1.0, 1.0); /* alb */

Alte modalităţi de specificare a culorilor sunt prezentate în capitolul II.10.

II.6.2. Ştergerea ferestrei

Stergerea fondului ferestrei este necesar să fie efectuată înaintea începerii creerii unei noi
imagini.

Exemplu
Se afişează toţi pixelii ferestrei în culoarea negru
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
Prima comandă setează culoarea de ştergere negru şi comanda următoare şterge
întreaga fereastră folosind culoarea curentă de ştergere. Parametrul funcţiei glClear
indică bufferul care va fi şters. De obicei se setează o singură dată culoarea de ştergere la
începutul aplicaţiei şi apoi se apelează funcţia de ştergere de câte ori este necesar.

void glClearColor( GLclampf red, GLclampf green, GLclampf


blue, GLclampf alpha);
Valorile red, green, blue sunt în intervalul [0, 1]. Culoarea implicită de ştergere
este (0, 0, 0, 0) - negru. Parametrul alpha specifică opacitatea; valoarea sa implicită este
0.0.

void glClear(GLbitfield mask);


Funcţia setează bufferul indicat prin parametrul mask la valoarea specificată. Valorile
posibile ale parametrului mask sunt prezentate în tabelul II.2:
Mask Buffer-ul ce va fi şters
GL_COLOR_BUFFER_BIT Buffer-ul curent pentru setarea culorii de desenare
GL_DEPTH_BUFFER_BIT Buffer-ul de adăncime
Tabelul II.2.

II.7. Definirea primitivelor geometrice

Orice primitivă geometrică este definită printr-o secvenţă de puncte 3D numite


vârfuri. Fiecare vârf este reprezentat intern prin cele 3 coordonate (x, y, z). Pentru
vârfurile specificate de programator în 2D, sistemul asignează coordonatei z valoarea 0.
În OpenGL, linia înseamnă segment de dreaptă.
Poligoanele sunt suprafeţe mărginite de un contur poligonal închis format din
segmente de dreaptă. Segmentele de dreaptă sunt specificate prin vârfurile capetelor
segmentelor. OpenGL impune câteva restricţii asupra poligoanelor (figura II.1): laturile
poligoanelor nu se pot intersecta (poligoane simple) şi poligoanele trebuie să fie convexe.
Nu pot fi descrise poligoane cu treceri interioare (ele nu sunt convexe).

Figura II.1. Poligoane valide si nevalide

Deoarece în OpenGL vârfurile sunt întotdeauna puncte 3D, punctele ce formează


frontiera unui poligon nu este necesar sa fie coplanare. Dacă vârfurile unui poligon nu
sunt coplanare, atunci după rotaţii, modificarea poziţiei observatorului şi proiecţia în
ecran, punctele pot să nu mai formeze un poligon convex simplu.
Problema din figura II.2 poate fi evitată prin folosirea triunghiurilor, deoarece orice trei
puncte sunt coplanare.
Figura II.2

II.7.1. Definirea vârfurilor

În OpenGL toate primitivele geometrice sunt definite prin mulţimi ordonate de


vârfuri. Pentru a specifica un vârf se foloseşte funcţia:

void glVertex{234}{sifd}[v](TYPE coords);

Funcţia poate avea:


 2 parametri – vârful va fi specificat în 2D prin coordonatele sale (x, y)
 3 parametri – vârful va fi specificat în 3D prin coordonatele sale (x, y, z)
 4 parametri – vârful va fi specificat în 3D prin coordonatele sale omogene (x, y,
z, w)
Şi în acest caz TYPE reprezintă tipul coordonatelor vârfului (s - short, i – int , f - float, d -
double). Apelul funcţiei glVertex trebuie să fie făcut între perechea de comenzi glBegin
şi glEnd.

Exemplu
Modalităţi de definire a vârfurilor:

glVertex2s(2, 3);
glVertex3d(0.0, 0.0, 3.1415926535898);
glVertex4f(2.3, 1.0, -2.2, 2.0);

GLdouble dvect[3] = {5.0, 9.0, 1992.0};


glVertex3dv(dvect);

II.7.2. Puncte, linii şi poligoane

Pentru a crea o mulţime de puncte, o linie sau un poligon pornind de la vârfuri,


fiecare set de vârfuri trebuie să fie apelat între comenzile glBegin şi glEnd. Argumentul
funcţiei glBegin determină tipul primitivei ce va fi afişată :
void glBegin(GLenum mode);
Parametrul mode poate avea una din valorile (figura II.3):
 GL_POINTS
 GL_LINES
 GL_POLYGON
 GL_TRIANGLES
 GL_QUADS
 GL_LINE_STRIP
 GL_LINE_LOOP
 GL_TRIANGLE_STRIP
 GL_TRIANGLE_FAN
 GL_QUAD_STRIP
Figura II.3. Tipurile de primitive geometrice definite în OpenGL.

GL_LINES specifică afişarea mai multor segmente de dreaptă, câte unul pentru fiecare
pereche de vârfuri. Vârfurile sunt conectate în ordinea v0 – v1, v2 – v3, v4 – v5 etc. Dacă
este dat un număr impar de vârfuri, ultimul vârf este ignorat.
GL_LINE_STRIP specifică o polilinie de la vârful v0 la varful vn, conectând vârfurile
în ordinea dată;
GL_LINE_LOOP specifică un poligon, vârfurile fiind unite în ordinea dată; ultimul
vârf este conectat cu primul.
GL_POLYGON specifică un poligon de la vârful v0 la vârful vn-1 (n trebuie să fie cel
puţin 3). Poligonul trebuie să fie un poligon convex simplu. Pentru poligoanele concave
rezultatul afişării este nedefinit. De asemenea, vârfurile trebuie să fie situate în acelaşi
plan;
GL_QUADS specifică o serie de patrulatere separate. Primul patrulater este desenat
folosind vârfurile v0, v1, v2 şi v3, următorul v4, v5, v6 şi v7 etc. Dacă n nu este multiplu
de 4 vârfurile suplimentare sunt ignorate;
GL_TRIANGLES specifică o serie de triunghiuri separate;
GL_QUAD_STRIP specifică o serie de patrulatere conectate. Primul patrulater este
desenat folosind vârfurile v0, v1, v2 şi v3, Următorul refoloseşte ultimele două vârfuri
v2, v3 şi foloseşte următoarele două în ordinea v5 şi v6. Fiecare patrulater foloseşte
ultimele două vârfuri de la patrulaterul anterior. În fiecare caz n trebuie să fie cel puţin 4
şi multiplu de 2;
GL_TRIANGLE_STRIP specifică o serie de triunghiuri conectate. Primul triunghi
foloseşte vârfurile v0, v1 şi v2. Următorul v2, v1 şi v3, următorul v2, v3 şi v4. Se observă
că ordinea asigură ca toate triunghiurile să fie orientate la fel;
GL_TRIANGLE_FAN specifică o serie de triunghiuri conectate într-un vârf comun,
vârful v0. Primul triunghi este desenat folosind vârfurile v0, v1 şi v2, următorul foloseşte
v0, v2 şi v3, următorul v0, v3 şi v4 etc.

Funcţia glEnd marchează sfârşitul listei de vârfuri.

void glEnd(void);

Exemplu:
Primitiva redată în figura II.4 este specificată prin următoarea secvenţă :
glBegin(GL_POLYGON);
glVertex2f(0.0, 0.0);
glVertex2f(0.0, 3.0);
glVertex2f(3.0, 3.0);
glVertex2f(4.0, 1.5);
glVertex2f(3.0, 0.0);
glEnd();

Dacă se foloseşte GL_POINTS în loc de GL_POLYGON, primitiva va fi formată din 5


puncte.

Figura II.4

Restricţii de folosire pentru glBegin şi glEnd

Coordonatele vârfurilor se specifică folosind funcţia glVertex*. Pentru un vârf pot


fi furnizate informaţii suplimentare, cum ar fi: culoarea, vectorul normală, etc. In tabelul
II.3 sunt specificate funcţiile care pot fi apelate între glBegin şi glEnd :

Funcţia Comentarii
glVertex* Stabileşte coordonatele vârfului
Stabileşte culoarea curentă a
glColor*
vârfului
Setează indexul curent de
glIndex*
culoare
glNormal* Stabileste vectorul normală
glEvalCoord* Generează coordonatele
glCallList,
Execută lista (listele) de afişare
glCallLists
Setează coordonatele de
glTexCoord*
texturare
contrololează desenarea
glEdgeFlag*
muchiilor
Stabileşte proprietăţile de
glMaterial*
material
Tabelul II.3. Funcţii care pot fi apelate între glBegin şi glEnd
Dacă între apelurile funcţiilor glBegin şi glEnd apar apelurile altor funcţii GLUT
se va genera cod de eroare. Pot apare însă instrucţiuni ale limbajului de programare în
care este implementată aplicaţia.

Exemplu
Desenarea unui cerc prin linii.

#define PI 3.1415926535897;
GLint circle_points = 100;

glBegin(GL_LINE_LOOP);
for (i = 0; i < circle_points; i++)
{ angle = 2*PI*i/circle_points;
glVertex2f(cos(angle), sin(angle));
}
glEnd();

Acest exemplu nu reprezintă cel mai eficient mod de a desena un cerc, mai ales
dacă acest lucru se va face în mod repetat. Comenzile grafice folosite sunt rapide, dar
pentru fiecare vârf se calculează un unghi şi se apelează functiile sin şi cos. În plus apare
şi overhead-ul introdus de buclă. Dacă cercul va fi afişat de mai multe ori, pentru a
îmbunătăţi timpul de execuţie, se vor calcula o singură dată coordonatele vârfurilor şi se
vor salva într-un vector sau se va crea o listă de afişare (display list).
Între comenzile glBegin şi glEnd vârfurile sunt generate doar la întâlnirea
apelului glVertex*. În momentul apelului funcţiei glVertex*, sistemul asociază vârfului
respectiv culoarea curentă, coordonatele de texturare, vectorul normală etc.

Exemplu
Primul vârf este desenat cu roşu, iar al doilea şi al treilea cu albastru:

glBegin(GL_POINTS);
glColor3f(0.0, 1.0, 0.0); /* verde */
glColor3f(1.0, 0.0, 0.0); /* roşu */
glVertex(...);
glColor3f(1.0, 1.0, 0.0); /* galben */
glColor3f(0.0, 0.0, 1.0); /* albastru */
glVertex(...);
glVertex(...);
glEnd();

II.7.3. Dreptunghiuri în planul XOY

OpenGL furnizează o funcţie particulară, care permite desenarea unui dreptunghi cu


interiorul plin, în planul XOY:
void glRect{sifd}(TYPE x1, TYPE y1, TYPE x2, TYPE y2);
void glRect{sifd}v(TYPE* v1, TYPE* v2);
Funcţia afişează dreptunghiul definit de colţurile (x1, y1) şi (x2, y2). Dreptunghiul se află
în planul z=0 şi are laturile paralele cu axele x, respectiv y. Dacă este folosită varianta cu
vectori, colţurile dreptunghiului sunt specificate prin doi vectori, fiecare conţinând o
pereche (x, y). TYPE reprezintă tipul coordonatelor colţurilor dreptunghiului (s - GLshort,
i – GLint , f - GLfloat, d - GLdouble).

II.8. Specificarea atributelor de afişare ale primitivelor geometrice

În mod implicit un punct este desenat ca un singur pixel ecran, un segment de


dreapta este desenat ca o linie continuă de lăţime egală cu un pixel, iar poligoanele sunt
afişate având conturul cu linie continuă şi interior gol.

II.8.1. Dimensiunea punctelor

Pentru a specifica dimensiunea unui punct afişat se foloseşte funcţia glPointSize.


void glPointSize(GLfloat size);
Funcţia setează lăţimea în pixeli a punctelor ce vor fi afişate. Parametrul size reprezintă
dimensiunea punctului exprimată în pixeli ecran. Ea trebuie să fie mai mare ca 0.0, iar
valoarea sa implicită este 1.0.

II.8.2. Atributele liniilor


În OpenGL segmentele de dreaptă pot fi desenate având diferite lăţimi, ca linii
continue sau ca linii punctate sau întrerupte.

II.8.2.1.Lăţimea liniilor

void glLineWidth(GLfloat width);


Funcţia setează lăţimea în pixeli a liniilor ce vor fi afişate; width trebuie să fie mai
mare ca 0.0, iar valoarea implicită este 1.0.

II.8.2.2. Tipul liniilor

Pentru afişarea liniilor punctate sau întrerupte se va apela funcţia glLineStipple,


care defineşte şablonul de generare a liniilor, apoi se va activa folosirea liniilor punctate
sau întrerupte folosind funcţia glEnable:

void glLineStipple(GLint factor, GLushort pattern);


Funcţia setează şablonul curent de generare a liniilor.
- factor - este un multiplicator pentru fiecare bit din şablonul de generare a
liniei cu şablon. De exemplu, dacă factor = 3 atunci fiecare bit din şablon va fi
folosit de trei ori înainte ca următorul bit din şablon să fie folosit. Are valori în
intervalul [1, 255]
- pattern - reprezintă o succesiune de 16 biţi care va fi repetată pentru a genera
o linie cu şablon. Dacă bit = 1 se face afişarea, dacă bit = 0 nu se afişează;
ordinea de parcurgerea a biţilor în şablon este de la bitul cel mai puţin
semnificativ către bitul cel mai semnificativ. Şablonul poate fi multiplicat prin
folosirea parametrului factor, care multiplică fiecare succesiune de biţi cu
valori egale (1 sau 0) din şablon cu valoarea factor. Astfel, dacă în sablon apar
3 biţi consecutivi cu valoarea 1 şi factor = 2, se vor genera 6 biţi cu valoarea
1.
In figura II.5 sunt exemplificate diferite tipuri de linii obţinute prin alegerea valorilor
pattern şi factor.
Figura II.5

Generarea de linii cu şablon se va activa apelând funcţia glEnable cu parametrul


GL_LINE_STIPPLE şi va fi dezactivată prin apelul funcţiei glDisable cu acelaşi
argument.
void glEnable(Glenum cap);

void glDisable(GLenum cap);


cap este o constantă simbolică ce specifică o ‘capabilitate’ OpenGL. Ea poate avea una
din următoarele valori:
 GL_CULL_FACE - activează / dezactivează eliminarea părţilor nevizibile ale
obiectelor folosind metoda Back Face Culling.
 GL_DEPTH_TEST –activează / dezactivează calcularea valorilor buffer-ului de
adâncime.
 GL_LIGHTi - activează / dezactivează luarea în calcul a sursei de lumină şi la
calculul de iluminare.
 GL_LIGHTING – activează / dezactivează calcularea culorii corespunzătoare
fiecărui vârf.
 GL_LINE_STIPPLE – activează / dezactivează generarea liniilor folosind
şablon.
 GL_NORMALIZE - acitivează /dezactivează normalizarea vectorilor normală.
 GL_POLYGON_STIPPLE - activează / dezactivează folosirea şablonului
curent la afişarea poligoanelor.

Exemplu:
glLineStipple(1, 0x3F07);
glEnable(GL_LINE_STIPPLE);

În acest exemplu, pentru şablonul 0x3F07 (0011111100000111), o linie va fi desenată cu


3 pixeli vizibili, apoi 5 invizibili, 6 vizibili şi 2 invizibili (ordinea de parcurgere este de la
bitul cel mai puţin semnificativ către cel mai semnificativ). Dacă factor = 2, şablonul va
fi multiplicat: 6 pixeli vizibili, 10 invizibili, 12 vizibili şi 4 invizibili. Figura următoare
exemplifică linii desenate folosind diverse şabloane şi diferiţi factori de multiplicare.
Dacă nu se activează generarea liniilor cu şablon, afişarea va decurge ca şi cum pattern ar
fi 0xFFFF şi factor 1. Generarea liniilor cu şablon poate fi combinată cu setarea
diferitelor lăţimi de linie pentru a produce linii punctate de diferite lăţimi.
O modalitate de a privi generarea liniilor cu şablon este aceea că şablonul este
deplasat cu o poziţie de fiecare dată când este afişat un pixel (sau factor pixeli sunt
afişati, dacă factor este diferit de 1). Dacă sunt desenate o serie de segmente de dreaptă
conectate între apelurile funcţiilor glBegin şi glEnd, atunci şablonul va continua să fie
deplasat când se trece de la un segment la altul. În acest mod un şablon este folosit pentru
mai multe segmente de dreaptă conectate între ele. La execuţia funcţiei glEnd şablonul
va fi resetat şi dacă în continuare, înainte de a dezactiva generarea de segmente de
dreaptă cu şablon, mai sunt afişate segmente de dreaptă, atunci la generare se va folosi
şablonul de la început. Dacă segmentele de dreaptă sunt afişate folosind GL_LINES,
atunci şablonul va fi resetat pentru fiecare segment de dreaptă.

Programul « exemplul3 » prezentat în continuare exemplifică folosirea diferitelor


şabloane şi a diferitelor lăţimi pentru generarea liniilor.

/* exemplul3.c*/

#include "glut.h"

#define drawOneLine(x1,y1,x2,y2) glBegin(GL_LINES); \


glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();

void myinit (void)


{ /* fondul are culoarea neagră */
glClearColor (0.0, 0.0, 0.0, 0.0);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(50.0, 150.0, -50.0, 350.0, -1.0, 1.0);
}

void display(void)
{
int i;

glClear (GL_COLOR_BUFFER_BIT);
/* afişează toate liniile cu alb */
glColor3f (1.0, 1.0, 1.0);

/* prima linie: 3 linii, fiecare cu un şablon */


glEnable (GL_LINE_STIPPLE);
glLineStipple (1, 0x0101); /* linie punctată */
drawOneLine (50.0, 125.0, 150.0, 125.0);
glLineStipple (1, 0x00FF); /* linie întreruptă */
drawOneLine (150.0, 125.0, 250.0, 125.0);
glLineStipple (1, 0x1C47); /* întreruptă/punctată/întreruptă
*/
drawOneLine (250.0, 125.0, 350.0, 125.0);

/* linia a doua */
glLineWidth (5.0);
glLineStipple (1, 0x0101);
drawOneLine (50.0, 100.0, 150.0, 100.0);
glLineStipple (1, 0x00FF);
drawOneLine (150.0, 100.0, 250.0, 100.0);
glLineStipple (1, 0x1C47);
drawOneLine (250.0, 100.0, 350.0, 100.0);
glLineWidth (1.0);

/* linia a treia: 6 linii cu şablon întrerupt/punctat/întrerupt,


*/
/* ca parte a unei singure linii */
glLineStipple (1, 0x1C47);
glBegin (GL_LINE_STRIP);
for (i = 0; i < 7; i++)
glVertex2f (50.0 + ((GLfloat) i * 50.0), 75.0);
glEnd ();

/* linia a patra: 6 linii independente cu şablon */


/* întrerupt/punctat/întrerupt */
for (i = 0; i < 6; i++) {
drawOneLine (50.0 + ((GLfloat) i * 50.0),
50.0, 50.0 + ((GLfloat)(i+1) * 50.0), 50.0);
}

/* linia a cincea: 1 linie cu şablon întrerupt/punctat/întrerupt


*/
/* şi factor de multiplicare egal cu 5 */
glLineStipple (5, 0x1C47);
drawOneLine (50.0, 25.0, 350.0, 25.0);
glFlush ();
}

int main(int argc, char** argv)


{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (250, 250);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
myinit ();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}

II.8.3. Atributele poligoanelor

De obicei poligoanele sunt afişate prin contur continuu, dar pot fi afişate şi cu
conturul punctat sau numai prin vârfurile ce-l compun. Un poligon poate avea interiorul
generat uniform sau folosind un anumit şablon. Poligoanele sunt afişate astfel încât
pentru poligoanele adiacente care împart o latură sau un vârf, pixelii ce formează latura
respectivă sau vârful respectiv sunt afişaţi o singură dată – ei sunt incluşi numai într-un
singur poligon.

II.8.3.1. Modul de afişare


Un poligon este considerat ca având două feţe – cea din faţă şi cea din spate. El
poate fi generat ţinând cont de ambele feţe, numai de feţele din faţă (cele aflate spre
observator) sau numai de feţele din spate. În mod implicit, feţele din faţă şi din spate sunt
afişate în acelaşi mod. Pentru a modifica acest lucru sau pentru a afişa poligoanele prin
contur sau numai prin vârfurile ce-l compun se va folosi funcţia glPolygonMode :

void glPolygonMode(GLenum face, GLenum mode);


- face specifică feţele la care se referă parametrul mode; poate fi
GL_FRONT_AND_BACK (feţele faţă şi spate), GL_FRONT (feţele faţă), sau
GL_BACK (feţele spate);
- mode reprezintă modul de afişare a feţelor selectate; poate fi: GL_POINT
(poligoanele vor fi afişate prin vârfuri), GL_LINE (poligoanele vor fi afişate prin
contur), sau GL_FILL (poligoanele vor fi afişate cu interior plin)
În mod implicit, ambele tipuri de feţe sunt afişate cu interiorul plin.

Exemplu
Se cere afişarea faţelor din faţă cu interiorul plin, iar a celor din spate prin contur:

glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);
II.8.3.2. Orientarea feţelor

In mod implicit, feţele ale căror vârfuri sunt parcurse în sens invers acelor de ceas
sunt considerate feţe din faţă (orientate spre observator). Această convenţie poate fi
modificată de programator, apelând funcţia glFrontFace.

void glFrontFace(GLenum mode);


Parametrul mode specifică orientarea feţelor din faţă ale poligoanelor. Valoarea
GL_CCW (valoarea implicită) corespunde unei orientări în sensul invers acelor de ceas a
conturului unei feţe proiectate în coordonate de afişare. Valoarea GL_CW, indică o
orientare în sensul acelor de ceas a contururilor feţelor din faţă.

În cazul unei suprafeţe închise construită din poligoane cu o anumită orientare,


toate feţele spate nu vor fi vizibile niciodată – ele vor fi ascunse de feţele din faţă ale
poligoanelor. În această situaţie se poate mări viteza de afişare prin eliminarea
poligoanelor imediat ce s-a determinat dacă ele reprezintă feţe spate. În mod similar dacă
suntem în interiorul unui obiect atunci vor fi vizibile numai poligoanele care reprezintă
feţele spate. Pentru a specifica eliminarea feţelor faţă, respectiv feţelor spate se va folosi
funcţia glCullFace iar activarea eliminării feţelor respective se va face folosind funcţia
glEnable având ca parametru GL_ CULL_FACE. Pentru dezactivare se va apela
glDisable cu acelaşi argument.

void glCullFace(GLenum mode);


Funcţia indică ce poligoane vor fi eliminate înainte de a fi convertite în coordonate ecran.
Parametrul mode poate avea una din valorile :
- GL_FRONT : se elimină feţele faţă
- GL_BACK :se elimină feţele spate
- GL_FRONT_AND_BACK :se elimină atât feţele faţă cât şi feţele spate.

II.8.3.3. Tipul interiorului

În mod implicit poligoanele cu interiorul plin sunt generate folosind un şablon


plin. De asemenea pentru generarea interiorului unui poligon se poate folosi un şablon de
32x32 biţi, care se va specifica folosind funcţia glPolygonStipple.

void glPolygonStipple(const GLubyte *mask);

Funcţia defineşte şablonul curent pentru generarea interiorului poligoanelor. Argumentul


mask este un pointer catre un bitmap de 32×32 care este interpretat ca o masca de 0 şi 1.
Pixelul din poligon corespunzator valorii 1 din mască va fi afişat.
Folosirea şablonului curent la generarea poligoanelor este activată, respectiv
dezactivată, apelând funcţiile glEnable respectiv glDisable cu argumentul
GL_POLYGON_STIPPLE.

In programul din fişierul exemplu4.c este exemplificată folosirea şabloanelor


pentru generarea interioarelor poligoanelor.

/* exemplu4.c */

#include "glut.h"

void display(void)
{ GLubyte fly[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08};

GLubyte halftone[] = {
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55};
glClear (GL_COLOR_BUFFER_BIT);
glColor3f (1.0, 1.0, 1.0);
glRectf (25.0, 25.0, 125.0, 125.0);
glEnable (GL_POLYGON_STIPPLE);
glPolygonStipple (fly);
glRectf (125.0, 25.0, 225.0, 125.0);
glPolygonStipple (halftone);
glRectf (225.0, 25.0, 325.0, 125.0);
glDisable (GL_POLYGON_STIPPLE);
glFlush ();
}

void myinit (void)


{ glClearColor (0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 400.0, -100.0, 300.0, -1.0, 1.0);
}

int main(int argc, char** argv)


{ glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA);
glutInitWindowSize (350, 150);
glutInitWindowPosition (0, 0);
glutCreateWindow (argv[0]);
myinit ();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
In mod implicit, pentru fiecare octet primul bit este considerat bitul cel mai semnificativ,
Ordinea biţilor se poate modifica prin apelul funcţiei glPixelStore*().

II.8.3.4. Muchiile de contur

OpenGL permite desenarea de poligoane convexe, dar în practică apar deseori şi


poligoane concave. Pentru a desena poligoane concave, de regulă acestea se descompun
în poligoane convexe – de obicei triunghiuri, aşa cum se arată în figura II.7, şi apoi sunt
desenate aceste triunghiuri.
Dacă se descompune un poligon în triunghiuri şi se face afişarea triunghiurilor nu
se poate folosi functia glPolygonMode pentru a se desena numai laturile poligonului,
deoarece laturile triunghiurilor se află în interiorul poligonului. Pentru a rezolva această
problema se poate specifica dacă un vârf aparţine sau nu unei muchii de contur prin
păstrarea pentru fiecare vârf a unui bit. La afişarea poligonului în modul GL_LINE nu
vor fi desenate laturile care nu aparţin muchiilor de contur. În figura II.6 liniile punctate
reprezintă laturi false.
Figura II.6. Divizarea unui poligon concav

În mod implicit orice vârf aparţine unei muchii de contur. Acestă convenţie poate
fi controlată prin setarea flag-ului de apartenenţă la o muchie de contur cu ajutorul
funcţiei glEdgeFlag*. Funcţia se apelează între comenzile glBegin şi glEnd şi acţionează
asupra tuturor vârfurilor specificate după apelul sau până la următorul apel al funcţiei
glEdgeFlag. Ea se aplică doar vârfurilor care aparţin poligoanelor (GL_POLYGON),
triunghiurilor (GL_TRIANGLES) şi patrulaterelor (GL_QUADS). Nu are efect asupra
primitivelor de tipurile GL_QUAD_STRIP, GL_TRIANGLE_STRIP şi
GL_TRIANGLE_FAN.

void glEdgeFlag(GLboolean flag);


void glEdgeFlagv(const GLboolean *flag);
Dacă flag este GL_TRUE, atunci flag-ul de contur va fi setat la TRUE (valoarea
implicită ), şi orice vârf definit în continuare va fi considerat ca aparţinând muchiilor de
contur până la un nou apel al funcţiei glEdgeFlag* cu parametrul GL_FALSE.

Exemplu: Marcarea muchiilor de contur ale unui poligon

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_POLYGON);
glEdgeFlag(GL_TRUE);
glVertex3fv(V0);
glEdgeFlag(GL_FALSE);
glVertex3fv(V1);
glEdgeFlag(GL_TRUE);
glVertex3fv(V2);
glEnd();
Efectul acestor instrucţiuni este următorul (figura II.7):

Figura II.7.

II.8.3.5. Vectori normală

Un vector normală este un vector care are orientarea perpendiculară pe o


suprafaţă. Pentru o suprafaţă plană este suficientă o singură direcţie perpendiculară, ea
fiind aceeaşi pentru orice punct de pe suprafaţă, dar pentru o suprafaţă oarecare direcţia
vectorului normală poate diferi pentru fiecare punct. În OpenGL se poate specifica un
vector normală pentru fiecare vârf. Vectorii normală pot fi asociaţi numai vârfurilor. Ei
sunt necesari în calculul iluminării suprafeţelor aproximate prin reţele poligonale.
Funcţia glNormal* se apelează pentru a specifica normala curentă. Normala
curentă va fi asociată tuturor vârfurilor definite în continuare prin apeluri ale funcţiei
glVertex*. Dacă fiecare vârf are asociată o normală diferită se va proceda ca în exemplul
următor:

glBegin (GL_POLYGON);
glNormal3fv(n0);
glVertex3fv(v0);
glNormal3fv(n1);
glVertex3fv(v1);
glNormal3fv(n2);
glVertex3fv(v2);
glNormal3fv(n3);
glVertex3fv(v3);
glEnd();

void glNormal3{bsidf}(TYPE nx, TYPE ny, TYPE nz);

void glNormal3{bsidf}v(const TYPE *v);


Funcţia glNormal3 setează componentele vectorului « normală curentă » la valoarile
(nx,ny,nz), specificate prin parametri.
Versiunea glNormal3*v a acestei funcţii primeşte ca parametru un vector de 3 elemente
pentru a specifica normala curentă.
Versiunile b, s şi i scalează liniar valorile parametrilor în domeniul [-1.0,1.0].

Pe o suprafaţă, într-un punct dat, sunt doi vectori perpendiculari având orientări în
direcţii opuse. Prin convenţie, normala este acel vector care are direcţia spre exteriorul
suprafeţei. Dacă se doreşte folosirea vectorului normală orientat spre interiorul suprafeţei
unui obiect se va modifica fiecare vector normală de la (x, y, z) la (-x, -y, -z).
Vectorii normală pot fi specificaţi având orice lungime, dar ei vor trebui
normalizaţi înainte de efectuarea calculelor de iluminare. În general, este bine ca vectorii
normală să fie furnizaţi normalizaţi. Dacă se specifică vectori normală nenormalizaţi
sistemul OpenGL îi poate normaliza în mod automat. Pentru a cere această operaţie se va
apela funcţia glEnable având ca parametru GL_NORMALIZE. Implicit este dezactivată
normalizarea automată. În unele implementari ale OpenGL-ului normalizarea automată
necesită calcule suplimentare care pot reduce performanţele aplicaţiei.

II.9. Transformări ale obiectelor 3D în OpenGL

II.9.1. Etapele transformării din spaţiul 3D pe suprafaţa de afişare

OpenGL utilizează un sistem de coordonate 3D dreapta.


Secvenţa de transformări aplicate asupra punctelor prin care este definit un obiect 3D
pentru a fi afişat pe ecran este următoarea (figura II.8):
 transformarea de modelare şi vizualizare ( ModelView)
 transformarea de proiecţie, însoţită de decupare la marginile volumului vizual canonic
 împărţirea perspectivă
 transformarea în poarta de vizualizare din fereastra curentă
Figura II.8. Secvenţa de transformari aplicate vârfurilor în OpenGL

Procesul de transformări necesar producerii imaginii dorite pentru a fi redată pe o


suprafaţă de afişare este asemănator cu cel al efectuării unei fotografii. Aceşti paşi ar fi
următorii:
 Aranjarea scenei pentru a fi fotografiată în contextul dorit - transformarea de
modelare;
 Aranjarea aparatului de fotografiat şi încadrarea scenei -transformarea de
vizualizare;
 Alegerea lentilelor aparatului sau modificarea zoom-ului - transformarea de
proiecţie;
 Determinarea dimensiunii imaginii finale - transformarea în poarta de
vizualizare.

II.9.2. Transformarea de modelare şi vizualizare

O aceeaşi imagine a unui obiect se poate obţine în două feluri :


 poziţionând obiectul în faţa unei camere de luat vederi fixe
 poziţionând camera de luat vederi în faţa obiectului fix

Prima operaţie corespunde unei transformări de modelare a obiectului. Cea de a doua


operaţie corespunde unei transformări de vizualizare. Deoarece ambele pot fi folosite
pentru a obţine o aceeaşi imagine, sunt tratate împreună, ca o singură transformare
Transformarea de modelare are drept scop poziţionarea obiectelor în scena 3D.
Această transformare este necesară deoarece, în mod uzual, fiecare obiect este definit ca
obiect unitate într-un sistem de coordonate local. De exemplu, un cub poate fi definit ca
având latura de o unitate, centrat în originea unui sistem de coordonate carteziene 3D.
Reprezentarea la mărimea dorită, poziţionarea şi orientarea sa în scena 3D, care este
definită într-un sistem de coordonate global, poate să presupună o transformare
compusă din scalare şi rotaţie faţa de originea sistemului de coordonate local, urmată de o
translaţie. Prin această transformare se crează o instanţă a cubului, de aceea
transformarea de modelare se mai numeşte şi transformare de instanţiere.
Transformarea de modelare este o transformare compusă din transformări
geometrice simple care poate fi definită ca produs matricial, folosind funcţiile de
translaţie, rotaţie şi scalare oferite de OpenGL.
Transformarea de vizualizare este determinată de poziţia observatorului
(camera de luat vederi), direcţia în care priveşte acesta si direcţia sus a planului de
vizualizare. In mod implicit, observatorul este situat in originea sistemului de coordonate
în care este descrisă scena 3D, direcţia în care priveşte este direcţia negativa al axei OZ,
iar direcţia sus a planului de vizualizare este direcţia pozitiva a axei OY. Cu aceste valori
implicite, transformarea de vizualizare este transformarea identică.
Funcţia gluLookAt permite modificarea parametrilor impliciţi ai transformării de
vizualizare :
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,
GLdouble centerx,GLdouble centery,GLdouble centerz,
GLdouble upx,GLdouble upy,GLdouble upz);
- eyex, eyey, eyez reprezintă poziţia observatorului
- centerx, centery, centerz reprezintă direcţa în care se priveşte
- upx, upy, upz reprezintă direcţia vectorului « sus » al planului de vizualizare

Poziţia observatorului reprezintă punctul de referinţă al vederii, R, iar direcţia în care se


priveşte este direcţia normalei la planul de vizualizare, în R. Vectorul « sus » determină
direcţia pozitivă a axei verticale a sistemului de coordonate 2D ataşat planului de
vizualizare. Sistemul de coordonate 2D ataşat planului de vizualizare împreună cu
normala la plan formează sistemul de coordonate 3D al planului de vizualizare, care în
terminologia OpenGL este numit sistemul de coordonate observator.
Funcţia gluLookAt construieşte matricea transformării din sistemul de
coordonate obiect în sistemul de coordonate observator şi o înmulţeşte la dreapta cu
matricea curentă.
In OpenGL transformarea de modelare şi de vizualizare sunt exprimate printr-o
singură matrice de transformare, care se obţine înmulţind matricile celor două
transformări. Ordinea de înmulţire a celor două matrici trebuie să corespundă ordinei în
care ar trebui aplicate cele două transformări : mai întâi transformarea de modelare apoi
transformarea de vizualizare.
In OpenGL punctele 3D se reprezintă prin vectori coloană. Astfel, un punct (x,y,z)
se reprezintă în coordonate omogene prin vectorul [x w yw zw w]T. Daca A, B şi C sunt 3
matrici de transformare care exprimă transformările de aplicat punctului în ordinea A, B,
C, atunci secvenţa de transformări se exprimă matricial astfel :

[xw’ yw’ zw’ w’] T= C •B •A• [xw yw zw w]T


Transformarea de modelare şi vizualizare este o transformare compusă, reprezentată
printr-o matrice VM, ce se obţine înmulţind matricea transformării de vizualizare cu
matricea transformării de modelare. Fie V şi M aceste matrici. Atunci,
VM = V•M
Dacă coordonatele unui vârf în sistemul de coordonate obiect sunt reprezentate prin
vectorul [xo yo zo wo]T, atunci coordonatele vârfului în sistemul de coordonate
observator (“eye coordinates”) se obţin astfel:

[xe ye ze we]T = VM • [xo yo zo wo]T

Matricea VM este aplicată automat şi vectorilor normali.

II.9.3. Transformarea de proiecţie

Matricea de proiecţie este calculată în OpenGL în funcţie de tipul de proiecţie


specificat de programator şi parametrii care definesc volumul de vizualizare. Matricea de
proiecţie este matricea care transformă volumul vizual definit de programator într-un
volum vizual canonic. Această transformare este aplicată vârfurilor care definesc
primitivele geometrice în coordonate observator, rezultând coordonate normalizate.
Primitivele reprezentate prin coordonate normalizate sunt apoi decupate la marginile
volumului vizual canonic, de aceea coordonatele normalizate se mai numesc şi
coordonate de decupare. Volumul vizual canonic este un cub cu latura de 2 unităti,
centrat în originea sistemului coordonatelor de decupare. După aplicarea transformării de
proiecţie, orice punct 3D (din volumul vizual canonic) se proiectează în fereastra 2D
printr-o proiecţie ortografică (x’ =x, y’=y) condiţie necesară pentru aplicarea algoritmului
z-buffer la producerea imaginii.
Dacă coordonatele unui vârf în sistemul de coordonate observator sunt
reprezentate prin vectorul [xe ye ze we]T, iar P este matricea de proiecţie, atunci
coordonatele de decupare ale vârfului (“clip coordinates”) se obţin astfel:

[xc yc zc wc]T = P • [xo yo zo wo]T

Prezentăm în continuare funcţiile OpenGL prin care pot fi definite proiecţiile şi volumul
de vizualizare.

void glFrustum(GLdouble left, GLdouble right, GLdouble bottom,


GLdouble top, GLdouble near, GLdouble far);
Funcţia defineşte o proiecţie perspectivă cu centrul de proiecţie în poziţia
observatorului (punctul de referinţă al vederii). Volumul de vizualizare (figura II.9) este
delimitat prin « planul din faţă » şi « planul din spate », plane paralele cu planul de
vizualizare, definite prin distanţele lor faţă de poziţia observatorului (planul de
vizualizare). Planul din faţă va fi folosit ca plan de proiecţie. Lui i se ataşează un sistem
de coordonate 2D având axa verticală (sus) orientată ca şi axa verticală a planului de
vizualizare. Deschiderea camerei de luat vederi este determinată printr-o fereastră
rectangulară, cu laturile paralele cu axele, definită în planul de proiecţie.
- (left, bottom) şi (right, top) reprezintă colţurile ferestrei din planul din faţă
- znear, zfar reprezintă distanţele de la poziţia observatorului la planul din faţă,
respectiv spate. Ambele distanţe trebuie să fie pozitive.

Figura II.9. Volumul vizual perspectivă

Dacă left=right sau bottom=top sau znear=zfar sau znear<=0 sau zfar<=0 se semnalează
eroare.
Colţurile ferestrei 2D din planul de proiecţie, (left, bottom,-near) şi (right, top,-near) sunt
mapate pe colţurile stânga-jos şi dreapta-sus ale ferestrei 2D din sistemul coordonatelor
de decupare, adică (-1,-1,-1) şi (1,1,-1). Matricea de proiecţie este în acest caz :

 2 * near 
 right - left 0 A 0
 
 2 * near 
P  0 B 0
top - bottom
 
 0 0 C D
 
 0 0 -1 0 
unde
right  left top  bottom
A , B
right - left top - bottom

far  near 2 * far * near


C- , D-
far - near far - near

Funcţia glFrustum înmulţeşte matricea curentă cu matricea de proiecţie rezultată


şi memorază rezultatul în matricea curentă. Astfel, dacă M este matricea curentă şi P este
matricea proiecţiei, atunci glFrustum înlocuieşte matricea M cu M * P.

O altă funcţie care poate fi folosită pentru proiecţia perspectivă este gluPerspective :

void gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble


near, GLdouble far);

Funcţia gluPerspective creează un volum de vizualizare la fel ca şi funcţia glFrustum,


dar în acest caz el este specificat în alt mod. În cazul funcţiei gluPerspective acesta se
specifică prin unghiul de vizualizare în planul XOZ (deschiderea camerei de luat vederi)
şi raportul dintre lăţimea şi înălţimea ferestrei definite în planul de aproape (pentru o
fereastră pătrată acest raport este 1.0.)
- fovy reprezintă unghiul de vizualizare în planul XOZ, care trebuie să fie în
intervalul [0.0,180.0].
- aspect reprezintă raportul lăţime / înălţime al laturilor ferestrei din planul de
aproape; acest raport trebuie să corespundă raportului lăţime/înăţime asociat porţii
de afişare. De exemplu, dacă aspect = 2.0 atunci unghiul de vizualizare este de
două ori mai larg pe direcţia x decât pe y. Dacă poarta de afişare este de două ori
mai lată decât înălţimea atunci imaginea va fi afişată nedistorsionată.
- near şi far reprezintă distanţele între observator şi planele de decupare de-a lungul
axei z negative. Întotdeauna trebuie să fie pozitivi.

Proiecţia ortografică este specificată cu ajutorul funcţiei glOrtho :

void glOrtho( GLdouble left, GLdouble right, GLdouble bottom,


GLdouble top, GLdouble near, GLdouble far);

unde
- (left, bottom) şi (right, top) reprezintă colţurile ferestrei din planul din faţă
- near, far reprezintă distanţele de la poziţia observatorului la planul din faţă,
respectiv planul din spate

Volumul de vizualizare este în acest caz un paralelipiped dreptunghic delimitat de planul


din faţa şi cel din spate, plane paralele cu planul de vizualizare (perpendiculare pe axa z),
precum şi de planele sus, jos, dreapta , stânga. Direcţia de proiecţie este dată de axa Z a
sistemului de coordonate ataşat planului de vizualizare.
Fereastra definită în planul din faţă, (left, bottom,-near) şi (right, top,-near), este
mapată pe fereastra 2D din sistemul coordonatelor de decupare (-1,-1,-1) şi (1,1,-1).
Matricea de proiecţie este în acest caz :

 2 
 right - left 0 0 tx 
 
 2 
0 0 ty 
P top - bottom
 
 -2 
 0 0 tz 
far - near
 
 0 0 0  1 

unde
right  left top  bottom far  near
tx  - , ty  - , tz  -
right - left top - bottom far - near

Matricea curentă este înmulţită cu matricea de proiecţie rezultată, rezultatul fiind depus în
matricea curentă.

Tot pentru proiecţia ortografică poate fi folosită şi funcţia gluOrtho2D care


defineşte matricea de proiecţie ortografică în care near=-1 şi far=1.

void gluOrtho2D( Gldouble left, Gldouble right,


Gldouble bottom, Gldouble top);
unde
- (left, bottom) şi (right, top) reprezintă colţurile ferestrei din planul din faţă

II.9.4. Impartirea pespectivă

Prin această operaţie se obţin coordonate 3D în spaţiul coordonatelor de decupare,


pornind de la coordonatele 3D omogene :

xd = xc/wc , yd = yc/wc , zd= zc/wc

Coordonatele (xd, yd, zd) sunt numite « coordonate dispozitiv normalizate ».


Operaţia este denumită « împărţire perspectivă » deoarece numai în cazul unei proiecţii
perspectivă wc este diferit de 1.

II.9.5. Transformarea în poarta de vizualizare

Această transformare se aplică coordonatelor dispozitiv normalizate pentru a se


obţine coordonate raportate la sistemul de coordonate al fereastrei curente de afişare. Este
o transformare fereastă-poartă, în care fereastra este fereastra 2D din sistemul
coordonatelor normalizate, având colţurile în (-1, -1, -1)-(1, 1, -1), iar poarta este un
dreptunghi din fereastra de afişare care poate fi definit prin apelul funcţiei glViewport.

void glViewport(GLint px, GLint Py, GLsizei width, GLsizei


height );

- (px, py) reprezintă coordonatele în fereastră ale colţului stânga jos al porţii de
afişare (în pixeli). Valorile implicite sunt (0,0).
- width, height reprezintă lăţimea, respectiv înălţimea porţii de afişare. Valorile
implicite sunt date de lăţimea şi înălţimea ferestrei curente de afişare.

Fie (xd, yd, zd) coordonatele dispozitiv normalizate ale unui vârf şi (xw, yw, zw)
coordonatele vârfului în fereastra de afişare. Transformarea în poarta de
vizualizare este definită astfel :

xw = ox + (width/2)xd
yw = oy + (height/2)yd
zw = ((f-n)/2)zd + (n+f)/2

unde (ox, oy) reprezintă coordonatele centrului porţii de afişare.


n şi f au valorile implicite 0.0 respectiv 1.0, dar pot fi modificate la valori cuprinse în
intervalul [0,1] folosind funcţia DepthRange. Dacă n şi f au valorile implicite, atunci
prin transformarea în poarta de vizualizare volumul vizual canonic se transformă în cubul
cu colţurile de minim şi maxim în punctele (px, py, 0)-(px+width, py+height,1).
Calculul coordonatei zw este necesară pentru comparaţiile de adâncime în algoritmul z-
buffer.

Observaţie: în programul din fişierul exemplu5.c lăţimea şi înălţimea porţii de afişare


sunt specificate folosind lăţimea şi înălţimea ferestrei curente a aplicaţiei.

II.9.6. Funcţii de operare cu matrici de transformare

Transformarea de modelare şi vizualizare şi transformarea de proiecţie sunt


reprezentate prin matrici. Programatorul poate încărca una dintre aceste matrici cu o
matrice proprie sau cu matricea unitate, sau o poate înmulţi cu alte matrici.
Înaintea specificării unei transformări trebuie selectată matricea curentă, care va
fi modificată. Pentru aceasta se va apela funcţia glMatrixMode.
void glMatrixMode( GLenum mode );
Parametrul mode reprezintă matricea asupra căreia se vor efectua modificările ulterioare.
El poate avea una dintre următoarele valori:
- GL_MODELVIEW – matricea curentă va fi matriciea de modelare şi vizualizare
- GL_PROJECTION – matricea curentă va fi matricea de proiecţie.
- GL_TEXTURE – matricea curentă va fi matricea textură.

In programul din fişierul exemplu5.c, înaintea transformării de vizualizare, matricea


curentă este setată la matricea identitate (de 4 linii şi 4 coloane) cu ajutorul functiei
glLoadIdentity.
void glLoadIdentity(void);
Apelul funcţiei glLoadIdentity este necesar deoarece majoritatea funcţiilor de
transformare înmulţesc matricea curentă cu matricea specificată şi rezultatul este depus în
matricea curentă. Dacă matricea curentă nu este setată iniţial la matricea identitate, atunci
se vor folosi matricile de transformare anterioare combinate cu cea furnizată în acel
moment
Dacă se doreşte ca o anumită matrice să devină matricea curentă, atunci se va
folosi funcţia glLoadMatrix*.
void glLoadMatrix{fd}(const TYPE *m);
Valorile conţinute în vectorul m se memorează în matricea curentă, în ordinea
coloanelor :

Observaţie : Elementele unei matrici din limbajul C sunt memorate în ordinea


liniilor.
Funcţia glMultMatrix* se poate folosi pentru a înmulţi matricea curentă cu
matricea dată ca parametru funcţiei glMultMatrix*.
void glMultMatrix{fd}(const TYPE *m);
Funcţia înmulţeşte matricea specificată de parametrul m cu matricea curentă şi
rezultatul înmulţirii este stocat în matricea curentă. Dacă C este matricea curentă, atunci
rezultatul înmulţirii este C=C•m.

Pentru specificarea unei translaţii se poate folosi funcţia:

void glTranslatef(GLfloat x, GLfloat y, GLfloat z);


unde x, y şi z reprezintă componentele vectorului de translaţie
Matricea curentă este înmulţită cu matricea de translaţie şi rezultatul înlocuieşte
matricea curentă. Dacă M este matricea curentă şi T este matricea de translaţie, atunci M
este înlocuită cu M•T.
Daca matricea curenta este GL_MODELVIEW sau GL_PROJECTION, toate
obiectele afişate după apelul functiei glTranslatef vor fi translatate.

Pentru rotaţii se poate folosi funcţia:

void glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z);


- angle reprezintă unghiul de rotaţie exprimat în grade.
- x, y, z reprezintă coeficienţii directori ai axei în jurul căreia se realizează rotaţia.
Funcţia efectuează o rotaţie în sens trigonometric cu unghiul specificat, în jurul
vectorului ce uneşte originea cu punctul de coordonate (x, y, z).
Matricea curentă este înmulţită cu matricea de rotaţie şi rezultatul înlocuieşte matricea
curentă. Dacă M este matricea curentă şi R este matricea de rotaţie, atunci M este
înlocuită cu M•R.
Dacă matricea curentă este GL_MODELVIEW sau GL_PROJECTION, toate
obiectele afişate după apelul funcţiei glRotatef vor fi rotite.

Pentru scalare se poate folosi funcţia:


void glScalef(GLfloat x, GLfloat y, GLfloat z);
unde x, y, z reprezintă factorii de scalare de-a lungul axelor x, y şi z.
Matricea curentă este înmulţită cu matricea de scalare şi rezultatul înlocuieşte matricea
curentă. Dacă M este matricea curentă şi S este matricea de translaţie, atunci M este
înlocuită cu M*S.
Dacă matricea curentă este GL_MODELVIEW sau GL_PROJECTION, atunci toate
obiectele afişate după apelul funcţiei glScalef vor fi scalate.
Folosirea funcţiei glScale* diminuează performanţele calculului iluminării, deoarece
vectorii normală trebuie renormalizaţi după această transformare.

Exemplu
Considerăm următoarea secvenţă care specifică o secvenţă de trei transformări:

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixf(N); /* aplică transformarea N */
glMultMatrixf(M); /* aplică transformarea M */
glMultMatrixf(L); /* aplică transformarea L */
glBegin(GL_POINTS);
glVertex3f(v); /* afişează vârful v transformat*/
glEnd();

La execuţia secvenţei de mai sus, matricea de modelare şi vizualizare (MODELVIEW) va


conţine succesiv : I, N, N•M, şi în final N•M•L, unde I reprezintă matricea identitate.
Transformarea aplicată vârfului v va fi NML•v, unde NML = N•M•L.

II.9.7. Gestiunea stivelor de matrici


Un sistem OpenGL păstrează câte o stivă de matrici pentru fiecare mod matrice
selectat cu ajutorul funcţiei MatrixMode. Astfel, există :
 Stiva matricilor Modelview (cel puţin 32 matrici 4 x 4);
 Stiva matricilor de proiecţie (cel puţin 2 matrici 4 x 4) ;
 Stiva matricilor textură(cel puţin 2 matrici 4 x 4).
Matricea curentă este întotdeauna matricea din vârful stivei corespunzătoare
modului matrice curent.
O stivă de matrici este folositoare pentru construirea modelelor ierarhice în care sunt
construite obiecte complexe pornind de la obiecte simple. De exemplu, să presupunem că
se desenează o maşină şi există o singură rutină care desenează o roată. Această rutină
desenează roata într-o anumită poziţie şi orientare. Când se desenează maşina, rutina de
afişare a roţii se va apela de 4 ori, aplicându-se diferite transformări pentru a poziţiona
corect roţile. De exemplu, pentru desenarea primei roţi aceasta trebuie să fie translatată.
La desenarea celei de a doua roţi trebuie aplicată o altă translaţie (dar faţă de poziţia
iniţială – nu trebuie ţinut cont de prima translaţie) şi în acelaşi mod pentru desenarea
celorlalte roţi.
Deoarece transformările sunt păstrate ca matrici, o stivă de matrici furnizează un
mecanism util pentru efectuarea unei transformări ca apoi să se realizeze o altă
transformare fără a se mai ţine cont de transformarea anterioară. Toate operaţiile cu
matrici (glLoadMatrix, glMultMatrix, glLoadIdentity şi funcţiile care creează matrici
de transformare specifice) lucrează cu matricea curentă sau cu matricea din vârful stivei.
Pentru lucrul cu stivele de matrici OpenGL pune la dispoziţie funcţiile
glPushMatrix şi glPopMatrix.

void glPushMatrix(void);

Funcţia adaugă un nou element la stiva curentă şi memorează matricea curentă atât în
elementul din vârful stivei cât şi în intrarea următoare. Stiva curentă este determinată de
ultimul apel al funcţiei glMatrixMode. Dacă prin adăugare se depaşeşte capacitatea
stivei se generează eroare.

void glPopMatrix(void);

Funcţia elimină intrarea din vârful stivei şi înlocuieşte matricea curentă cu matricea care
era memorată în a 2-a intrare a stivei. Dacă stiva avea o singură intrare, apelul funcţiei
glPopMatrix generează eroare.

Exemplu
Programul din fişierul exemplu5.c afişează un cub care este mai întâi scalat
(transformarea de modelare). Transformarea de vizualizare constă dintr-o translaţie a
poziţiei observatorului pe axa z, în poziţia (0,0,5). Observatorul priveşte spre origine iar
direcţia axei sus este direcţia axei OY a sistemului de coordonate obiect.

/* exemplu5.c */
#include "glut.h"

void init(void)
{ glClearColor (0.0, 0.0, 0.0, 0.0);
}
void display(void)
{ glClear (GL_COLOR_BUFFER_BIT);
glColor3f (1.0, 1.0, 1.0);
glLoadIdentity ();
gluLookAt(0.0,0.0,5.0,0.0,0.0,0.0,0.0,1.0,0.0);
glScalef (1.0, 2.0, 1.0);
glutWireCube (1.0);
glFlush ();
}
void reshape (int w, int h)
{ glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
glMatrixMode (GL_MODELVIEW);
}
int main(int argc, char** argv)
{ glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
Transformarea de vizualizare şi modelare este creată în funcţia display unde este
apelată şi funcţia de afişare a cubului, glutWireCube. Astfel funcţia display poate fi
folosită în mod repetat pentru a afişa conţinutul ferestrei (de exemplu în cazul în care
fereastra este mutată pe ecran).
Transformarea de proiecţie şi transformarea în poarta de vizualizare sunt
specificate în funcţia Reshape, care este apelată de sistem ori de câte ori este
redimensionată fereastra aplicaţiei.
Efectul obţinut prin mutarea observatorului în spatele cubului (folosind
transformarea de vizualizare) se poate obţine şi prin deplasarea cubului, folosind o
transformare de modelare.

II.10. Iluminarea

II.10.1. Specificarea culorilor pentru lumină şi materiale

In capitolul II.6 ne-am referit la modul uzual în care sunt specificate culorile de
afişare a obiectelor. Funcţiile glColor3 specifică un triplet (R, G, B). Funcţiile glColor4
adaugă tripletului (R,G,B) o valoare de opacitate, numită valoarea alfa (A).
OpenGL permite utilizarea a două moduri de reprezentare a culorilor de afişare:
 modul RGBA: pentru fiecare pixel se memorează valorile R, G, B şi A.
 modul indexat: pentru fiecare pixel se memorează un număr, reprezentând un index
într-o tabela de culori.
Valorile R, G, B şi A variază în domeniul [0, 1].

În modul RGBA, pentru selectarea culorii curente de desenare se folosesc funcţiile


glColor*.
void glColor3{b s i f d ub us ui} (TYPE r, TYPE g, TYPE b);
void glColor4{b s i f d ub us ui} (TYPE r, TYPE g, TYPE b, TYPE
a);
void glColor3{b s i f d ub us ui}v (const TYPE* v);
void glColor4{b s i f d ub us ui}v (const TYPE* v);
Valoarea implicită a opacităţii este 1.0.
Pentru versiunile funcţiei glColor* care acceptă valori de tip real, domeniul
acestora trebuie să fie în intervalul [0, 1]. Valorile din afara intervalului [0,1] sunt
trunchiate la valori în intervalul [0,1] când sunt folosite ca parametri direcţi, dar nu sunt
trunchiate dacă sunt folosite pentru a modifica parametrii de material şi iluminare.
Valorile parametrilor de culoare specificate prin numere întregi sunt convertite în
valori reale, conform tabelulului II.4

Valoarea Valoarea minimă Valoarea Valoarea maximă


Tip
minimă se mapeaza la maximă se mapează la
b 1-byte integer -128 -1.0 127 1.0
s 2-byte integer -32,768 -1.0 32,767 1.0
i 4-byte integer -2,147,483,648 -1.0 2,147,483,647 1.0
u
unsigned 1-byte integer 0 0.0 255 1.0
b
u
unsigned 2-byte integer 0 0.0 65,535 1.0
s
u
unsigned 4-byte integer 0 0.0 4,294,967,295 1.0
i
Tabelul II.4. Conversia valorilor de culoare la numere reale

Componentele culorilor specificate prin funcţiile glColor* au semnificaţii diferite


după cum sunt folosite pentru lumisa emisă de o sursă sau lumina reflectată de o
suprafaţă. Pentru o sursă de lumină, valorile componentelor R, G şi B corespund unui
procent din intensitatea maximă pentru fiecare culoare. Dacă valorile R, G şi B sunt 1.0
atunci lumina este alb strălucitor. Dacă valorile sunt 0.5, culoarea este albă, iar la
jumătate din intensitate apare gri. Dacă R=G=1 şi B=0 lumina apare galbenă.
Pentru proprietăţile de material, valorile componentelor R, G şi B corespund
proporţiilor reflectate din acea culoare. Dacă R=1, G=0.5 şi B=0 pentru un material,
atunci acel material va reflecta toată lumina incidentă roşie, jumătate din lumina
incidentă verde şi nimic din lumina incidentă albastră. Cu alte cuvinte dacă o sursă de
lumină are componentele (LR, LG, LB) şi un material are componentele
corespunzătoare (MR, MG, MB) atunci ignorându-se toate celelalte efecte ale
reflectivităţii, lumina ce este percepută de ochiul observatorului este dată de formula
(LR*MR, LG*MG, LB*MB).
Analog dacă sunt două surse de lumină, cu componentele (R1, G1, B1) şi (R2,
G2, B2), atunci lumina rezultată va fi dată de (R1+R2, G1+G2, B1+B2). Dacă oricare
dintre componentele rezultate sunt mai mari ca 1, atunci componenta respectivă va fi
trunchiată la 1.

II.10.2. Specificarea modelului de iluminare

Modelele de iluminare care pot fi folosite în OpenGL sunt modelul Lambert şi


modelul Gouraud. Modelul de iluminare dorit se specifică cu ajutorul funcţiei
glShadeModel.
void glShadeModel (GLenum mode);

- mode specifică modelul de iluminare ce va fi selectat; el poate avea valorile:


 GL_SMOOTH: este valoarea implicită, prin care se selectează modelul Gouraud
 GL_FLAT : selectează modelul Lambert.

În modelul Gouraud culoarea fiecărui vârf este tratată în mod individual. Pentru un
segment de dreaptă culoarea se obţine prin interpolarea culorilor vârfului. Pentru un
poligon, culorile punctelor interioare se obţin prin interpolare pe baza culorilor vârfurilor.

Exemplu : în fişierul exemplul6.c se afişează un triunghi folosind modelul de iluminare


Gouraud.

/* fişierul exemplul6.c */
#include "glut.h"

void init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel (GL_SMOOTH);
}

void triangle(void)
{
glBegin (GL_TRIANGLES);
glColor3f (1.0, 0.0, 0.0);
glVertex2f (5.0, 5.0);
glColor3f (0.0, 1.0, 0.0);
glVertex2f (25.0, 5.0);
glColor3f (0.0, 0.0, 1.0);
glVertex2f (5.0, 25.0);
glEnd();
}

void display(void)
{
glClear (GL_COLOR_BUFFER_BIT);
triangle ();
glFlush ();
}

void reshape (int w, int h)


{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
if (w <= h)
gluOrtho2D (0.0, 30.0, 0.0, 30.0*(GLfloat) h/(GLfloat) w);
else
gluOrtho2D (0.0, 30.0*(GLfloat) w/(GLfloat) h, 0.0, 30.0);
glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv)


{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}

În modelul Lambert culoarea unei primitive este dată de culoarea unui singur
vârf. Culoarea unui segment de dreaptă este dată de culoarea celui de-al doile vârf.
Culoarea unui poligon este dată de culoarea unui vârf, conform tabelului II.5. Vârfurile
poligoanelor sunt numerotate începând cu 1. Pentru a evita confuzii în ceea ce priveşte
culoarea de desenare în modelul Lambert se va specifica o singură culoare pentru o
primitivă.

Vârful folosit pentru selectarea culorii


Tipul poligonului
poligonului
Poligon singular 1
triangle strip i+2
triangle fan i+2
independent triangle 3i
quad strip 2i+2
independent quad 4i

Tabelul II.5. Selectarea culorii pentru un poligon în modelul Lambert

II.10.3. Iluminarea unei scene 3D

Paşii de adăugare a iluminării într-o scenă sunt următorii :


1) Definirea vectorilor normală pentru fiecare vârf al tuturor obiectelor. Aceste
normale determină orientarea relativă a obiectelor faţă de sursele de lumină ;
2) Crearea, selectarea şi poziţionarea uneia sau a mai multor surse de lumină ;
3) Crearea şi selectarea unui model de iluminare care defineşte nivelul luminii
globale, ambiante şi poziţia observatorului ;
4) Definirea proprietăţilor de material pentru obiectele din scenă.

Exemplu
Programul din fişierul exemplul7.c realizează afişarea unei sfere cu iluminare folosind o
sursă de lumină. Normalele pentru sferă sunt definite de funcţia glutSolidSphere.

/* fişierul exemplul7.c */
#include "glut.h"

void init(void)
{
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel (GL_SMOOTH);

glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);


glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
}

void display(void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glutSolidSphere (1.0, 20, 16);
glFlush ();
}

void reshape (int w, int h)


{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

int main(int argc, char** argv)


{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
II.10.3.1. Crearea, poziţionarea şi activarea uneia sau a mai multor surse de lumină

În programul din fişierul exemplul7.c se foloseşte o singură sursă de lumină albă.


Poziţia sa este specificată prin apelul funcţiei glLightfv. Acest exemplu foloseşte
culoarea implicită pentru sursa de lumina 0 (GL_LIGHT0), care este albă ; dacă se
doreşte o sursă de lumină având o altă culoare se va folosi funcţia glLight*. Într-o scenă
pot fi incluse cel mult 8 surse de lumină diferite. Culoarea implicită a acestor surse de
lumină este negru. Ele pot fi poziţionate la o distanţă finită sau infinită faţă de scenă.
Sursele de lumină pot produce un fascicul de lumină mai îngust sau mai larg. După ce au
fost definite caracteriticile surselor de lumină, acestea trebuie să fie activate folosind
funcţia glEnable. De asemenea, trebuie apelată funcţia glEnable având ca parametru
GL_LIGHTING pentru a activa efectuarea calculelor de iluminare (în mod implicit
acestea sunt dezactivate).

Crearea surselor de lumină


Sursele de lumină au o serie de proprietăţi cum sunt culoarea, poziţia şi direcţia.
Funcţia folosită pentru a specifica toate proprietăţile luminii este glLight*.

void glLight{if}(GLenum light, GLenum pname, TYPE param);


void glLight{if}v(GLenum light, GLenum pname, TYPE *param);

Funcţia crează o sursă de lumină.


- light specifică sursa de lumină creată; poate avea una din următoarele valori:
GL_LIGHT0, GL_LIGHT1, ... , sau GL_LIGHT7;
- pname specifică caracteristicile luminii, conform tabelului II.6;
- param indică valorile caracteristicilor setate prin pname.

Valorile implicite ale parametrului param pentru valorile parametrului pname

pname Valoarea implicita Observatie


GL_AMBIENT (0.0, 0.0, 0.0, 1.0) Intensitatea ambiantă a luminii (RGBA)
GL_DIFFUSE (1.0, 1.0, 1.0, 1.0) Intensitatea luminii difuze (RGBA)
GL_SPECULAR (1.0, 1.0, 1.0, 1.0) Intensitatea luminii speculare (RGBA)
GL_POSITION (0.0, 0.0, 1.0, 0.0) Poziţia (x, y, z, w) a luminii
GL_SPOT_DIRECTION (0.0, 0.0, -1.0) Direcţia (x, y, z) spotului de lumină
GL_SPOT_EXPONENT 0.0 Exponentul spotului delumină
GL_SPOT_CUTOFF 180.0 Unghiul spotului de lumină
GL_CONSTANT_ATTENUATION 1.0 Factor de atenuare constant
GL_LINEAR_ATTENUATION 0.0 Factor de atenuare liniar
GL_QUADRATIC_ATTENUATION 0.0 Factor de atenuare cuadric
Tabelul II.6.

Valorile implicite din tabelul de mai sus pentru GL_DIFFUSE şi GL_SPECULAR se


aplică doar pentru sursa de lumină GL_LIGHT0. Pentru celelate surse de lumină,
valoarea implicită este (0.0, 0.0, 0.0, 1.0) atât pentru GL_DIFFUSE cât şi pentru
GL_SPECULAR.

Exemplu : definirea culorilor, poziţiei şi a factorilor de atenuare pentru o sursă de lumină

GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };


GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);


glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5);

O sursă de lumină poziţionată (care nu este plasată la infinit) poate acţiona ca un


spot – lumina va fi emisă sub forma unui con. Pentru a specifica unghiul dintre axele
conului şi o rază de pe suprafaţa conului se foloseşte parametrul GL_SPOT_CUTOFF.
Unghiul conului este de fapt dublul acestei valori după cum se arată în figura II.10:

Figura II.10.
Valoarea implicită a parametrului GL_SPOT_CUTOFF este 180.0
(caracteristica de lumină spot este dezactivată), adică lumina este emisă în toate direcţiile.
Valoarea parametrului GL_SPOT_CUTOFF trebuie să fie situată în intervalul [0.0,90.0]
(altfel are valoarea 180.0).

Exemplu : setarea parametrului GL_SPOT_CUTOFF la 45o

glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);

De asemenea, trebuie specificată o direcţie a spotului care determină axele conului de


lumină :

GLfloat spot_direction[] = { -1.0, -1.0, 0.0 };


glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);

Direcţia este specificată în coordonate obiect omogene. Valoarea implicită a direcţiei este
(0.0, 0.0, -1.0).
Surse de lumină multiple
Într-o scenă pot fi definite cel mult 8 surse de lumină. Constatele folosite pentru
referirea celor 8 surse de lumină sunt : GL_LIGHT0, GL_LIGHT1, GL_LIGHT2,
GL_LIGHT3, …, GL_LIGHT7. Dacă se specifică o altă sursă de lumină trebuie să i se
seteze parametrii corespunzători ca şi în cazul sursei de lumină GL_LIGHT0.

Exemplu : definirea unui spot de lumina albă :

GLfloat light1_ambient[] = { 0.2, 0.2, 0.2, 1.0 };


GLfloat light1_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light1_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light1_position[] = { -2.0, 2.0, 1.0, 1.0 };
GLfloat spot_direction[] = { -1.0, -1.0, 0.0 };

glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);


glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);
glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.5);
glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.5);
glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.2);
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 45.0);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2.0);

glEnable(GL_LIGHT1);

Observaţie: Pentru a activa fiecare sursă de lumină se foloseşte funcţia glEnable cu


argumentul GL_LIGHTING. Pentru a dezactiva iluminarea se apelează functia
glDisable cu acelaşi parametru.

II.10.3.2. Specificarea parametrilor modelului de iluminare

Funcţia glLightModel* defineşte parametrii modelului de iluminare:

void glLightModel{fi}(GLenum pname, TYPE param);

- pname reprezintă parametrul modelului de iluminare. El poate avea una din


următoarele două valori:
 GL_LIGHT_MODEL_LOCAL_VIEWER, caz în care param specifică
modul de calcul al unghiului de reflexie.
 GL_LIGHT_MODEL_TWO_SIDE - specifică feţele pentru care se face
calculul de iluminare. Nu are nici un efect în calculele de lumină pentru
puncte, linii şi bitmap-uri. Dacă param este 0 , calculele de iluminare se
fac numai pentru feţele faţă şi vor fi folosite numai proprietăţile de
material ale feţelor faţă. Altfel vor fi considerate atât feţele faţă cât şi
feţele spate. În acest caz vârfurile feţelor spate vor fi luminate folosind
proprietăţile de material ale feţelor spate (normalele vor fi inversate
înainte de a calcula iluminarea).
- param reprezintă valoarea ce va fi folosită în corelaţie cu pname.

void glLightModel{fi}v( GLenum pname, const TYPE *params );

- pname identifică modelul de iluminare. El poate avea una din următoarele


două valori:
 GL_LIGHT_MODEL_AMBIENT, caz în care params va conţine
componentele RGBA ale luminii ambiante.
 GL_LIGHT_MODEL_LOCAL_VIEWER are aceeaşi semnificaţie ca
şi în cazul funcţiei glLightModel{fi}; în acest caz param devine params;
 GL_LIGHT_MODEL_TWO_SIDE are aceeaşi semnificaţie ca şi în
cazul funcţiei glLightModel{fi}; în acest caz param devine params;
- param reprezintă valoarea ce va fi folosită în corelaţie cu pname.

În exemplul din fişierul exemplul7.c singurul element care este definit explicit
pentru modelul de iluminare este lumina ambiantă globală. De asemenea, modelul de
iluminare defineşte şi poziţia observatorului (la infinit sau la distanţă finită de scenă)
precum şi modul de efectuare a calculelor de iluminare a feţelor faţă respectiv spate ale
scenei. În programul din fişierul exemplul7.c se folosesc valorile implicite pentru acestea
– observatorul este plasat la infinit şi calculele de iluminare sunt efectuate pentru feţele
faţă. Folosirea unui observator local scenei creşte complexitatea calculelor deoarece
sistemul OpenGL trebuie să calculeze unghiul dintre observator şi fiecare obiect.
Folosind un observator plasat la infinit unghiul este ignorat şi rezultatele sunt ceva mai
puţin realiste.

În OpenGL modelul de iluminare are trei componente :


 Intensitatea luminii ambiante globale
 Poziţionarea observatorului (local scenei sau la infinit)
 Diferenţierea calculării iluminării pentru feţele obiectelor faţă, respectiv spate.

Lumina ambianta globală


Pentru a specifica intensitatea luminii ambiante globale ca RGBA se va folosi
parametrul GL_LIGHT_MODEL_AMBIENT după cum urmează :

GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };


glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

În exemplul de mai sus valorile folosite pentru lmodel_ambient sunt valorile implicite
pentru GL_LIGHT_MODEL_AMBIENT.

Observator local sau plasat la infinit


Poziţia observatorului afectează calculele pentru strălucirea produsă de lumina
speculară, mai precis intensitatea strălucirii într-un vârf depinde de normala în acel vârf,
direcţia de la vârf la sursa de lumină şi direcţia de la vârf la observator.
Cu un observator plasat la infinit direcţia dintre observator şi orice vârf rămâne
constantă. Un observator local va furniza rezultate mai aproape de realitate, dar direcţia
va trebui calculată pentru fiecare vârf, astfel că în acest caz performaţele scad. În mod
implicit observatorul este plasat la infinit.

Exemplu: definirea unui observator local în punctul de coordonate (0, 0, 0) (în


coordonate observator):

glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

Pentru a trece din nou la un observator plasat la infinit:

glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);

Calculul luminii pentru ambele feţe


În programul din fişierul exemplul7.c numai feţele faţă sunt iluminate.

Exemplu: calcularea iluminării pentru ambele feţe :

glLightModeli(LIGHT_MODEL_TWO_SIDE, GL_TRUE);
Dacă se doreşte să se calculeze apoi iluminarea numai pentru feţele faţă se va apela:

glLightModeli(LIGHT_MODEL_TWO_SIDE, GL_FALSE);

II.10.3.3. Definirea proprietăţilor de material pentru obiectele din scenă

Proprietăţile de material ale unui obiect determină modul în care el reflectă


lumina. Pentru proprietăţile unui material se pot specifica culorile ambiantă, difuză,
speculară, stralucirea sa şi culoarea oricărei lumini emise. Acestea pot fi setate folosind
funcţia glMaterialfv.

void glMaterial{if}[v](GLenum face, GLenum pname, TYPE param);


Funcţia specifică proprietăţile de material folosite în calculul iluminării.
- face reprezintă faţa obiectului pe care se vor aplica proprietăţile de material; poate
avea una dintre valorile GL_FRONT, GL_BACK sau
GL_FRONT_AND_BACK.
- pname indică proprietăţile de material;
- param indică una dintre valorile proprietăţilor selectate în parametrul pname.

Valorile posibile pentru pname sunt date în tabelul II.7.

pname Valoare implicita Observaţie


GL_AMBIENT (0.2, 0.2, 0.2, 1.0) Culoarea ambiantă a materialului
GL_DIFFUSE (0.8, 0.8, 0.8, 1.0) Culoarea difuză a materialului
GL_AMBIENT_AND_DIFFUSE Culoarea ambiantă şi difuză a materialului
GL_SPECULAR (0.0, 0.0, 0.0, 1.0) Culoarea speculară a materialului
GL_SHININESS 0.0 Exponentul specular
GL_EMISSION (0.0, 0.0, 0.0, 1.0) Culoarea emisă a materialului
GL_COLOR_INDEXES (0,1,1) Indicii de culoare ambiantă, difuză şi speculară
Tabelul II.7.

Reflexia difuză şi ambiantă


Parametrii GL_DIFFUSE şi GL_AMBIENT setaţi cu funcţia glMaterial*
afectează culoarea luminii ambiante şi difuze reflectate de un obiect.

Exemplu: asignarea simultană a aceleeaşi valori luminii reflectate difuze şi ambiante :

GLfloat mat_amb_diff[] = { 0.1, 0.5, 0.8, 1.0 };


glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
mat_amb_diff);

Reflexia speculară
OpenGL permite setarea culorii RGBA a strălucirii speculare (folosind
GL_SPECULAR) şi poate controla dimensiunea şi luminozitatea strălucirii (folosind
GL_SHININESS). Parametrului GL_SHININESS i se poate asocia o valoare în
domeniul [0.0, 128.0];cu cât valoarea este mai mare cu atât stălucirea este mai mică şi
mai luminoasă.

Exemplu :

GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };


GLfloat low_shininess[] = { 5.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);

Emisia
Prin asocierea unei culori RGBA parametrului GL_EMISSION se poate face ca
un obiect să pară că furnizează lumină de culoarea selectată.

Exemplu:

GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0};


glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);

Modificarea proprietăţilor de material


În fişierul din exemplul6.c toate vârfurile au asociate aceleaşi proprietăţi de
material. Uneori se doreşte să se asocieze vârfurilor aceluiaşi obiect proprietăţi de
material diferite. De obiecei într-o scenă sunt mai multe obiecte şi fiecare au asociate
proprietăţi de material diferite.

Exemplu : desenarea a opt sfere fiecare având alte proprietăţi de material :

GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 };


GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0 };
GLfloat mat_ambient_color[] = { 0.8, 0.8, 0.2, 1.0 };
GLfloat mat_diffuse[] = { 0.1, 0.5, 0.8, 1.0 };
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat no_shininess[] = { 0.0 };
GLfloat low_shininess[] = { 5.0 };
GLfloat high_shininess[] = { 100.0 };
GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0};

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/* reflexie difuză */
glPushMatrix();
glTranslatef (-3.75, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
glutSolidSphere();
glPopMatrix();

/* reflexie difuză şi speculară; strălucire redusă */


glPushMatrix();
glTranslatef (-1.25, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
glutSolidSphere();
glPopMatrix();

/* reflexie difuză şi speculară; stralucire ridicată */


glPushMatrix();
glTranslatef (1.25, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
glutSolidSphere();
glPopMatrix();

/* reflexie difuză, emisie */


glPushMatrix();
glTranslatef (3.75, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
glutSolidSphere();
glPopMatrix();

Funcţia glMaterialfv este apelată în mod repetat pentru a seta proprietăţile de


material pentru fiecare sferă. Ea este reapelată doar pentru acele proprietăţi care se
modifică de la o sferă la alta (adică de la un obiect la altul). Deoarece funcţia
glMaterial* are un cost de execuţie mare este bine să se minimizeze modificările
proprietăţilor de material. O altă tehnică de a minimiza costurile asociate cu modificarile
proprietăţilor de material este folosirea funcţiei glColorMaterial :

void glColorMaterial(GLenum face, GLenum mode);

Proprietăţile de material specificate de parametrul mode ale feţei specificate de


parametrul face ia întotdeauna valoarea culorii curente. Astfel, o modificare asupra
culorii curente (folosind funcţia glColor*) actualizează imediat proprietăţile de material
specificate.
- face poate avea una dintre următoarele valori: GL_FRONT, GL_BACK sau
GL_FRONT_AND_BACK (valoare implicită);
- mode poate avea una din următoarele valori: GL_AMBIENT, GL_DIFFUSE,
GL_AMBIENT_AND_DIFFUSE (valoare implicită), GL_SPECULAR sau
GL_EMISSION.

Observaţie: glColorMaterial actualizează proprietatea/proprietăţile de material


specificate de parametrul mode ale faţei/feţelor specificate de parametrul face.

După apelul funcţiei glColorMaterial trebuie apelată funcţia glEnable având ca


parametru GL_COLOR_MATERIAL. Apoi culoarea curentă poate fi modificată
folosind funcţia glColor* (sau alte proprietăţi de material folosind funcţia glMaterial*).

Exemplu:

glColorMaterial(GL_FRONT, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glColor3f(0.2, 0.5, 0.8);
/* afişează obiecte */
glColor3f(0.9, 0.0, 0.2);
/* afişează alte obiecte */
glDisable(GL_COLOR_MATERIAL);
Funcţia glColorMaterial se va folosi de câte ori se doreşte modificarea unui singur
parametru de material pentru majoritatea vârfurilor din scenă. Dacă se doreşte
modificarea mai multor parametri de material se va folosi funcţia glMaterial*.

II.11. Liste de afişare

II.11.1. Utilizarea listelor de afişare

O listă de afişare reprezintă un grup de comenzi OpenGL stocate pentru o


execuţie ulterioară. La invocarea unei liste de afişare comenzile din ea sunt executate în
ordinea în care sunt întâlnite. Majoritatea comenzilor OpenGL pot fi stocate într-o listă de
afişare sau pot apare în modul imediat (sunt executate imediat). Modul imediat de
programare poate fi combinat cu listele de afişare.
Listele de afişare pot îmbunătăţi programul deoarece instrucţiunile sunt stocate
pentru execuţii ulterioare. Este indicat să se folosească liste de afişare în cazul în care se
redesenează de mai multe ori aceeaşi figură geometrică sau dacă trebuie aplicat de mai
multe ori un set de modificări de stare.
O listă de afişare este un mod eficient şi convenabil de a combina un set de
comenzi OpenGL.

Exemplu: desenarea unui cerc format din 100 de segmente. Codul corespunzător
desenării cercului fără a folosi liste de afişare este următorul:

drawCircle()
{
GLint i;
GLfloat cosine, sine;

glBegin(GL_POLYGON);
for(i=0;i<100;i++){
cosine=cos(i*2*PI/100.0);
sine=sin(i*2*PI/100.0);
glVertex2f(cosine,sine);
}
glEnd();
}

Această metodă este ineficientă deoarece calculele trigonometrice vor fi efectuate de


fiecare dată când cercul va fi afişat. O altă modalitate este de a salva aceste coordonate
într-un vector şi a le folosi ori de câte ori este nevoie :

drawCircle()
{ GLint i;
GLfloat cosine, sine;
static GLfloat circoords[100][2];
static GLint inited=0;

if(inited==0){
inited=1;
for(i=0;i<100;i++){
circcoords[i][0]=cos(i*2*PI/100.0);
circcoords[i][1]=sin(i*2*PI/100.0);
}
}
glBegin(GL_POLYGON);
for(i=0;i<100;i++)
glVertex2fv(&circcoords[i][0]);
glEnd();
}
Şi această metodă prezintă dezavantajul incrementării şi testării variabilei i. Ceea ce se
doreşte este să se deseneze o singură dată cercul şi să se cunoască modul de redesenare
ulterior. Acest lucru este realizat prin folosirea listelor de afişare.

Exemplu: crearea unei liste de afişare

#define MY_CIRCLE_LIST 1

buildCircle()
{
GLint i;
GLfloat cosine, sine;

glNewList(MY_CIRCLE_LIST, GL_COMPILE);
glBegin(GL_POLYGON);
for(i=0;i<100;i++){
cosine=cos(i*2*PI/100.0);
sine=sin(i*2*PI/100.0);
glVertex2f(cosine,sine);
}
glEnd();
glEndList();
}

Codul pentru desenarea cercului se află între apelurile glNewList şi glEndList.


Aceste apeluri delimitează o listă de afişare. Argumentul MY_CIRCLE_LIST al funcţiei
glNewList este un index întreg care identifică în mod unic lista de afişare. Ulterior lista
de afişare poate fi executată folosind comanda glCallList:

glCallList(MY_CIRCLE_LIST);

Listele de afişare sunt un mod convenabil şi eficient de a vedea sub forma unui
nume o secvenţă de comenzi OpenGL. O listă de afişare conţine numai apeluri OpenGL.
Alte apeluri - ca în exemplul de mai sus, cum ar fi funcţiile C cos şi sin – nu sunt stocate
în listele de afişare. Coordonatele şi celelalte variabile (cum ar fi conţinutul vectorului)
sunt evaluate şi copiate în lista de afişare având valorile de la momentul compilării listei.
După compilarea listei aceste valori nu pot fi modificate. Lista de afişare poate fi ştearsă
şi se poate crea una nouă, dar o listă de afişare existentă nu poate fi editată.

Exemplu
Programul din fişierul exemplul8.c vizualizează unui tor din diferite unghiuri. Cel mai
eficient mod de a face acest lucru este de a păstra torul într-o listă de afişare. Apoi, de
câte ori se doreşte să se modifice poziţia observatorului se va modifica matricea
ModelView şi se va executa lista de afişare pentru desenarea torului.

/* fişierul exemplul8.c */

#include "glut.h"

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define M_PI 3.14

GLuint theTorus;

/* afişare tor */
void torus(int numc, int numt)
{
int i, j, k;
double s, t, x, y, z, twopi;

twopi = 2 * (double)M_PI;
for (i = 0; i < numc; i++) {
glBegin(GL_QUAD_STRIP);
for (j = 0; j <= numt; j++) {
for (k = 1; k >= 0; k--) {
s = (i + k) % numc + 0.5;
t = j % numt;

x = (1+.1*cos(s*twopi/numc))*cos(t*twopi/numt);
y = (1+.1*cos(s*twopi/numc))*sin(t*twopi/numt);
z = .1 * sin(s * twopi / numc);
glVertex3f(x, y, z);
}
}
glEnd();
}
}

/* creează lista de afişare pentru tor */


void init(void)
{
theTorus = glGenLists (1);
glNewList(theTorus, GL_COMPILE);
torus(8, 25);
glEndList();

glShadeModel(GL_FLAT);
glClearColor(0.0, 0.0, 0.0, 0.0);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f (1.0, 1.0, 1.0);
glCallList(theTorus);
glFlush();
}

void reshape(int w, int h)


{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30, (GLfloat) w/(GLfloat) h, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0);
}

/* la apăsarea tastei 'x' - se roteşte în jurul axei x


la apăsarea tastei 'y' - se roteşte în jurul axei y
la apăsarea tastei 'i' - se poziţioneaza torul în poziţia
originală
*/
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'x':
case 'X':
glRotatef(30.,1.0,0.0,0.0);
glutPostRedisplay();
break;
case 'y':
case 'Y':
glRotatef(30.,0.0,1.0,0.0);
glutPostRedisplay();
break;
case 'i':
case 'I':
glLoadIdentity();
gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0);
glutPostRedisplay();
break;
case 27:
exit(0);
break;
}
}

int main(int argc, char **argv)


{
glutInitWindowSize(200, 200);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutCreateWindow(argv[0]);
init();
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}

Utilizatorul poate roti torul în jurul axei OX sau OY prin apăsarea tastelor x
respectiv y. De câte ori se întamplă acest lucru este apelată funcţia callback keyboard
care înmulţeşte matricea de rotaţie de 30o în jurul axei x sau y cu matricea curentă
ModelView, după care este apelată funcţia glutPostRedisplay, care face ca funcţia
glutMainLoop să apeleze funcţia display şi să afişeze torul după prelucrarea altor
evenimente. La apăsarea tastei ‘i’ funcţia keyboard reface matricea iniţială ModelView şi
reafişează torul în poziţia sa iniţială. Funcţia display şterge fereastra şi apelează funcţia
glCallList pentru a executa comenzile din lista de afişare.
Dacă nu s-ar fi folosit liste de afişare funcţia display ar fi trebuit să conţină
comenzi de desenare a torului de fiecare dată când ar fi fost apelată.

O listă de afişare conţine numai comenzi OpenGL. În exemplul din fişierul


exemplul8.c sunt stocate apelurile funcţiilor glBegin, glVertex şi glEnd. Parametrii
apelurilor sunt evaluaţi şi valorile lor sunt copiate în lista de afişare la crearea sa. Toate
calculele trigonometrice pentru crearea torului sunt făcute o singură dată ceea ce duce la
creşterea performanţelor afişării.

Exemplu: aplicarea unor transformări unor obiecte geometrice şi apoi desenarea


rezultatului:

glNewList(1, GL_COMPILE);
afiseaza_obiectele_geometrice();
glEndList();

glLoadMatrix(M);
glCallList(1);

Dacă obiectele sunt transformate de fiecare dată în acelaşi mod este bine să se
păstreze matricea de transformare într-o listă de afişare.

Exemplu
In unele implementări, se vor putea îmbunătaţi performanţele prin transformarea
obiectelor în momentul definirii lor în loc de a le transforma de fiecare dată când sunt
afişate :

glNewList(1, GL_COMPILE);
glLoadMatrix(M);
afiseaza_obiectele_geometrice();
glEndList();

glCallList(1);

Listele de afişare au şi dezavantaje. Listele foarte mici nu vor îmbunătăţi execuţia


programului datorită overhead-ului execuţiei listei. Un alt dezavantaj constă din faptul că
o listă de afişare nu poate fi modificată şi conţinutul său nu poate fi citit. Dacă aplicaţia
necesită păstrarea datelor separat faţă de lista de afisare atunci va fi necesară memorie
suplimentară.

II.11.2. Crearea şi executarea listelor de afişare

Funcţiile glNewList şi glEndList sunt folosite pentru delimitarea unei liste de


afişare, care este executată prin apelul funcţiei glCallList având ca parametru
identificatorul său. În fişierul exemplul9.c lista de afişare este creată în funcţia init. În
funcţia display lista de afişare va fi apelată de 10 ori. Listele de afişare alocă memorie
pentru a stoca comenzile şi valorile oricărei variabilele necesare. Funcţia glTranslatef
din lista de afişare modifică poziţia următorului obiect ce va fi afişat. Apelul drawLine
este de asemenea afectat de funcţia glTranslatef care o precede.

/* fişierul exemplul9.c */

#include “glut.h”
#include <stdlib.h>

GLuint listName;

void init (void)


{
listName = glGenLists (1);
glNewList (listName, GL_COMPILE);
glColor3f (1.0, 0.0, 0.0); /* culoarea curentă roşu */
glBegin (GL_TRIANGLES);
glVertex2f (0.0, 0.0);
glVertex2f (1.0, 0.0);
glVertex2f (0.0, 1.0);
glEnd ();
glTranslatef (1.5, 0.0, 0.0); /* modificare poziţie */
glEndList ();
glShadeModel (GL_FLAT);
}
void drawLine (void)
{
glBegin (GL_LINES);
glVertex2f (0.0, 0.5);
glVertex2f (15.0, 0.5);
glEnd ();
}

void display(void)
{
GLuint i;

glClear (GL_COLOR_BUFFER_BIT);
glColor3f (0.0, 1.0, 0.0); /* culoarea curentă verde */
for (i = 0; i < 10; i++) /* afişare 10 triunghiuri */
glCallList (listName);
drawLine ();
glFlush ();
}

void reshape(int w, int h)


{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
gluOrtho2D (0.0, 2.0, -0.5 * (GLfloat) h/(GLfloat) w,
1.5 * (GLfloat) h/(GLfloat) w);
else
gluOrtho2D (0.0, 2.0*(GLfloat) w/(GLfloat) h, -0.5, 1.5);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)


{
switch (key)
{
case 27:
exit(0);
}
}

int main(int argc, char** argv)


{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(650, 50);
glutCreateWindow(argv[0]);
init ();
glutReshapeFunc (reshape);
glutKeyboardFunc (keyboard);
glutDisplayFunc (display);
glutMainLoop();
return 0;
}

Observaţie: Dacă o listă de afişare conţine comenzi de transformare trebuie avut grijă
care va fi efectul acestora în program mai târziu.

La un moment dat poate fi creată numai o singură lista de afişare. Cu alte cuvinte
trebuie terminată crearea unei liste de afişare (glNewList şi glEndList) înainte de a crea
o altă listă. Apelul funcţiei glEndList fără a fi apelat înainte funcţia glNewList va genera
eroarea GL_INVALID_OPERATION.

II.11.2.1. Crearea unei liste de afişare

Fiecare listă de afişare este identificată printr-un index întreg. La crearea unei liste
de afişare trebuie să se aibă grijă să nu se aleagă un index care este deja folosit, altfel se
va şterge lista de afişare existentă. Pentru evitarea acestui lucru se va folosi
funcţia glGenLists ce va genera unul sau mai mulţi indici nefolosiţi :

GLuint glGenLists(GLsizei range);

Funcţia alocă un domeniu de range numere continue nefolosite ca indici pentru liste de
afişare. Valoarea întoarsă de funcţie reprezintă indicele de început a blocului de indici
nefolosiţi. Indicii returnaţi vor fi marcaţi ca folosiţi astfel ca apelurile ulterioare ale
funcţiei glGenLists nu vor întoarce aceşti indici până nu vor fi şterşi. Funcţia întoarce 0
dacă nu este disponibil numarul de indici ceruţi sau dacă range este 0.

Exemplu: alocarea unui singur index; dacă el este liber va fi folosit pentru a crea o nouă
listă de afişare :

listIndex = glGenLists(1);
if (listIndex != 0) {
glNewList(listIndex,GL_COMPILE);
...
glEndList();
}
Observaţie: Indexul 0 nu reprezintă un index valid pentru lista de afişare.

void glNewList (GLuint list, GLenum mode);

Funcţia specifică începerea unei liste de afişare. Funcţiile OpenGL care vor fi apelate
(până la întâlnirea funcţiei glEndList care marchează sfârşitul listei de afişare) sunt
stocate în lista de afişare, excepţie facând câteva funcţii OpenGL care nu pot fi stocate.
Aceste funcţii restricţionate sunt executate imediat în timpul creării listei de afişare.
- list este un întreg pozitiv diferit de 0 care identifică în mod unic lista de afişare.
- valorile posibile ale parametrului mode sunt GL_COMPILE şi
GL_COMPILE_AND_EXECUTE. Se foloseşte valoarea GL_COMPILE dacă
nu se doreşte executarea comenzilor OpenGL la plasarea lor în lista de afişare;
pentru execuţia imediată în momentul plasării în lista de afişare, pentru folosiri
ulterioare, se va secifica parametrul GL_COMPILE_AND_EXECUTE.

void glEndList (void);

Funcţia marchează sfârşitul unei liste de afişare.

La crearea unei liste de afişare ea va fi stocată împreună cu contextul OenGL


curent. Astfel, dacă contextul va fi distrus, de asemenea lista de afişare va fi distrusă. În
unele sisteme este posibil ca listele de afişare să partajeze contexte multiple. În acest caz
lista de afişare va fi distrusă în momentul în care ultimul context din grup este distrus.
La crearea unei liste de afişare în ea vor fi stocate doar valorile expresiilor. Dacă
valorile dintr-un vector sunt modificate, valorile din lista de afişare nu vor fi modificate.

Exemplu
Se crează o listă de afişare ce conţine o comandă care setează culoarea curentă de afişare
negru
(0.0, 0.0, 0.0). Modificarea ulterioară a valorii vectorului color_vector în roşu (1.0, 0.0,
0.0) nu
are nici un efect asupra listei de afişare deoarece aceasta conţine valorile de la crearea sa.
GLfloat color_vector[3] = {0.0, 0.0, 0.0};
glNewList(1, GL_COMPILE);
glColor3fv(color_vector);
glEndList();
color_vector[0] = 1.0;

Într-o listă de afişare nu pot fi stocate şi executate orice comenzi OpenGL. În


tabelul II.8 sunt prezentate comenzile care nu pot fi stocate într-o listă de afişare (apelul
funcţiei glNewList în timpul creării unei liste de afişare generează eroare).

glColorPointer() glFlush() GlNormalPointer()


glDeleteLists() glGenLists() GlPixelStore()
glDisableClientState() glGet*() GlReadPixels()
glEdgeFlagPointer() glIndexPointer() GlRenderMode()
glEnableClientState() glInterleavedArrays() GlSelectBuffer()
glFeedbackBuffer() glIsEnabled() GlTexCoordPointer()
glFinish() glIsList() GlVertexPointer()
Tabelul II.8.

La folosirea unui sistem OpenGL în reţea, clientul poate rula pe o maşină şi


server-ul pe alta. După crearea unei liste de afişare ea se va afla la server astfel că server-
ul nu se poate baza pe client pentru nici o informaţie relativă la lista de afişare. Astfel,
orice comandă care întoarce o valoare nu poate fi stocată într-o listă de afişare. În plus,
comenzile care modifică starea clientului cum ar fi glPixelStore, glSelectBuffer şi
comenzile de definire a vectorilor de vârfuri nu pot fi stocate într-o listă de afişare.
Operaţiile unor comenzi OpenGL depind de starea clientului. De exemplu,
funcţiile de specificare a vârfurilor vectorilor (cum ar fi glVertexPointer, glColorPointer
şi glInterleavedArrays) setează pointeri de stare ai clientului şi nu pot fi stocate într-o
listă de afişare. Funcţiile glArrayElement, glDrawArrays şi glDrawElements transmit
date către server pentru a construi primitive din elementele vectorilor. Astfel de operaţii
pot fi stocate într-o listă de afişare.
Vectorul de vârfuri stocat în lista de afişare este obţinut prin dereferenţierea datei
din pointeri nu prin stocarea pointerilor . Astfel, modificările datelor din vectorul de
vârfuri nu va afecta definirea primitivei în lista de afişare.
Funcţii cum ar fi glFlush şi glFinish nu pot fi stocate într-o listă de afişare
deoarece depind de starea clientului în momentul execuţiei.
II.11.2.2. Execuţia unei liste de afişare

După crearea unei liste de afişare aceasta poate fi executată prin apelul funcţiei
glCallList. O listă de afişare poate fi executată de mai multe ori şi de asemenea o listă de
afişare poate fi executată îmbinându-se cu apeluri în modul imediat.

void glCallList (GLuint list);

Functia execută lista de afişare specificată de parametrul list. Comenzile din lista de
afişare sunt executate în ordinea în care au fost salvate ca şi cum ar fi executate fără a se
folosi o lista de afişare. Dacă lista nu a fost definită nu se va întâmpla nimic.
Funcţia glCallList poate fi apelată din orice punct al programului atâta timp cât
contextul OpenGL care accesează lista de afişare este activ (contextul care a fost activ la
crearea listei de afişare sau un context din acelaşi grup partajat). O listă de afişare poate
fi creată într-o funcţie şi executată în altă funcţie atâta timp cât indexul său o identifică în
mod unic. De asemenea, contextul unei liste de afişăre nu poate fi salvat într-un fişier şi
nici nu se poate crea o listă de afişare dintr-un fişier. În acest sens o listă de afişare este
proiectată pentru a fi folosită temporar.

II.11.3. Liste de afişare ierarhice

O listă de afişare ierarhică este o listă de afişare care execută o altă listă de afişare
prin apelul funcţiei glCallList, între perechile de funcţii glNewList şi glEndList. O listă
de afişare ierarhică este folositoare pentru un obiect alcătuit din componente, în mod
special dacă aceste componente sunt folosite de mai multe ori.

Exemplu: o listă de afişare care desenează o bicicletă prin apelul altor liste de afişare
pentru desenarea componentelor :

glNewList(listIndex,GL_COMPILE);
glCallList(handlebars);
glCallList(frame);
glTranslatef(1.0,0.0,0.0);
glCallList(wheel);
glTranslatef(3.0,0.0,0.0);
glCallList(wheel);
glEndList();
Pentru evitarea recursivităţii infinite limita nivelului de imbricare al listelor de
afişare este 64, dar această limită depinde de implementare. Pentru a determina limita
specifică implementării OpenGL pe care se lucrează se apelează funcţia :

glGetIntegerv(GL_MAX_LIST_NESTING, GLint *data);

OpenGL permite crearea unei liste de afişare care să apeleze o altă listă de afişare
care nu a fost încă creată. Nu va avea nici un efect dacă prima listă o apelează pe cea de a
doua care încă nu a fost definită.

Exemplu : listă de afişare ierarhică

glNewList(1,GL_COMPILE);
glVertex3f(v1);
glEndList();
glNewList(2,GL_COMPILE);
glVertex3f(v2);
glEndList();
glNewList(3,GL_COMPILE);
glVertex3f(v3);
glEndList();

glNewList(4,GL_COMPILE);
glBegin(GL_POLYGON);
glCallList(1);
glCallList(2);
glCallList(3);
glEnd();
glEndList();

Pentru afişarea poligonului se apelează lista de afişare 4. Pentru a edita un vârf


este necesar să se creeze din nou lista de afişare corespunzătoare vârfului respectiv.
Deoarece un index identifică în mod unic lista de afişare, crearea unei alte liste cu acelaşi
index o va şterge în mod automat pe cea existentă. Trebuie reţinut că această metodă nu
foloseşte în mod optim memoria şi nu îmbunătăţeşte performanţele, dar este folositoare în
unele cazuri.

II.11.4. Gestiunea listelor de afişare cu indici


Pentru a obţine un indice nefolosit care să identifice o nouă listă de afişare se
poate folosi funcţia glGenLists. Dacă nu se doreşte folosirea acestei funcţii atunci se
poate folosi glIsList pentru a determina dacă un anumit index este folosit.

GLboolean glIsList(GLuint list);

Funcţia întoarce GL_TRUE dacă indexul specificat de parametrul list este deja folosit
pentru o listă de afişare şi GL_FALSE în caz contrar.

Pentru a şterge în mod explicit o listă de afişare sau un domeniu continuu de liste
se foloseşte funcţia glDeleteLists. Folosirea funcţiei glDeleteLists eliberează indicii
corespunzători listelor şterse să fie disponibili din nou.

void glDeleteLists(GLuint list, GLsizei range);

Funcţia şterge range liste de afişare începând de la indexul specificat de list. Este ignorată
încercarea de a se şterge o listă de afişare care nu a fost creată.

II.11.5. Execuţia listelor de afişare multiple

OpenGL furnizează un mecanism eficient de a executa succesiv liste de afişare.


Acest mecanism necesită introducerea indicilor listelor de afişare într-un vector si apoi
apelarea funcţiei glCallLists. O utilizare pentru acest mecanism este acela de a crea un
font şi fiecare indice de listă de afişare să corespundă valorii ASCII a caracterului din
font. Pentru a avea mai multe fonturi este necesară stabilirea unui index iniţial diferit
pentru fiecare font. Acest index iniţial poate fi specificat prin apelarea funcţiei glListBase
înainte de a apela glCallLists.

void glListBase(GLuint base);

Funcţia specifică offset-ul care este adunat indicilor listelor de afişare în apelul funcţiei
glCallLists pentru a obţine indicii listelor de afişare finali. Valoarea implicită a
parametrului base este 0. Parametrul base nu are nici un efect asupra apelului glCallList,
care execută o singură listă de afişare, sau asupra funcţiei glNewList.

void glCallLists(GLsizei n, GLenum type, const GLvoid *lists);


Functia execută n liste de afişare. Indicii listelor care vor fi executate vor fi calculaţi prin
adunarea offset-ului indicat de baza curentă a listei de afişare (specificat cu ajutorul
funcţiei glListBase la valorile întregi indicate de parametrul lists.
Parametrul type specifică tipul valorilor din lists. El poate avea una din valorile
GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT,
GL_INT, GL_UNSIGNED_INT sau GL_FLOAT, adică ceea ce indică lists poate fi
tratată ca un vector de bytes, unsigned bytes, shorts, unsigned shorts, integers, unsigned
integers, sau floats. De asemenea parametrul type poate avea una din valorile
GL_2_BYTES, GL_3_BYTES sau GL_4_BYTES caz în care succesiuni de 2, 3 sau 4
octeţi vor fi citiţi din lists şi apoi sunt deplasaţi şi adunaţi octet cu octet pentru a calcula
offset-ul listei de afişare. Pentru aceasta este folosit următorul algoritm (byte[0]
reprezintă începutul secvenţei de octeţi).

/* b = 2, 3 sau 4; octeţii sunt numerotaţi în vector 0,1,2,3 */


offset = 0;
for (i = 0; i < b; i++) {
offset = offset << 8;
offset += byte[i];
}
index = offset + listbase;

Exemplu
Definirea listelor de afişare multiple: afişarea caracterelor dintr-un set de caractere
vectorial

void initStrokedFont(void)
/* setează indicii listelor de afişare pentru fiecare caracter
corespunzător valorii lor ASCII */
{
GLuint base;

base = glGenLists(128);
glListBase(base);
glNewList(base+'A', GL_COMPILE);
drawLetter(Adata); glEndList();
glNewList(base+'E', GL_COMPILE);
drawLetter(Edata); glEndList();
glNewList(base+'P', GL_COMPILE);
drawLetter(Pdata); glEndList();
glNewList(base+'R', GL_COMPILE);
drawLetter(Rdata); glEndList();
glNewList(base+'S', GL_COMPILE);
drawLetter(Sdata); glEndList();
glNewList(base+' ', GL_COMPILE); /* spaţiu */
glTranslatef(8.0, 0.0, 0.0);
glEndList();
}

Funcţia glGenLists alocă 128 de indici continui pentru listele de afişare. Primul
indice alocat devine baza listei de afişare. Pentru fiecare caracter va fi creată câte o listă
de afişare; fiecare index al listei de afişare este suma dintre indicele de bază şi valoarea
ASCII a literei. În acest exemplu sunt create numai câteva litere şi caracterul ‘ ‘. După
crearea listelor de afişare poate fi apelata funcţia glCallLists pentru a le executa.

Exemplu
Apelul functiei printStrokedString având ca parametru un caracter :

void printStrokedString(GLbyte *s)


{
GLint len = strlen(s);
glCallLists(len, GL_BYTE, s);
}

II.11.6. Gestiunea variabilelor de stare folosind liste de afişare

O listă de afişare poate conţine apeluri care să modifice valorile variabilelor de


stare OpenGL. Aceste valori se modifică la execuţia listei de afişare (ca şi în modul
imediat de execuţie al comenzilor) şi modificările se păstrează şi după execuţia completă
a listei de afişare.

Exemplu
Modificările culorii curente şi a matricii curente făcute în timpul execuţiei listei de
afişare rămân vizibile şi după executarea sa:

glNewList(listIndex,GL_COMPILE);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_POLYGON);
glVertex2f(0.0,0.0);
glVertex2f(1.0,0.0);
glVertex2f(0.0,1.0);
glEnd();
glTranslatef(1.5,0.0,0.0);
glEndList();

Dacă se va apela următoarea secvenţă de cod, segmentul de dreaptă desenat după lista de
afişare va avea culoarea roşie (culoarea curentă) şi va fi translatat cu (1.5, 0.0, 0.0):
glCallList(listIndex);
glBegin(GL_LINES);
glVertex2f(2.0,-1.0);
glVertex2f(1.0,0.0);
glEnd();
Uneori este necesar ca modificările stării să fie păstrate, dar alteori după execuţia
unei liste de afişare se doreşte să se revină la starea anterioară. Într-o listă de afişare nu
poate fi folosită funcţia glGet*, aşa că trebuie folosit un alt mod de a interoga şi stoca
valorile variabilelor de stare. În acest scop poate fi folosită funcţia glPushAttrib pentru a
salva un grup de variabile de stare şi glPopAttrib pentru a reface variabilele.

Exemplu
Refacerea variabilelor de stare din interiorul unei liste de afişare

glNewList(listIndex,GL_COMPILE);
glPushMatrix();
glPushAttrib(GL_CURRENT_BIT);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_POLYGON);
glVertex2f(0.0,0.0);
glVertex2f(1.0,0.0);
glVertex2f(0.0,1.0);
glEnd();
glTranslatef(1.5,0.0,0.0);
glPopAttrib();
glPopMatrix();
glEndList();
Exemplu
Dacă se foloseşte lista de afişare de mai sus atunci se va desena un segment de dreaptă de
culoare verde şi netranslatat:
void display(void)
{
GLint i;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 1.0, 0.0); /* setează culoarea curentă verde */
for (i = 0; i < 10; i++)
glCallList(listIndex); /* lista de afişare apelată de 10
ori*/
drawLine(); /* unde şi cum apare această linie? */
glFlush();
}

II.11.7. Încapsularea schimbărilor de mod

Listele de afişare pot fi folosite pentru a organiza şi stoca grupuri de comenzi,


pentru a modifica diferite moduri sau a seta diferiţi parametri. Când se doreşte comutarea
de la un grup de setări la un altul folosirea listelor de afişare poate fi mai eficientă decât
apelarea directă.
Listele de afişare pot fi mai eficiente decât modul imediat pentru comutarea între
diferite setări ale surselor de lumină, modelelor de iluminare şi parametrilor de material.
De asemenea, listele de afişare se pot folosi şi pentru generarea liniilor cu şablon, precum
şi pentru ecuaţiile planului de decupare. În general, execuţia listelor de afişare este cel
puţin tot atât de rapidă ca şi apelul direct dar în cazul folosirii listelor de afişare este
introdus un overhead.

Exemplu
Folosirea listelor de afişare pentru a comuta între trei şabloane diferite de linii. La început
se apelează funcţia glGenLists pentru a aloca o listă de afişare pentru fiecare şablon.
Apoi se foloseşte funcţia glCallList pentru a comuta între un şablon şi altul.

GLuint offset;
offset = glGenLists(3);
glNewList (offset, GL_COMPILE);
glDisable (GL_LINE_STIPPLE);
glEndList ();
glNewList (offset+1, GL_COMPILE);
glEnable (GL_LINE_STIPPLE);
glLineStipple (1, 0x0F0F);
glEndList ();
glNewList (offset+2, GL_COMPILE);
glEnable (GL_LINE_STIPPLE);
glLineStipple (1, 0x1111);
glEndList ();
#define drawOneLine(x1,y1,x2,y2) glBegin(GL_LINES); \
glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();
glCallList (offset);
drawOneLine (50.0, 125.0, 350.0, 125.0);
glCallList (offset+1);
drawOneLine (50.0, 100.0, 350.0, 100.0);
glCallList (offset+2);
drawOneLine (50.0, 75.0, 350.0, 75.0);

S-ar putea să vă placă și