OpenGL – Stipple

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.

  1. Per prima cosa disegnate il pattern su un foglio quadrettato utilizzando solamente 16 quadretti.
  2. 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.
  3. Ribaltate il numero binario che avete trovato. Io ad esempio ottengo 0000111100001111.
  4. 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.

8 comments on “OpenGL – Stipple

    • 😮 Linkami il codice che hai eseguito che gli do un occhiata.

      • Ho usato i sorgenti in python per entrambi… fa la stessa cosa con quelli in C.

        • A dire il vero nello screen che hai linkato non si vede nemmeno il bordo delle finestre 😐 Come mai? 😐

          • Quello è un difetto di compiz. Spostando la finestra poi compaiono anche i bordi.

          • Il codice che ho postato mi sembra giusto. Non so perché a te non “stippla” potrebbe essere un problema della libreria 😐 Controlla la versione della libreria scrivendo questi due comandi nella shell di python

            import OpenGL
            OpenGL.__version__

          • Allora… mi hanno detto che a volte alcuni driver delle schede video disabilitano lo stipple per questione di prestazioni. Quindi andrebbe abilitata una variabile d’ambiente che sfortunatamente non conosco. 😐