ROBOTICA

video
immagini
papers
progettati
costruiti
toolbox
vrml
simulatori

links

 

Papers
meccanica
sistemi
documents

Capitolo 28° Approfondimenti sulle Textures Capitolo 30°

Anticipo la parte di approfondimenti sulle textures... (a gran richiesta di una persona ;) ).

Avevamo visto nella prima parte come mappare textures sulle superfici degli oggetti. In particolare si era analizzato il caso di mapping sulle semplici primitive geometriche rese disponibili da Vrml (Cone,Sphere,Cylinder,Box). Ricapitoliamo velocemente quanto visto con un banale esempio:

#VRML V2.0 utf8
#esempio di mapping su primitiva
Shape {
 appearance Appearance {
  material Material { diffuseColor 1 1 1 }
  texture ImageTexture { url "immagine.jpg" }
 }
 geometry Sphere { }
}

Nel campo texture del nodo Appearance introduco un nodo ImageTexture il cui campo url contiene una reference alla immagine da mappare.
Il mapping sulle primitive viene eseguito secondo certe modalita' di default. In particolare, per la sfera l'immagine viene avvolta sull'intera superficie; per il cilindro si avvolge l'immagine sulla superficie laterale e due copie delle immagini sono mappate sulle due basi; per il cono si effettua un avvolgimento sulla superficie laterale e si pone una copia sulla base; per la box si riporta una copia dell'immagine per ogni faccia.

Fin qui e' tutto noto.

Vediamo ora di approfondire le nostre conoscenze su come gestire le textures. Si noti bene che nella maggior parte dei casi si utilizzeranno le modalita' di default; quando pero' si vogliono ottenere certi effetti e maggiore controllo sul mapping ci si deve preoccupare di aspetti piu' 'complicati'.

Iniziamo con l'analizzare un campo del nodo Appearance che ancora ci manca: textureTransform. Questo campo serve per modificare le modalita' secondo cui mappare le textures sulle superfici. Il campo textureTransform contiene al suo interno un nodo TextureTransform (attenzione alle lettere maiuscole). La specifica del nodo TextureTransform e' la seguente:

TextureTransform {
  exposedField  SFVec2f  center      0 0
  exposedField  SFFloat  rotation    0
  exposedField  SFVec2f  scale       1 1
  exposedField  SFVec2f  translation 0 0
}

I campi sono molto semplici. Il campo rotation offre la possibilita' di ruotare la texture da mappare. La rotazione viene ovviamente effettuata attorno all'asse perpendicolare al piano della texture. Il centro attorno a cui effettuare la rotazione viene specificato nel campo center. Questo deve essere espresso in coordinate s,t della texture.

Molto brevemente, le coordinate s,t indicano le variabili utilizzate per la scansione dell'immagine da mappare. s in pratica indica l'ascissa di un punto dell'immagine, mentre t ne rappresenta l'ordinata. La coppia 0 0 indica l'angolo in basso a sinistra; 1 0 l'angolo in basso a destra, 0 1 l'angolo in alto a sinistra; 1 1 l'angolo in alto a destra. Per cui .5 .5 indica il centro dell'immagine.

Il campo translation serve per traslare la texture. Anche qui utilizziamo le coordinate s,t. Fornendo la coppia .5 0 si trasla la texture verso destra di una quantita' di pixels pari a meta' dell'immagine. La parte a sinistra che rimane scoperta viene mappata con la parte destra dell'immagine stessa (si ha un rientro).

Il campo scale serve per scalare l'immagine da mappare. Sotto certe condizioni questo consente di mappare sulla superficie piu' copie della stessa texture. Vedremo poi come ottenere questo effetto.

Non preoccupatevi se ora vi sembra tutto abbastanza confuso. Con gli esempi successivi vedremo di chiarirci le idee.

Tutte queste operazioni hanno senso nel caso di un IndexedFaceSet; per le primitive il modo di mapping e' quello standard.

Vediamo allora come mappare una texture su un semplice oggetto creato con IndexedFaceSet. Il piu' semplice oggetto di questo tipo e' una singola faccia. Definiamo dunque un quadrilatero e vediamo gli effetti che otteniamo.

#VRML V2.0 utf8
# esempio texture 1
DirectionalLight {
 direction 0 -1 -1
}
NavigationInfo {
 headlight FALSE
}
Viewpoint {
 position 0 10 50
}
Shape {
 appearance Appearance {
  material Material { diffuseColor 1 1 1 }
  texture ImageTexture { url "../textures/generali/gioconda.jpg" }
 }
 geometry IndexedFaceSet {
  coord Coordinate {
   point [ -10 0 0, 10 0 0, 10 20 0, -10 20 0 ]
  }
  coordIndex [0,1,2,3,-1]
 }
}

esempio 1

L'effetto che otteniamo non e' affatto buono... tant'e' che non riusciamo neppure ad intravedere l'immagine che forma la texture. Quando si ha un IndexedFaceSet il browser (Cosmo Player beta 3 e' quello di riferimento in questo caso) mappa la texture sulla bounding box che avvolge l'oggetto. Per cui in questo caso si vede solo una piccola parte della texture.

Cio' che serve e' un qualcosa che ci consenta di dimensionare la texture in modo opportuno. In particolare dobbiamo indicare che nell'angolo in basso a sinistra della faccia vogliamo che si vada a mappare l'angolo in basso a sinistra dell'immagine; e lo stesso per tutti gli altri 3 angoli.

Il nodo IndexedFaceSet offre appunto questa possibilita'. In particolare si utilizzano i campi texCoord e texCoordIndex il cui funzionamento e' identico a quello dei campi usati per definire la faccia.

Vediamo l'esempio e poi spieghiamo in dettaglio questi nuovi campi. (nell'esempio riporto solo il nodo Shape).

Shape {
  appearance Appearance {
   material Material { diffuseColor 1 1 1 }
   texture ImageTexture { url "../textures/generali/gioconda.jpg" }
  }
  geometry IndexedFaceSet {
   coord Coordinate {
    point [ -10 0 0, 10 0 0, 10 25 0, -10 25 0 ]
   }
   coordIndex [0,1,2,3,-1]
   texCoord TextureCoordinate {
    point [0 0, 1 0, 1 1, 1 0]
   }
   texCoordIndex [0,1,2,3,-1]
  }
}

esempio 2

In questo caso otteniamo il risultato che ci attendavamo.

Il campo texCoord contiene il nodo TextureCoordinate. In pratica questo nodo contiene le stesse informazioni contenute nel nodo Coordinate, con la sola differenza che qui ci si riferisce alle coordinate della texture. Quindi si specifica una coppia di valori; in particolare associamo al primo punto del quadrilatero la coppia 0 0 che indica appunto l'angolo in basso della texture. E cosi' via per quanto riguarda gli altri angoli. Il campo texCoordIndex contiene gli indici dei punti contenuti nel campo point del nodo TextureCoordinate; in questo caso definiamo dunque una texture quadrata che va a mapparsi sul quadrilatero generato.

Finche' ci si limita a mappare textures su forme geometriche piuttosto semplici, si puo' fare questo lavoro a mano; ma se avessimo geometrie molto piu' complicate allora la cosa diventa ingestibile e l'ausilio di un tool di sviluppo risulta necessario.

Prima di passare ad analizzare come impiegare le potenzialita' del nodo TextureTransform, mi interessa evidenziare l'esistenza di altri due campi non ancora analizzati e che fanno parte del nodo ImageTexture. Infatti, oltre al campo url, vi sono i campi repeatS e repeatT. Questi due prendono un valore booleano e sono settati a TRUE di default. In pratica abilitano la ripetizione delle textures in fase di mapping. Vediamo subito un esempio.

Nell'esempio precedente abbiamo mappato la Gioconda sopra un quadrilatero, senza preoccuparci molto delle dimensioni della texture e del quadrilatero stesso. Imponendo che agli angoli del quadrilatero le coordinate s,t devono assumere certi valori, si impone al browser di interpolare nelle posizioni intermedie e in pratica di riempire l'intero quadrilatero.

Supponiamo ora di volere mappare piu' esemplari della Gioconda sul piano. In tal caso un modo possibile e' quello di riscalare la texture tramite il nodo TextureTransform. Se i campi repeatS e repeatT sono settati a TRUE il browser effettuera' il tiling della texture.

Vediamo il risultato con il seguente esempio:

Shape {
  appearance Appearance {
   material Material { diffuseColor 1 1 1 }
   textureTransform TextureTransform {
    scale 2 2 
   }
   texture ImageTexture { url "../textures/generali/gioconda.jpg" }
  }
  geometry IndexedFaceSet {
   coord Coordinate {
    point [ -10 0 0, 10 0 0, 10 25 0, -10 25 0 ]
   }
   coordIndex [0,1,2,3,-1]
   texCoord TextureCoordinate {
    point [0 0, 1 0, 1 1, 1 0]
   }
   texCoordIndex [0,1,2,3,-1]
  }
}

esempio 3

Come si puo' vedere abbiamo ora quattro copie della Gioconda mappate sul piano. Una tale operazione puo' essere molto utile quando dobbiamo mappare una texture che rappresenta un certo materiale sopra una superficie molto grossa. Applicando la texture singola a tutto l'oggetto si ha una dilatazione eccessiva che porta a dettagli molto grossolani.

Fornendo valori maggiori di 1 al campo scale si ottiene il rimpicciolimento della texture. Valori minori di 1 (ma comunque maggiori di 0) realizzano invece un ingrandimento.

Vediamo ora brevemente il funzionamento degli altri campi.

Qui di seguito riporto due esempi; il primo riguarda una traslazione della texture, mentre l'altro una rotazione effettuata attorno al centro.

Primo esempio:

  TextureTransform {
                  translation .5 0
                }

esempio 4

Secondo esempio:

 TextureTransform {
                    center .5 .5
                    rotation 1.57
                  }

esempio 5

Direi che possiamo fermarci a questo livello con le textures. Penso che abbiamo analizato grossomodo tutti gli aspetti piu' importanti. Solo un ultimo appunto: non si possono avere oggetti con diverse textures, a meno che l'oggetto stesso non sia definito da piu' nodi Shape.

Rimando inoltre al tutorial sull'uso di Vrml che trovate al sito:

http://www.ywd.com/cindy/texture.html

Questo e' molto piu' completo del presente documento e presenta inoltre diversi esempi, nonche' una introduzione per comprendere come effettivamente lavorano le textures (che qui non riporto per motivi di spazio).