Questa è la penultima lezione del primo grande capitolo di OpenGL: il disegno di poligoni. Oggi parleremo dei “pattern” e di come istruire il motore grafico affinché non utilizzi un colore uniforme per disegnare righe e campiture ma colori soltanto i pixel che decidiamo noi. L’esempio più banale consiste nel voler disegnare una riga tratteggiata o di voler realizzare campiture in half-tone come quella nell’immagine dell’articolo.
Prima di entrare nel dettaglio facciamo un attimo il punto sulla struttura di OpenGL. OpenGL è una macchina a stati. Gli stati attivi influenzano il comportamento di OpenGL in modo sostanziale. Per attivare uno stato si utilizza il comando:
glEnable(state)
e si disabilita con
glDisable(state)
Dove state è l’identificativo di uno dei possibili stati di OpenGL. Non elencherò adesso tutti gli stati possibili di OpenGL perché sono tantissimi ma ve li citerò nel momento del bisogno. Un avvertimento: non tenete tutti gli stati abilitati se non ne avete bisogno perché questo danneggia pesantemente le prestazioni. Se non usate la nebbia (GL_FOG) è inutile che la attivate rendendo più pesante il processo di rendering.
STIPPLE NELLE LINEE
Cominciamo con le linee. Abbiamo visto come tracciare una linea (ad esempio tramite la modalità GL_LINE_STRIP) e come modificare il colore di tale linea (tramita glColor() e le sue varianti). Vediamo ora come effettuare il tratteggio.
La prima cosa da fare è abilitare lo stato GL_LINE_STIPPLE nella macchina tramite il comando enable. Dopodiché possiamo utilizzare il pratico comando:
glLineStipple(coefficente, pattern)
Questo comando imposta semplicemente lo stipple di una linea usando il tratteggio indicato in pattern. Ma come definiamo questo pattern? Tramite un numero esadecimale a 16bit. Qui bisogna fare un po’ di attenzione ma esiste un metodo infallibile per ricavare il numero corrispondente al pattern dei vostri sogni.
- Per prima cosa disegnate il pattern su un foglio quadrettato utilizzando solamente 16 quadretti.
- Sotto questo schema disegnate un 1 in corrispondenza dei quadratini anneriti e uno 0 in corrispondenza dei quadratini vuoti. Ad esempio se voglio fare un pattern tratteggiato con 4pixel disegnati e 4 no avrò: 1111000011110000.
- Ribaltate il numero binario che avete trovato. Io ad esempio ottengo 0000111100001111.
- Raggruppate le cifre a gruppi di 4 e convertite il numero in esadecimale. Io ad esempio ottengo 0F0F.
Se vi domandate perché vada ribaltato vi basti sapere che dipende solamente dal modo con cui vengono letti i numeri nei calcolatori.
Il parametro coefficente è un semplice moltiplicatore. Se lo impostate ad 1 allora avrete identicamente il pattern che avete progettato, se lo impostate a 2 questo pattern verrò raddoppiato (ad esempio, nel mio caso, invece di 4pixel si e 4 no avrò 8pixel si e 8 no), con 3 triplicato e così via.
Ho questo esempio pronto:
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
def drawLine(x1,y1,x2,y2) :
glBegin(GL_LINES)
glVertex2f(x1,y1)
glVertex2f(x2,y2)
glEnd()
def init() :
glClearColor(0,0,0,0)
glShadeModel(GL_FLAT)
def display() :
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(1,1,1)
glEnable(GL_LINE_STIPPLE)
#Prima Riga
glLineStipple(1, 0x0101)
drawLine(50, 125, 150, 125)
glLineStipple(1, 0x00FF)
drawLine(150, 125, 250, 125)
glLineStipple(1, 0x1C47)
drawLine(250, 125, 350, 125)
#Seconda Riga
glLineWidth(5)
glLineStipple(1, 0x0101)
drawLine(50, 100, 150, 100)
glLineStipple(1, 0x00FF)
drawLine(150, 100, 250, 100)
glLineStipple(1, 0x1C47)
drawLine(250, 100, 350, 100)
glLineWidth(1)
#Terza Riga
glLineStipple(1, 0x1C47)
glBegin(GL_LINE_STRIP)
for i in range(7) :
glVertex2f(50 + (i * 50), 75)
glEnd()
#Quarta Riga
for i in range(6) :
drawLine(50 + (i * 50), 50, 50 + (i+1) * 50, 50)
#Quinta Riga
glLineStipple(5, 0x1C47)
drawLine(50, 25, 350, 25)
glDisable(GL_LINE_STIPPLE)
glFlush()
def reshape(w,h) :
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0, w, 0, h)
if __name__=='__main__' :
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(400, 150)
glutInitWindowPosition(100, 100)
glutCreateWindow("Righe")
init()
glutDisplayFunc(display)
glutReshapeFunc(reshape)
glutMainLoop()
Uso tutti comandi già visti (tranne glLineWidth() ma credo che in questo caso il significato sia piuttosto intuitivo). La funzione drawLine() è una funzione personale che serve a snellire il codice. Potete usarla oppure usare direttamente ogni volta lo schema begin-vertex-end.
STIPPLE NEI POLIGONI
Ora invece vediamo come possiamo fare qualcosa di analogo con le campiture dei poligoni. Il meccanismo è analogo a quello delle linee. Per prima cosa si abilità lo stato GL_POLYGON_STIPPLE. Poi si imposta il pattern con:
glPolygonStipple(pattern)
Questa volta però disegnare il pattern è più complesso. Bisogna infatti utilizzare un array di 128 numeri esadecimali a 8bit al fine di formare una bitmap di 32×32 bit. Disegnare trame complesse è quindi molto più complesso del disegnare pattern per le righe. Proprio a causa di questa complessità e per il fatto che lo stipple di poligoni non è importante come lo stipple di linee, tralascerò per il momento il modo con cui vengono costruite queste bitmap. Ne farò forse un appendice.
Per il momento vi lascio questo codice di esempio:
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
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 ]
def init() :
glClearColor(0,0,0,0)
glShadeModel(GL_FLAT)
def reshape(w,h) :
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0, w, 0, h)
def display() :
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(1,1,1)
#Disegna un quadrato bianco
glRectf(25, 25, 125, 125)
#Disegna un quadrato stipple in halftone
glEnable(GL_POLYGON_STIPPLE)
glPolygonStipple(halftone)
glRectf(125, 25, 225, 125)
glDisable(GL_POLYGON_STIPPLE)
glFlush()
if __name__ == '__main__' :
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(250, 150)
glutCreateWindow("HalfTone")
init()
glutDisplayFunc(display)
glutReshapeFunc(reshape)
glutMainLoop()
Per questa volta è tutto. La prossima volta vedremo alcuni modi per evitare di inserire singolarmente tutti i vertici di una figura ed i rispettivi colori. Sostituendo quindi 20 istruzioni con una sola.

Nelle precedenti lezioni abbiamo visto come si riesca a disegnare qualunque poligono semplicemente inviando a OpenGL le coordinate dei suoi vertici. Per fare questo abbiamo anche visto che dobbiamo inserire il comando glVertex() all’interno di un blocco di istruzioni che comincia con:
Prima di cominciare a spiegare il piccolo codice di esempio è importante fare qualche piccola premessa. La fluidità dell’animazione dipende dai frame per secondo (fps). I frame per secondo massimi dipendono dal refresh dello schermo. Gli schermi moderni vanno all’incirca a 60Hz, e quindi che in un secondo lo schermo proietta 60 immagini. Questo significa che ogni secondo possiamo rappresentare al massimo 60 immagini differenti e, quindi, possiamo avere al massimo 60fps.
Stavo ripassando gran parte delle API delle OpenGL. Stavo facendo piccoli schemi e riassunti ed allora ho pensato: perché non condividere un po’ di questo lavoro? Così ci trascrivo qualche piccolo cenno di OpenGL senza nessuna intenzione di portare alla luce chissà quale serie di tutorial. Se poi esce qualcosa di appetibile meglio così.
Recent Comments