RAYTRACING, COME GODO :-) Corso di Ray-Tracing per principianti Parte 4/7 di Alfonso Martone (alfmar) Riparto all'assalto con altri files .POV, stavolta vediamo un po' di tutto. La prima cosa da considerare e' il caos che si puo' fare con le coordinate tridimensionali (i vettori specificati tra parentesi angolari: <0,0,0>...). Nella mia piccola esperienza ho notato che la cosa migliore e' definire una scena con unita' di misura "quasi reali". Per cui "assemblando" una stanza conviene usare per esempio i "centimetri" - consideriamo uno spazio largo, in centimetri, x=500, y=300, z=400, cioe' una stanza di 5 metri per 4 ed alta 3 : l'osservatore (camera) sara' alla porta (cioe' x=150, y=160, z=0), il lampadario sara' a x=250, y=260, z=200 (centro soffitto meno 40 cm), e cosi' via. Ovviamente questo livello di difficolta' lo affronteremo in seguito, per immagini piu' semplici quasi sempre conviene usare come look_at della telecamera il punto <0,0,0>, cioe' l'origine degli assi, e piazzare la location in un altro punto un po' piu' lontano degli oggetti (che saranno evidentemente disposti intorno all'origine, altrimenti non vedremo un bel tubo). Nei prossimi esempi faro' proprio cosi'. Gli oggetti che consideriamo oggi sono ancora oggetti del linguaggio di POV (sui quali ovviamente POV fa tutte le ottimizzazioni possibili). Abbiamo a disposizione, oltre alla sphere, anche i parallelepipedi, per esempio: box { <1,2,3>, <5,4,8> texture { Silver2 } } equivale ad un parallelepipedo di grandezza x=4, y=2, z=5 (ricordate che z e' la "profondita'" cioe' dal vostro occhio verso l'interno dello schermo), con uno dei due spigoli a <1,2,3> e l'altro a <5,4,8> (per cui la grandezza sara' x=5-1=4, y=4-2=2, z=8-3=5). Abbiamo a disposizione anche coni e tronchi di cono: cone { <1,2,3>, 1.5, <5,6,8>, 5 texture { Silver2 } } Il primo vettore <1,2,3> specifica il centro superiore; segue il raggio della circonferenza superiore, quindi il centro inferiore e il raggio inferiore. L'esempio citato e' un cono il cui asse centrale e' la linea da <1,2,3> a <5,6,8>. Per ottenere un cono perfetto anziche' un tronco di cono basta usare 0 al posto del raggio superiore (anziche' 1.5 comenell'esempio). Infine abbiamo i cilindri; usano piu' o meno la stessa logica (e magari anche le stesse routines, visto che un cono con i raggi superiore e inferiore uguali non e' altro che un cilindro)... tanto per usarli, si usano cosi': cylinder { <5,2,1>, <9,5,3>, 1.3333 texture { Silver2 } } Nell'ordine: i due punti estremi dell'asse centrale, il raggio, e la deliziosa immancabile texture. Tutti gli oggetti sono "texturizzabili"; negli esempi uso sempre quelle definite in "textures.inc" perche' il discorso in dettaglio (come crearne o modificarne una) lo analizzeremo piu' avanti; nel frattempo queste qui a base di granito, marmo, legno, metallo, sono una vera delizia... :-) Vediamo dunque un esempio: #include "colors.inc" #include "textures.inc" camera {location <0,0,-20> look_at <0, -4.5, 0> } sphere {<-5,0,0>,2 texture { Gold_Texture}} box {<-2,-2,-2>, <2, 2, 2> texture { Apocalypse }} cone {<5,2, 0>,0 <5,-2, 0>,2 pigment { DMFWood4 }} cylinder {<-5,-5, 0>, <5,-5, 0>,2 texture { Yellow_Pine }} light_source{<-9,4,-2> color White } light_source{<0,5,-2> color Magenta } light_source{<9,4,-2> color Yellow } Inizialmente avevo posto la telecamera a <0,0,-20> guardando verso <0,0,0>, poi dopo aver "sistemato" anche il cilindro, ho pensato che non ce l'avrei fatta ad inquadrarlo tutto ed ho spostato piu' giu' il centro del mirino, precisamente <0,-4.5,0> ("-4.5" sull'asse y, cioe' proprio "piu' giu'"). Solita sfera, un po' a sinistra della visuale (che o riginariamente era a <0,0,0>), di raggio 2 - quindi "sfruttera'" nell'asse delle x lo spazio da -5-2=-7 a -5+2=-3. Per cui la "box" la piazzo a partire da x=-2. Nel nostro esempio e' un cubo perfetto, di lato 4 (2-(-2)=4), guardacaso con centro proprio nell'origine. Non ho letto il manuale, se non sbaglio c'e' anche un comando "cube" per risparmiarsi ancora un po' di fatica... La box finisce a x=2, per cui piazziamo un cono con la base maggiore a x=5 e di raggio 2 (quindi sara' largo da x=5-2=3 a x=5+2=7), sempre "allineato" come altezza a sfera e cubo. Dunque abbiamo messo in linea sfera, cubo e cono, quest'ultimo non ha una texture completa ma solo il colore. Infine ho messo "sotto" il cilindro, di raggio 2, -5 unita' piu' giu' dell'origine - i tre oggetti di sopra sono "estesi" sull'asse delle y da y=2 a y=-2, dunque il cilindro, essendo da -5-2=-7 a -5+2=-3, non andra' a collidere con loro. Anche questo vedremo in seguito - intersezioni e unioni potranno fare "forme" stranissime ed effetti notevoli. Osservate infine come ho messo le tre (!) sorgenti di luce (per cui il rendering sara' piu' lentuccio dei primi velocissimi esempi dello scorso messaggio). Guardando le coordinate scoprirete che la prima e la terza sono piu' o meno "simmetriche", e la piu' vicina ha una luce meno intensa delle altre due. Per "meno intensa" intendo minor "carico R/G/B". Infatti i colori si definiscono proprio in termini di "carico" Red/Green/Blue, con valori che vanno da 0 a 1, che ricordano il modo di costruire le "palette VGA". Per esempio il rosso completo si ottiene con: color red 1 green 0 blue 0 In colors.inc e' definito gia' come Red, per cui scrivere"color {Red}" anziche' "color red 1 green 0 blue 0" non cambia niente. NB dato che green e blue sono zero, possiamo anche eliminarli. Dicevo prima, "carichi" diversi: infatti il giallo e' "color red 1 green 1" e il magenta e' "color red 1 blue 1", mentre il bianco e' "color red 1 green 1 blue 1". Oops, questo significa che giallo e magenta hanno lo stesso "carico", anch'io sono un essere umano e posso sbagliarmi :-). Nell'esempio si vedono solo zeri e uni; sono valori in virgola mobile; volendo infatti ottenere un "due terzi magenta" basta fare "color red 0.66 blue 0.66". Il colore oro (occhio, il colore, non la texture) e' definito ("Gold") come "color red 0.8 green 0.498 blue 0.196". Terminato il rendering dell'immagine di sopra, notiamo il piccolo grave errore di valutazione commesso poco fa: il "-4.5" era un po' troppo. Gli oggetti a video si vedono lo stesso; sono un po' lontanucci, quindi portare un po' piu' avanti la telecamera fara' bene; al momento sono ancora ben visibili, in particolare le ombre. Non essendoci nessuna superficie riflettente dietro, l'immagine e' un po' scura (infatti lo spazio 3D inizialmente e' vuoto e tutto nero; mettere solo le luci non servirebbe a niente perche' se non riflettono su nessun oggetto dello spazio, si "disperdono" nel vuoto. Una volta completato questo, voglio fare una piccola modifica e commetto un errore grossolano, aggiungendo infatti l'oggetto: plane { -z, 7 texture { PinkAlabaster } } Con questa linea ho definito un piano che ha come normale l'asse "-z", cioe' l'asse z pero' nella direzione del nostro occhio. In pratica avrei definito un "pavimento" di alabastro con normale "-z=7" (vi ricordo che nel linguaggio di POV "x", "y" e "z" sono le famose tre abbreviazioni dei vettori di base unitari: x = <1,0,0>, y = <0,1,0>, z=<0,0,1>, per cui -z = <0,0,-1>). La normale "-z = 7" (cioe' il piano z=-7), che nelle mie intenzioni doveva essere "a quota 7", cioe' giusto dietro gli oggetti (nel senso di "molto piu' in profondita' di loro"), e' invece andata a coprirli - col rendering mi e' uscita una tremendissima immagine completa di alabastro, ben illuminato, ma senza gli oggetti (che sono evidentemente andati a finire dietro) perche', dato che i piani si definiscono come in geometria (ax+by+cz=k), ho commesso l'errore di piazzarlo "prima" dell'origine e non "dopo" (infatti -z=7 diventa z=-7, cioe' "7 unita' dall'origine verso l'occhio", cioe' piu' delle 2 o 3 unita' di misura della profondita' massima degli oggetti verso il mio occhio). Correggo dunque il piano e ne approfitto anche per correggere la visuale: dato che mi son finalmente sprecato a misurare e a notare che gli oggetti si estendono sull'asse y da -7 a 2 (cioe' 9 unita', cioe' il centro e' 4.5 sopra il punto piu' basso, cioe' -7+4.5=-2.5), cambio l'errato "look_at <0, -4.5,0>" in "look_at <0, -2.5, 0>" e ne approfitto per spostare avanti la telecamera alla "location <0, 0, -15>". Tutti i calcoli (piano e telecamera) sono esatti e dunque non dovrei avere problemi (vi faccio il report a fine render :-). Prima di chiudere la "puntata" di oggi, una piccola osservazione. Gli oggetti che abbiamo visto (sphere, cone, etc) sono tutti "finiti", mentre il piano e' "infinito" - il che ovviamente richiede maggiore complessita' di calcolo. Per definire altri oggetti ci sara' bisogno di mettere mano a quadriche, quartiche ed altre cose dai nomi similmente preoccupanti. Per fortuna i pochi oggetti che abbiamo a disposizione gia' ci permettono di fare parecchio.