Skip to content Skip to navigation

Connexions

You are here: Home » Content » Texture in Processing

Navigation

Content Actions

  • Download module PDF
  • Add to ...
    Add the module to:
    • My Favorites
    • A lens
    • An external social bookmarking service
    • My Favorites (What is 'My Favorites'?)
      'My Favorites' is a special kind of lens which you can use to bookmark modules and collections directly in Connexions. 'My Favorites' can only be seen by you, and collections saved in 'My Favorites' can remember the last module you were on. You need a Connexions account to use 'My Favorites'.
    • A lens (What is a lens?)

      Definition of a lens

      Lenses

      A lens is a custom view of Connexions content. You can think of it as a fancy kind of list that will let you see Connexions through the eyes of organizations and people you trust.

      What is in a lens?

      Lens makers point to Connexions materials (modules and collections), creating a guide that includes their own comments and descriptive tags about the content.

      Who can create a lens?

      Any individual Connexions member, a community, or a respected organization.

    • External bookmarks
  • E-mail the authors

Recently Viewed

This feature requires Javascript to be enabled.

Texture in Processing

Module by: Davide Rocchesso, Pietro Polotti

Summary: Una breve guida al texture mapping in Processing

Interpolazione di colori

Come visto in Graphic Composition in Processing, si possono ottenere superfici come collezioni di poligoni mediante definizione di vertici all'interno della coppia beginShape() - endShape(). E' possibile attribuire un colore a uno o più vertici, in modo da rendere variazioni continue di colore (gradiente). Ad esempio, si provi ad eseguire il codice

 
	  size(200,200,P3D);
          beginShape(TRIANGLE_STRIP);
          fill(240,   0, 0); vertex(20,31, 33);
          fill(240, 150, 0); vertex(80, 40, 38);
          fill(250, 250, 0); vertex(75, 88, 50);
                             vertex(49, 85, 74);
          endShape();
per ottenere una sfumatura continua dal rosso al giallo nella strip di due triangoli.

Interpolazione bilineare

Il sistema grafico opera una interpolazione dei valori di colore attribuiti ai vertici. Questo tipo di interpolazione bilineare è definita come segue:

  • Per ogni poligono della collezione
    • Per ogni lato del poligono si attribuisce ad ogni punto sul segmento il colore ottenuto per interpolazione lineare dai colori dei vertici i i e j j che delimitano il poligono: C ij α=1-α C i +α C j C ij α 1 α C i α C j
    • Una scan line spazzola il poligono (o meglio, la sua proiezione sulla finestra immagine) intersecando ad ogni passo due lati in due punti l l ed r r i cui colori sono già stati individuati come C l C l e C r C r . In ogni punto della linea di scan il colore è determinato per interpolazione lineare C lr β=1-β C l +β C r C lr β 1 β C l β C r

Un esempio significativo di interpolazione di colori associati ai vertici di un cubo si trova negli esempi di Processing, nel codice RGB Cube.

Texture

Nella modellizzazione di scene complesse per composizione di elementi grafici semplici non si può superare una certa soglia di complessità. Si pensi ad esempio alla modellizzazione di una scena campestre nella quale si debbano rappresentare i singoli elementi di vegetazione, ivi compresi i fili d'erba del prato. E' impensabile svolgere manualmente questo compito. Potrebbe essere possibile collocare e controllare i fili d'erba per via algoritmica, ed è un strada simile che si segue, ad esempio, per rendere realistica la capigliatura o la pelle dei personaggi nei più sofisticati film di animazione (si veda ad esempio, the Incredibles). Oppure, in special modo se sono richieste prestazioni di grafica interattiva, bisogna ricorrere all'artificio delle texture. In sostanza, si tratta di immagini che rappresentano la tessitura visiva delle superfici e che vengono mappate sui poligoni che modellano gli oggetti della scena. Ai fini della resa qualitativa delle superfici ha senso limitare il livello di dettaglio a frammenti non più piccoli di un pixel, e quindi il texture mapping viene inserito nella catena di rendering a livello di rastering delle primitive grafiche, cioè laddove si passa da descrizioni geometriche in 3D a illuminazione di pixel sul display. E' a questo livello che avviene anche la rimozione delle facce nascoste, i soli frammenti di interesse essendo quelli visibili.

In Processing, una texture viene definita all'interno di un blocco beginShape() - endShape() mediante la funzione texture() che ha come unico parametro una variabile di tipo PImage. Le successive chiamate a vertex() possono contenere, come ultima coppia di parametri, il punto della texture al quale si vuole associare il vertice. Infatti, ogni immagine di texture è parametrizzata in due variabili u u e v v, che possono riferirsi direttamente alla riga e colonna di un texel (pixel della texture) ovvero essere normalizzate tra 0 0 e 1 1, in maniera da poter ignorare la dimensione nonché la larghezza e altezza della texture stessa. L'interpretazione da attribuire ai parametri u u e v v viene fissata dalla direttiva textureMode() con parametro IMAGE o NORMALIZED.

Esempio 1

Nel codice che segue il l'immagine di vetro rotto viene usata come texture e affiancata all'interpolazione dei colori, nonché all'illuminazione di default. Lo shading delle superfici, prodotto dall'illuminazione e dai colori, viene modulato in maniera moltiplicativa dai colori della texture.


            size(400,400,P3D);
            PImage a = loadImage("vetro.jpg");
            lights();
            textureMode(NORMALIZED);
            beginShape(TRIANGLE_STRIP);
            texture(a);
            fill(240,   0, 0); vertex(40,61, 63, 0, 0);
            fill(240, 150, 0); vertex(340, 80, 76, 0, 1);
            fill(250, 250, 0); vertex(150, 176, 100, 1, 1);
                               vertex(110, 170, 180, 1, 0);
            endShape();
	  

Texture mapping

E' evidente che l'operazione di mappatura da immagine texture a superficie oggetto, di forma arbitraria, implica una qualche forma di interpolazione. In modo analogo a quanto avviene per i colori, soltanto i vertici che delimitano la superficie sono messi in corrispondenza con punti esatti dell'immagine texture. Quello che avviene per i punti interni deve essere stabilito in qualche modo. In effetti, Processing e OpenGL si comportano secondo quanto illustrato in Sezione 2, cioè per interpolazione bilineare: una prima interpolazione lineare su ogni segmento del contorno è seguita da interpolazione lineare su scan line. Se u u e v v eccedono i limiti dell'immagine texture, il sistema (Processing) può assumere che questa sia ripetuta periodicamente ovvero fissarla ai valori assunti alla frontiera.

Un problema che si verifica è che raramente un pixel su display corrisponde esattamente a un texel. Su un pixel possono essere mappati più texel o, viceversa, un texel può mapparsi su più pixel. Il primo caso corrisponde ad un sottocampionamento (o decimazione) che, come visto in Sampling and Quantization, può dare luogo ad aliasing. L'effetto dell'aliasing si può attenuare mediante filtraggio passa-basso dell'immagine texture. Il secondo caso corrisponde ad un sovracampionamento, interpretabile in frequenza come un allontanamento delle immagini spettrali.

Generazione di texture

Le texture, oltre che importate da immagini, possono anche essere generate per via algoritmica, ciò è particolarmente indicato quando si vogliano generare dei pattern regolari o pseudo casuali. Ad esempio, il pattern di una scacchiera si può generare con il codice



PImage textureImg = loadImage("vetro.jpg"); // dummy image
colorMode(RGB,1);

int biro = 0;
int bbiro = 0;
int scacco = 5;
for (int i=0; i<textureImg.width; i+=scacco) {
   bbiro = (bbiro + 1)%2; biro = bbiro;
   for (int j=0; j<textureImg.height; j+=scacco) {
     for (int r=0; r<scacco; r++)
       for (int s=0; s<scacco; s++)
        textureImg.set(i+r,j+s, color(biro));
    biro = (biro + 1)%2; 
  }
}
image(textureImg, 0, 0);

      

L'uso della funzione random, combinato con filtri di vario tipo, consente una ampia versatilità nella produzione di texture. Ad esempio, in Figura 1 è rappresentato un pattern ottenuto da una variazione del codice di generazione della scacchiera. In particolare, è stata aggiunta l'istruzione scacco=floor(2+random(5)); all'interno del for più esterno, e si è eseguito un filtraggio di media.

Figura 1
Pattern generato per via algoritmica
Pattern generato per via algoritmica (expattern.png)

Exercise 1

Come si modifica il codice di Esempio 1 in modo da rendere le fratture del vetro più grandi?

Solution 1

Basta considerare solo un pezzo della texture, con chiamate del tipo vertex(150, 176, 0.3, 0.3);

Exercise 2

Si modifichi il codice del generatore di scacchiera di Sezione 5 in modo che generi la texture di Figura 1

Exercise 3

Si esegua e si analizzi il codice seguente. Si provi inoltre a variare la dimensione dei quadratini e il tipo di filtraggio.


            

size(200, 100, P3D);

PImage textureImg = loadImage("vetro.jpg"); // dummy image
colorMode(RGB,1);

int biro = 0;
int bbiro = 0;
int scacco = 5;
for (int i=0; i<textureImg.width; i+=scacco) {
   // scacco=floor(2+random(5));
   bbiro = (bbiro + 1)%2; biro = bbiro;
   for (int j=0; j<textureImg.height; j+=scacco) {
     for (int r=0; r<scacco; r++)
       for (int s=0; s<scacco; s++)
        textureImg.set(i+r,j+s, color(biro));
    biro = (biro + 1)%2; 
  }
}
image(textureImg, 0, 0);
textureMode(NORMALIZED);
beginShape(QUADS);
  texture(textureImg);
  vertex(20, 20, 0, 0);
  vertex(80, 25, 0, 0.5);
  vertex(90, 90, 0.5, 0.5);
  vertex(20, 80, 0.5, 0);
endShape();

// ------ filtraggio -------

PImage tImg = loadImage("vetro.jpg"); // dummy image
float val = 1.0/9.0;
float[][] kernel = { {val, val, val}, 
                     {val, val, val}, 
                     {val, val, val} }; 
int n2 = 1; 
int m2 = 1;
colorMode(RGB,255);
// Convolve the image 
for(int y=0; y<textureImg.height; y++) { 
  for(int x=0; x<textureImg.width/2; x++) { 
    float sum = 0; 
    for(int k=-n2; k<=n2; k++) { 
      for(int j=-m2; j<=m2; j++) { 
        // Reflect x-j to not exceed array boundary 
        int xp = x-j; 
        int yp = y-k; 
        if (xp < 0) { 
          xp = xp + textureImg.width; 
        } else if (x-j >= textureImg.width) { 
          xp = xp - textureImg.width; 
        } 
        // Reflect y-k to not exceed array boundary 
        if (yp < 0) { 
          yp = yp + textureImg.height; 
        } else if (yp >= textureImg.height) { 
          yp = yp - textureImg.height; 
        } 
        sum = sum + kernel[j+m2][k+n2] * red(textureImg.get(xp, yp)); 
      } 
    } 
    tImg.set(x,y, color(int(sum)));
  } 
} 
 
translate(100, 0);
beginShape(QUADS);
  texture(tImg);
  vertex(20, 20, 0, 0);
  vertex(80, 25, 0, 0.5);
  vertex(90, 90, 0.5, 0.5);
  vertex(20, 80, 0.5, 0);
endShape();

	  

Comments, questions, feedback, criticisms?

Send feedback