Documente Academic
Documente Profesional
Documente Cultură
Aplicatii OpenGL Folosing GLUT
Aplicatii OpenGL Folosing 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
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ă).
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
Iniţializare fereastră
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:
Creare fereastră
Distrugere fereastră
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ă.
Redimensionarea ferestrei
void glutReshapeFunc(void (*func)(int width, int height)) ;
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.
Exemplu
Funcţia output prezentata mai jos realizeaza afişarea unui text cu format, incepand dintr-
o pozitie specificata a ferestrei curente.
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();
}
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.
Exemplu 1
Programul din fişierul exemplu11.c afişează un dreptunghi centrat în fereastră.
/* fişierul exemplul1.c */
void display(void)
{
/* şterge toţi pixelii */
glClear (GL_COLOR_BUFFER_BIT);
glFlush ();
}
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 */
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();
}
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
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) ;
Î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.
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.
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);
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.
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();
Figura II.4
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.8.2.1.Lăţimea liniilor
Exemplu:
glLineStipple(1, 0x3F07);
glEnable(GL_LINE_STIPPLE);
/* exemplul3.c*/
#include "glut.h"
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);
/* 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);
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.
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.
/* 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 ();
}
Î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.
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.
glBegin (GL_POLYGON);
glNormal3fv(n0);
glVertex3fv(v0);
glNormal3fv(n1);
glVertex3fv(v1);
glNormal3fv(n2);
glVertex3fv(v2);
glNormal3fv(n3);
glVertex3fv(v3);
glEnd();
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.
Prezentăm în continuare funcţiile OpenGL prin care pot fi definite proiecţiile şi volumul
de vizualizare.
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
O altă funcţie care poate fi folosită pentru proiecţia perspectivă este gluPerspective :
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
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ă.
- (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
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();
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
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 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.
/* 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 ();
}
Î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ă.
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);
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 ();
}
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).
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.
glEnable(GL_LIGHT1);
Î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 exemplul de mai sus valorile folosite pentru lmodel_ambient sunt valorile implicite
pentru GL_LIGHT_MODEL_AMBIENT.
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
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);
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 :
Emisia
Prin asocierea unei culori RGBA parametrului GL_EMISSION se poate face ca
un obiect să pară că furnizează lumină de culoarea selectată.
Exemplu:
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();
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*.
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();
}
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.
#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();
}
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();
}
}
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();
}
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ă.
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);
/* fişierul exemplul9.c */
#include “glut.h”
#include <stdlib.h>
GLuint listName;
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 ();
}
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.
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 :
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.
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.
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;
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.
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.
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 :
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ă.
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();
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.
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ă.
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.
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 :
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();
}
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);