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  comenell'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.