home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 109 / EnigmaAmiga109CD.iso / software / testi / corsoasm / sorgenti_3d / 3d-gfx2.txt next >
Text File  |  1998-09-22  |  132KB  |  2,978 lines

  1.  
  2.         **********************************************************
  3.     ******************************************************************
  4.   **********************************************************************
  5.  *********                                                      *********
  6. ********            REAL TIME COMPUTER GRAPHICS 3D                ********
  7. *******                                                            *******
  8. *******            tECNICHE e aLGORITMI aPPLICATIVI                *******
  9. *******             pER lA rEALIZZAZIONE dI mOTORI                 *******
  10. ********               iN gRAFICA 3d cOL cOMPUTER                 ********
  11.  *********                                                      *********
  12.   **********************************************************************
  13.     ******************************************************************
  14.         **********************************************************
  15.  
  16.        Realizzato da    :  -+- Cristiano Tagliamonte -+- Aceman/RamJam -+-
  17.        Ultima revisione :  22 Luglio 1998
  18.        Versione         :  0.02
  19.  
  20.                                   !     !
  21.                     _..-/\        |\___/|        /\-.._
  22.                  ./||||||\\.      |||||||      .//||||||\.
  23.               ./||||||||||\\|..   |||||||   ..|//|||||||||\.
  24.             ./||||||||||||||\|||||||||||||||||/|||||||||||||\.
  25.           ./||||||||||||||||||||||||||||||||||||||||||||||||||\.
  26.          /||||||||||||||||||||||||||||||||||||||||||||||||||||||\
  27.         '||||||||||||||||||||||||||||||||||||||||||||||||||||||||'
  28.       '||||'     '|||||/'   ''\|||||||||||||/''   '\||||||'    '|||'
  29.       |/'          '\|/         \!|||||||!/         \|/'         '\|
  30.       V              V            \|||||/            V             V
  31.       '              '             \|||/             '             '
  32.                                     \./
  33.                                      V
  34.  
  35.  
  36. ==========================================================================
  37. ============================ pREFAZIONE ==================================
  38. ==========================================================================
  39.  
  40.  
  41.  Questo breve testo vuol essere di aiuto a chi vuol intraprendere l'arduo
  42. ed affascinante cammino verso la programmazione di un motore grafico 3D,
  43. scoprendone inizialmente i concetti di base per poi affrontare i piu'
  44. complessi e spettacolari effetti realizzabili. Col termine "motore" si
  45. indica l'insieme di routine atte alla gestione e alla manipolazione di
  46. specifici dati che una volta elaborati nella maniera dovuta daranno come
  47. risultato la visualizzazione dell'ambiente 3D in tempo reale.
  48.  
  49.  Il seguente testo non vuol essere un corso di programmazione orientato
  50. alle applicazioni 3D, bensi' un'opera nella quale vengano forniti piu'
  51. semplicemente i concetti sui quali si fondano molti motori 3D.
  52.  
  53.  Il tutor e' stato suddiviso in parti, in cui ogni parte e' composta da
  54. capitoli tra loro indipendenti, ovvero (esclusi alcuni casi in cui certi
  55. argomenti sono legati tra loro intrinsecamente) la comprensione di un
  56. argomento citato in un determinato capitolo non dipende dagli altri
  57. capitoli. In questo modo si rende piu' efficace ed immediata la
  58. consultazione del presente testo. Naturalmente un capitolo potrebbe
  59. richiedere la conoscenza di particolari argomenti, pertanto all'inizio di
  60. ognuno di essi vengono elencati eventuali titoli dei capitoli in cui
  61. vengono fornite le informazioni necessarie affinche' sia possibile
  62. comprendere appieno gli argomenti trattati. Inoltre, nella lista degli
  63. argomenti necessari alla comprensione, potranno essere presenti eventuali
  64. sigle "n.p." atte ad indicare che tali argomenti, a causa della loro
  65. natura, non sono presenti nel seguente testo.
  66.  
  67.  Nel caso il lettore non abbia le dovute conoscenze del linguaggio C si
  68. raccomanda di porgere priorita' alla consultazione dell'Appendice A, nella
  69. quale vengono forniti chiarimenti riguardo la sintassi dello pseudocodice
  70. utilizzato, rispresa appunto dal C.
  71.  
  72.  Per concludere si raccomanda la conoscenza di base della trigonometria,
  73. dell'algebra lineare e di un linguaggio di programmazione (meglio se non
  74. troppo evoluto).
  75.  
  76.  
  77. ==========================================================================
  78. ============================= iNDICE =====================================
  79. ==========================================================================
  80.  
  81.    Parte 0 : Cenni di grafica 2D relativa agli 80x86 con VGA
  82.   
  83.       Introduzione ............................................. 0.0
  84.       Apertura di uno schermo 320*200 a 256 colori ............. 0.1
  85.       Organizzazione della memoria video ....................... 0.2
  86.       Tracciamento di un pixel ................................. 0.3
  87.       Settare la palette di colori ............................. 0.4
  88.       Sincronismo del processore col refresh video ............. 0.5
  89.  
  90.    Parte 1 : Geometry engine
  91.  
  92.       Introduzione ............................................. 1.0
  93.       Trasformazioni prospettiche .............................. 1.1
  94.          Concetto di prospettiva ............................... 1.1.0
  95.          Implementazione della prospettiva ..................... 1.1.1
  96.       Traslazione .............................................. 1.2
  97.          Applicazione delle traslazioni ........................ 1.2.0
  98.       Trasformazioni in scala .................................. 1.3
  99.          Principio
  100.          Algoritmo
  101.          Realizzazione
  102.       Rotazione ................................................ 1.4
  103.          Concetto di rotazione ................................. 1.4.0
  104.          Rotazione intorno l'origine del sistema
  105.          Rotazione intorno un'asse arbitrario
  106.          Ottimizzazioni
  107.       Affine transformations
  108.          Principio
  109.          Algoritmo
  110.          Realizzazione
  111.       Gestione delle telecamere in una scena 3D
  112.          Principio
  113.          Algoritmo
  114.          Realizzazione
  115.       Gerarchia degli oggetti
  116.          Principio
  117.          Algoritmo
  118.          Realizzazione
  119.  
  120.    Parte 2 : Hidden surface removal
  121.  
  122.       Introduzione ............................................. 2.0
  123.       Back face culling
  124.          Principio
  125.          Realizzazione
  126.       Algoritmo del pittore
  127.       Algoritmi di ordinamento
  128.          Radix sort
  129.          Byte sort
  130.       Z-Buffer
  131.       Clipping 2D
  132.          Principio
  133.          Clipping di una linea
  134.             Algoritmo per interpolazione
  135.             Algortimo di Liang-Barsky
  136.          Clipping di un poligono
  137.             Algoritmo di Sutherland-Hodgman
  138.             Algoritmo di Liang-Barsky
  139.       Ray casting
  140.          Principio
  141.          Algoritmo
  142.          Realizzazione
  143.       Binary space partition
  144.          Principio
  145.          BSP trees 2D
  146.             Algoritmo
  147.             Realizzazione
  148.          BSP trees 3D
  149.             Algoritmo
  150.             Realizzazione
  151.  
  152.    Parte 3 : Tecniche di rendering
  153.  
  154.       Introduzione ............................................. 3.0
  155.       Wireframe
  156.          Principio
  157.          Tracciamento di una linea
  158.             Algoritmo di Bresenham
  159.             Algoritmo per interpolazione
  160.       Filled vector
  161.          Principio
  162.          Algoritmo
  163.          Realizzazione
  164.          Ottimizzazioni
  165.       Metodi di ombreggiatura
  166.          Calcolo della luminosita'
  167.             Algoritmo
  168.             Realizzazione
  169.             Ottimizzazioni
  170.          Ambient shading
  171.          Flat shading
  172.             Principio
  173.             Algoritmo
  174.             Realizzazione
  175.          Depth cueing
  176.             Principio
  177.             Algoritmo
  178.             Realizzazione
  179.          Gouraud shading
  180.             Principio
  181.             Algoritmo
  182.             Realizzazione
  183.          Phong shading
  184.             Principio
  185.             Algoritmo
  186.             Realizzazione
  187.          Metal shading
  188.          Modello di illuminazione di Phong
  189.         Metodi di mappatura delle texture
  190.          Affine texture mapping
  191.             Principio
  192.             Algoritmo
  193.             Realizzazione
  194.          Free direction texture mapping
  195.             Principio
  196.             Algoritmo
  197.             Realizzazione
  198.          Texture mapping bilineare
  199.             Principio
  200.             Algoritmo
  201.             Realizzazione
  202.          Texture mapping biquadratico
  203.             Principio
  204.             Algoritmo
  205.             Realizzazione
  206.          Correzione prospettica
  207.             Principio
  208.             Algoritmo
  209.             Realizzazione
  210.          Reflection mapping
  211.             Principio
  212.             Algoritmo
  213.             Realizzazione
  214.          Phong mapping
  215.          Environment mapping
  216.       Tecniche avanzate e ibride
  217.          Principio
  218.          Texture + Flat
  219.          Texture + Depth
  220.          Texture + Gouraud
  221.          Texture + Metal
  222.          Texture trasparenti
  223.          Bump mapping
  224.             Principio
  225.             Algoritmo
  226.             Realizzazione
  227.  
  228.    Parte 4 : Effetti Speciali
  229.  
  230.       Introduzione
  231.       Morphing dell'oggetto
  232.          Principio
  233.          Algoritmo
  234.          Realizzazione
  235.       Antialiasing
  236.          Principio
  237.          Motion blur
  238.             Algoritmo
  239.             Realizzazione
  240.       Lens flare
  241.          Principio
  242.          Algoritmo
  243.          Realizzazione
  244.  
  245.    Parte 5 : Appendici
  246.  
  247.       A: Caratteristiche dello pseudocodice utilizzato
  248.       B: Rendering pipeline
  249.       C: Notazione in virgola fissa
  250.       D: Coordinate polari
  251.       E: Gestione degli oggetti
  252.       F: Interpolazione lineare
  253.       G: Cenni di algebra lineare .............................. G.0
  254.          I vettori ............................................. G.1
  255.             Definizione di vettore ............................. G.1.0
  256.             Operazioni tra vettori ............................. G.1.1
  257.             Vettore unitario ................................... G.1.2
  258.             Vettore normale .................................... G.1.3
  259.          Le matrici ............................................ G.2
  260.             Definizione ........................................ G.2.0
  261.             Operazioni tra matrici ............................. G.2.1
  262.       H: Tecniche di wrapping/mapping della texture............. H.0
  263.          Mapping normalizzato .................................. H.1
  264.          Mapping planare ....................................... H.2
  265.          Mapping sferico ....................................... H.3
  266.  
  267.    Note finali
  268.  
  269.  
  270. ==========================================================================
  271. ======== pARTE 0 : cENNI dI gRAFICA 2d rELATIVA aGLI 80X86 e vga =========
  272. ==========================================================================
  273.  
  274.  Argomenti necessari alla comprensione: assembly 80x86 (n.p.)
  275.  
  276.  
  277. *****  0.0        iNTRODUZIONE
  278.  
  279.  In questa sezione non approfondiremo l'utilizzo delle VGA e SVGA, bensi'
  280. tratteremo le piu' elementari funzioni atte alla manipolazione di
  281. strutture bidimensionali, indispensabili per poter realizzare progetti in
  282. computer grafica 3D.
  283.  
  284.  In altre parole verrano date le nozioni minime per poter comprendere ed
  285. applicare i concetti sulla computer grafica 3D, senza esporre in maniera
  286. peculiare le caratteristiche delle VGA/SVGA, il cui studio richiederebbe
  287. una documentazione dedicata, che non e' l'obiettivo di questo tutor.
  288. A tal proposito verra' analizzato il solo modo video 320*200 a 256 colori,
  289. il piu' immediato da gestire.
  290.  
  291.  Tutti gli argomenti presentati in questa sezione richiedono espressamente
  292. la conoscienza dell'Assembly relativo agli 80x86 presumendo di lavorare in
  293. modalita' protetta 32 bit con un modello di memoria di tipo flat.
  294.  
  295.  
  296. *****  0.1        aPERTURA dI uNO sCHERMO 320*200 A 256 cOLORI
  297.  
  298.  Analizziamo questa manciata di istruzioni assembly:
  299.  
  300.         mov     eax,13h         ; imposta EAX al cui valore corrisponde
  301.                                 ; la modalita' video da aprire. Nel nostro
  302.                                 ; caso 13h ci permette l'apertura di uno
  303.                                 ; schermo 320*200 a 256 colori.
  304.         int     10h             ; interrupt per la gestione della grafica.
  305.  
  306.  Come possiamo vedere e' molto semplice utilizzare una risoluzione 320*200
  307. a 256 colori: basta eseguire giusto queste due istruzioni assembly e tale
  308. modo video viene aperto!
  309.  
  310.  Nel caso volessimo ripristinare la vecchia modalita' testo (standard del
  311. dos) non dovremo far altro che seguire queste istruzioni:
  312.  
  313.         mov     eax,03h         ; imposta EAX al cui valore corrisponde
  314.                                 ; la modalita' video da aprire. Nel nostro
  315.                                 ; caso 03h ci permette l'apertura di uno
  316.                                 ; schermo in modalita' testo 80*25.
  317.         int     10h             ; interrupt per la gestione della grafica.
  318.  
  319.  Questa coppia di istruzioni dovranno essere eseguite appena prima
  320. dell'uscita all' MS-DOS del nostro programma.
  321.  
  322.  
  323. *****  0.2        oRGANIZZAZIONE dELLA mEMORIA vIDEO
  324.  
  325.  Uno schermo 320*200 a 256 colori ha come indirizzo fisico di partenza
  326. 000A0000h. A partire da tale indirizzo ogni byte rappresentera' un pixel
  327. visualizzato su schermo e di conseguenza, dato che un byte puo' coprire un
  328. range di valori da 0 a 255, potremo utilizzare al massimo 256 colori.
  329.  
  330.  Il byte corrispondente l'indirizzo 000A0000h e' il pixel estremo
  331. superiore a sinistra, mentre ai relativi pixel adiacenti corrisponderanno
  332. i byte immediatamente successivi a all'indirizzo 000A0000h. Il 321esimo
  333. byte (a partire da 000A0000h, quindi 000A0140h) risulta il pixel estremo
  334. sinistro della seconda riga.  Vediamo un semplice schema per chiarire le
  335. idee:
  336.  
  337.               +---- 000A0001h             . = ipotetico pixel sul monitor
  338.               |+--- 000A0002h
  339.               ||+-- 000A0003h
  340.               vvv
  341.  000A0000h -> ............................ <- 000A013Fh
  342.  000A0140h -> ............................ <- 000A027Fh
  343.  000A0280h -> ............................
  344.               ............................
  345.               ............................
  346.               ............................
  347.               ............................
  348.               ............................
  349.               ............................
  350.               ............................
  351.               ............................
  352.               ............................
  353.               ............................
  354.               ............................
  355.               ............................
  356.               ............................
  357.  000AF8C0h -> ............................ <- 000AF987h
  358.  
  359. E' possibile notare che l'area di memoria interessata alla visualizzazione
  360. dei pixel e' compresa tra 000A0000 e 000AF987, che corrisponde a 64000
  361. byte (difatti 320*200=64000).
  362.  
  363.  
  364. *****  0.3        tRACCIAMENTO dI uN pIXEL
  365.  
  366.  Per visualizzare un pixel conoscendone le coordinate X e Y dovremo
  367. calcolare la giusta locazione di memoria su cui e' posto in memoria. Per
  368. far cio' occorrera' utilizzare una legge matematica che a partire dalle
  369. coordinate X e Y ricavi l'indirizzo effettivo del pixel, ovvero:
  370.  
  371.         x        <- coordinata x del pixel (range: da 0 a 319)
  372.         y        <- coordinata y del pixel (range: da 0 a 199)
  373.         destaddr <- indirizzo fisico effettivo del pixel
  374.         destaddr = 000A0000 + 320*y + x
  375.  
  376. La formula appena vista permette si' un corretto calcolo dell'indirizzo
  377. fisico del pixel, ma non la si puo' propriamente definire ottimizzata in
  378. quanto e' presente una moltiplicazione; cerchiamo di sostituirla con
  379. qualche operazione meno pesante:
  380.  
  381.         320*y = (256 + 64)*y = 256*y + 64*y = y<<8 + y<<6
  382.         destaddr = 000A0000 + y<<8 + y<<6 + x
  383.  
  384. Abbiamo eliminato la moltiplicazione utilizzando due snelli shift a
  385. sinistra (256 e 64 sono potenze di 2) che rendono il nostro codice
  386. sicuramente piu' efficiente.
  387.  
  388.  
  389. *****  0.4        sETTARE lA pALETTE dEI cOLORI
  390.  
  391.  Abbiamo gia' detto che ogni byte rappresenta il colore di un pixel, tale
  392. byte si definisce "chunky pixel". Questo byte non e' altro che un indice
  393. di una tabella dove sono indicati i 256 colori da utilizzare. Infatti
  394. l'insieme dei colori esistenti nella nostra risoluzione (320*200) sono
  395. 262144, di cui possiamo sceglierne 256 da poter visualizzare. Analizziamo
  396. dove e come definire tale tabella.
  397.  
  398.  Ogni colore idealmente realizzabile puo' essere scomposto da tre
  399. componenti: il rosso, il verde e il blu. Mescolando questi tre colori
  400. secondo intensita' differenti e' possibile ottenere tutti i colori
  401. rappresentabili dalla nostra VGA.
  402.  
  403.  Nel nostro caso, per dimensionare un qualsiasi colore, definiremo le
  404. relative componenti R (red), G (green) e B (blue).
  405.  
  406.  L'insieme delle componenti RGB rappresenta il colore che vogliamo
  407. ottenere. Una componente R, G o B non e' altro che un byte in cui
  408. vengono presi in considerazione i 6 bit meno significativi; in altre
  409. parole per ogni componente abbiamo a disposizione un range di valori
  410. compreso tra 0 e 63, ovvero 64 combinazioni (infatti 64*64*64=262144
  411. ovvero l'intera gamma di colori a disposizione). Un valore basso indica
  412. una bassa intensita' (tonalita' scura), al contrario piu' il valore sara'
  413. alto e maggiore sara' l'intensita' di quella componente (tonalita'
  414. chiara).
  415.  
  416.  Per settare la palette (ovvero la tavolozza di colori che si vuol
  417. utilizzare), ci serviranno solo le due porte hardware di indirizzo 03C8h e
  418. 03C9h. Ecco la procedura da seguire per settare un colore:
  419.  
  420.   - scrivere nella porta 03C8h un byte, il quale indichera' quale colore
  421.     andremo a settare (0 e' il primo colore, 255 e' l'ultimo);
  422.   - scrivere nella porta 03C9h il byte contenente la componente R (rosso);
  423.   - scrivere nella porta 03C9h il byte contenente la componente G (verde);
  424.   - scrivere nella porta 03C9h il byte contenente la componente B (blu).
  425.  
  426.  Da notare che una volta settato un colore si potra' continuare a scrivere
  427. nella porta 03C9h i successivi colori senza dover memorizzare (di volta in
  428. volta) il numero del colore successivo nella porta 03C8h.
  429.  
  430.  Tutto cio' tradotto in assembly porta ad un codice che puo' essere
  431. scritto nella seguente forma:
  432.  
  433.         lea     esi,[palette]   ; puntatore alla tabella dei colori
  434.         mov     edx,03C8h       ; porta 03C8h
  435.         xor     eax,eax         ; puliamo eax
  436.         out     dx,al           ; iniziamo a settare il primo colore
  437.         inc     edx             ; porta 03C9h
  438.         mov     ecx,256         ; numero di iterazioni del loop
  439. @@loop:
  440.         mov     al,[esi]        ; leggiamo la componente Red...
  441.         out     dx,al           ; ... e la memorizziamo
  442.         mov     al,[esi+1]      ; leggiamo la componente Green...
  443.         out     dx,al           ; ... e la memorizziamo
  444.         mov     al,[esi+2]      ; leggiamo la componente Blue...
  445.         out     dx,al           ; ... e la memorizziamo
  446.         add     esi,3           ; prossimo colore da settare
  447.         dec     ecx
  448.         jnz     @@loop
  449.         ...
  450.         ...                     ; altro codice
  451.         ...
  452.  
  453. palette:                        ; la tabella dei colori
  454.         db      00, 00, 00      ; colore 00h : R, G, B
  455.         db      11, 23, 45      ; colore 01h : R, G, B
  456.         ...
  457.         ...                     ; altri colori della tavolozza
  458.         ...
  459.         db      63, 63, 63      ; colore FFh : R, G, B
  460.  
  461.  Il sorgente appena esaminato non fa altro che leggere i byte nella
  462. tabella dei colori a partire dall'indirizzo 'palette' e li scrive nella
  463. porta 03C9h non prima di aver precisato nella porta 03C8h che intendiamo
  464. iniziare a settare la palette a partire dal colore 00h, fino ad arrivare
  465. all'ultimo, ovvero il colore FFh.
  466.  
  467.  
  468. *****  0.5        sINCRONISMO dEL pROCESSORE cOL rEFRESH vIDEO
  469.  
  470.  Utilizzando una risoluzione come la 320*200 a 256 colori, lo schermo
  471. viene aggiornato 70 volte al secondo (70 Hz). In altre parole quel che si
  472. visualizza sul monitor viene tracciato 70 volte al secondo, cio' permette
  473. l'illusione di realizzare animazioni fluide. Il refresh non e' che il
  474. procedimento fisico per cui lo schermo viene aggiornato (illuminando in
  475. maniera opportuna ogni pixel).
  476.  
  477.  Mettiamo caso di voler realizzare l'animazione di un oggetto 3D
  478. renderizzato in tempo reale. Per rendere il movimento di tale oggetto piu'
  479. fluido possibile dovremo calcolare un intero frame in un 70esimo di
  480. secondo. Consideriamo che le routine che abbiamo realizzato siano
  481. abbastanza ottimizzate da permettere che l'elaboratore calcoli oltre
  482. 70 fotogrammi per secondo. In questo caso dovremo scrivere una routine
  483. che, una volta renderizzato un frame, permetta al processore di attendere
  484. il termine del refresh video prima di calcolare il prossimo fotogramma.
  485. Questo e' cio' che si intende con "sincronizzazione del processore col
  486. refresh video".
  487.  
  488.  Ora vediamo come realizzare il tutto in Assembly evitando di analizzare
  489. dettagliatamente il codice:
  490.  
  491.         mov     edx,3DAh
  492. @@wait_refresh1:
  493.         in      al,dx
  494.         test    al,8
  495.         jz      @@wait_refresh1
  496. @@wait_refresh2:
  497.         in      al,dx
  498.         test    al,8
  499.         jnz     @@wait_refresh2
  500.  
  501.  In questo spezzone di codice il processore non fa altro che attendere
  502. (grazie al primo loop) che il pennello elettronico (che aggiorna lo
  503. schermo) giunga ad una ben determinata linea video, quindi (nel secondo
  504. loop) si aspetta che tale linea venga completamente tracciata.
  505.  
  506.  Questa tecnica viene applicata per evitare di calcolare un nuovo
  507. fotogramma ancor prima di averlo visualizzato.
  508.  
  509.  
  510. ==========================================================================
  511. ==================== pARTE 1 : gEOMETRY eNGINE ===========================
  512. ==========================================================================
  513.  
  514.  
  515. *****  1.0        iNTRODUZIONE
  516.  
  517.  In questa sezione studieremo la geometria che si cela dietro la computer
  518. grafica 3D, in quanto ogni elemento elaborato da un motore 3D consiste in
  519. un insieme di valori numerici che attribuiscono una o piu' proprieta'
  520. geometriche, quindi il movimento compiuto da un solido proiettato su
  521. monitor e' frutto di opportune trasformazioni geometriche.
  522.  
  523.  
  524. *****  1.1        tRASFORMAZIONI pROSPETTICHE
  525.  
  526.  Argomenti necessari alla comprensione: i vettori [G.1]
  527.  
  528.  
  529. *****  1.1.0      cONCETTO dI pROSPETTIVA
  530.  
  531.  Riguardo le nostre applicazioni (relative alla realizzazione di un motore
  532. 3d), alla base della prospettiva risiede il principio con cui una
  533. qualsiasi entita' (punto, linea, poligono o oggetto che sia) da uno spazio
  534. tridimensionale possa essere rappresentata in uno spazio a due dimensioni.
  535. Difatti uno dei primissimi problemi che intercorre nella scrittura di un
  536. engine 3d e' come visualizzare tali entita' su uno spazio 2d, ossia il
  537. monitor, quando all'origine ogni cosa viene rappresentata matematicamente
  538. in uno spazio 3d. Per far fronte a questa esigenza viene in aiuto la
  539. prospettiva che ci permette si applicare la "conversione" da uno spazio 3d
  540. ad uno spazio 2d.
  541.  
  542.  Innanzitutto va sottolineato che lo studio di trasformazioni prospettiche
  543. sara' limitato ai soli vettori. Infatti ogni entita' e' derivabile da un
  544. insieme ordinato di uno o piu' vettori (un punto e' rappresentabile con un
  545. vettore; una linea si puo' rappresentare matematicamente come quella
  546. coppia di vettori coincidenti con gli estremi della linea stessa; un
  547. poligono e' una sequenza ordinata di vertici, i quali possono essere
  548. indicati con vettori; infine un oggetto e' un insieme ben definito di
  549. poligoni).
  550.  
  551.  Dati un punto di vista e un piano di proiezione perpendicolare alla
  552. direzione di osservazione e posto di fronte al suddetto punto vista, il
  553. concetto di prospettiva e' di tracciare un raggio passante sul punto di
  554. vista e sul vettore 3d di cui si vuol conoscere la proiezione sul piano
  555. (questo avviene esclusivamente a livello teorico, a livello pratico non si
  556. traccia nessun raggio!); quindi il vettore coincidente con il punto di
  557. intersezione tra il raggio ed il piano, rappresentera' la proiezione di
  558. quel vettore.
  559.  
  560.  Dato un ipotetico punto di vista e un piano di proiezione (rispetto ai
  561. quali dovremo ricavare le coordinate 2d), consideriamo che la direzione
  562. di osservazione sia perpendicolare al nostro piano e che tale piano
  563. coincida con il piano XY del nostro spazio 3d (ovvero il piano di
  564. equazione z = 0). Inoltre la retta coincidente con la direzione di
  565. osservazione passa per l'origine dei tre assi del nostro sistema di
  566. riferimento XYZ. Questo implica che tale retta coincide con l'asse z,
  567. sul quale sara' quindi posto il punto di vista. Il monitor e' inteso come
  568. quella parte (ovvero quel sottospazio) di piano visibile. Vediamo
  569. praticamente la nostra situazione:
  570.  
  571.                   + monitor                    +----------+ monitor
  572.            asse Z |            _               |          |
  573.         <---------| - - - - - ¢_>              |    .     |
  574.                   |         occhio             | [0,0,0]  |
  575.                   +                            +----------+
  576.  
  577.  Questa sitazione e' molto importante, in quanto permette di semplificare
  578. concettualmente e algoritmicamente le trasformazioni prospettiche.
  579.  
  580.  
  581. *****  1.1.1      iMPLEMENTAZIONE dELLA pROSPETTIVA
  582.  
  583.  Abbiamo un vettore di cui conosciamo le coordinate [x, y, z]. Il nostro
  584. obiettivo consiste nel calcolare le coordinate [xs, ys] proiettate sullo
  585. schermo, quindi dobbiamo applicare una trasformazione prospettica.
  586.  
  587.  Consideriamo un punto, denominato A, nello spazio visibile dal video.
  588. Facciamo finta che il nostro monitor sia trasparente e che quel punto sia
  589. davvero presente nello spazio. Ora, la nostra situazione vista
  590. lateralmente e' la seguente:
  591.  
  592.                   A.      |
  593.                    |-_    |
  594.                    |  -_  |
  595.                    |    -_|A'
  596.                    |      +_
  597.                    |      | -_
  598.                    |      |   -_
  599.                    |      |     -_   _
  600.             /______|______|_______- ¢_> (occhio)
  601.             \ z    B      |B'      C
  602.                           |
  603.                           |y
  604.                           v
  605.  
  606. Mentre questo e' il caso analogo visto dall'alto:
  607.  
  608.                           ^
  609.                           |x
  610.                   A.      |
  611.                    |-_    |
  612.                    |  -_  |
  613.                    |    -_|A'
  614.                    |      +_
  615.                    |      | -_
  616.                    |      |   -_
  617.                    |      |     -_   _
  618.             /______|______|_______- ¢_> (occhio)
  619.             \ z    B      |B'      C
  620.                           |
  621.  
  622.         A  = vettore nello spazio [x, y, z] rappresentante il punto a cui
  623.              si vuol applicare la trasformazione prospettica;
  624.         B  = vettore avente la stessa coordinata z del vettore A, ma con
  625.              con coordinate x e y uguali a zero, e' del tipo [0, 0, z];
  626.         C  = vettore coincidente con il punto vista, per comodita' e'
  627.              posto sull'asse z (quindi ha le coordinate x,y = 0);
  628.         A' = vettore coincidente con la proiezione su video del vettore A,
  629.              e' della forma [xs, ys, 0];
  630.         B' = vettore avente la stessa coordinata z del vettore A', ma con
  631.              con coordinate x e y uguali a zero, e' esattamente [0, 0, 0],
  632.              cio' significa che B' coincide con l'origine degli assi;
  633.  
  634.  Si ricorda, come precedentemente definito, che il piano di proiezione e'
  635. quello di equazione z = 0, ovvero il piano XY, su cui coincide, appunto,
  636. il monitor. Quindi sara' chiaro perche' le coordinate [xs, ys] sono
  637. esattamente le coordinate 2d rappresentanti il nostro vettore 3d su
  638. schermo.
  639.  
  640.  Vediamo adesso come ricavare queste coordinate. Sia nella vista dall'alto
  641. che in quella laterale i triangoli ABC e A'B'C sono simili in quanto hanno
  642. un angolo in comune ed entrambi hanno un angolo retto, quindi possiamo
  643. scrivere la seguente proporzione:
  644.  
  645.         A'B' / AB = B'C / BC
  646.  
  647. che corrisponde a:
  648.  
  649.         A'B' = AB * B'C / BC
  650.  
  651.  Possiamo scrivere BC come BB'+B'C. Dato che B' coincide con l'origine, la
  652. distanza BB' equivale alla coordinata z del vettore B. Inoltre definiamo
  653. "d" come la lunghezza del lato B'C. La nostra formula si traduce nella
  654. seguente:
  655.  
  656.         A'B' = d * AB / (z + d)
  657.  
  658.  Rispetto alla vista laterale AB coincide con la coordinata y di A, mentre
  659. A'B' coincide con ys; sostituendo questi valori otteniamo proprio la
  660. formula per calcolare la y proiettata su schermo:
  661.  
  662.         ys = d * y / (z + d)
  663.  
  664.  Nel caso della vista dall'alto il discorso e' analogo, ma relativo alla
  665. coordinata x proiettata:
  666.  
  667.         xs = d * x / (z + d)
  668.  
  669.  Il valore d rappresenta la distanza tra il punto di vista e l'origine; e
  670. come tale puo' essere fissato arbitrariamente. Un buon valore con cui
  671. istanziare d e', ad esempio, 256.
  672.  
  673.  Bisogna tener conto che nel video le coordinate hanno come origine il
  674. punto in alto a sinistra, mentre per la trasformazione 3d->2d sono state
  675. considerate al centro del monitor. Per ovviare questo problema basta
  676. sommare, al termine dei calcoli per la proiezione, una costante a xs e a
  677. ys grazie alle quali i relativi assi verranno traslati di un numero di
  678. pixel equivalente alla costante. Riassumendo, ponendo d = 256, le
  679. coordinate proiettate sullo schermo sono equivalenti a:
  680.  
  681.         xc = larghezza schermo/2, per traslare l'ascissa a destra
  682.         yc = altezza schermo/2, per traslare l'ordinata in basso
  683.  
  684.         xs = 256 * x / (z + 256) + xc
  685.         ys = 256 * y / (z + 256) + yc
  686.  
  687.  Attenzione al fatto che la coordinata z del punto non puo' coincidere con
  688. il punto di vista dato che si verificherebbe una divisione per zero.
  689. Nemmeno si deve porre la profondita' di un punto inferiore a quella del
  690. punto di vista: sarebbe assurdo poter visualizzare punti posti dietro
  691. l'osservatore!
  692.  
  693.  Finalmente siamo in grado di svolgere trasformazioni prospettiche, che
  694. tuttavia risultano veramente semplici da applicare.
  695.  
  696.  
  697. *****  1.2        tRASLAZIONE
  698.  
  699.  Argomenti necessari alla comprensione: i vettori [G.1]
  700.  
  701.  
  702. *****  1.2.0      aPPLICAZIONE dELLE tRASLAZIONI
  703.  
  704.  Dato un vettore V che rappresenta un punto nello spazio, traslare V
  705. rispetto ad un vettore T significa trovare un ulteriore vettore,
  706. chiamiamolo W, che rappresenta il vettore V spostato, in direzione
  707. coincidente con il verso di T, di uno spazio equivalente alla
  708. lunghezza di T.
  709.  
  710.  Tutto cio' si traduce in una banalissima somma tra vettori, piu'
  711. precisamente tra il vettore V e T, quindi:
  712.  
  713.         V[xv, yv, zv] = vettore da traslare
  714.         T[tx, ty, tz] = vettore coincidente la traslazione da applicare
  715.         W[xw, yw, zw] = vettore coincidente V traslato rispetto a T
  716.  
  717.         W = V + T
  718.  
  719.         xw = xv + tx
  720.         yw = yv + ty
  721.         zw = zv + tz
  722.  
  723.  
  724. *****  1.3        tRASFORMAZIONI iN sCALA
  725.  
  726.  Argomenti necessari alla comprensione: i vettori [G.1]
  727.  
  728.  
  729. *****  1.3.0      aPPLICAZIONE dELLE tRASFORMAZIONI iN sCALA
  730.  
  731.  
  732.  
  733. *****  1.4        rOTAZIONE
  734.  
  735.  Argomenti necessari alla comprensione: i vettori [G.1], le matrici [G.2]
  736.  
  737.  
  738. *****  1.4.0      cONCETTO dI rOTAZIONE
  739.  
  740.  Consideriamo un orologio analogico, le sue lancette ruotano intorno
  741. l'asse passante per il centro dell'orologio e perpendicolare l'orologio
  742. stesso:
  743.                        \
  744.                         \ ______________
  745.                          X____________ /
  746.                         //\   12     // orologio
  747.                        //  \  /     //
  748.                       //9   \/___ 3//
  749.                      //           //
  750.                     //           //
  751.                    //     6     //
  752.                   //___________/X
  753.                  /_____________/ \  asse immaginario su cui
  754.                                   \ ruotano le lancette
  755.  
  756.  Tecnicamente parliamo di rotazione di un punto su di un asse, quando il
  757. punto si muove sul piano appartenente al punto stesso e perpendicolare
  758. all'asse in modo da non alterare la distanza punto-asse (che rimane
  759. costante). In questo modo il punto compie un movimento circolare intorno
  760. l'asse, ossia ci ruota intorno, e la distanza punto-asse funge da raggio
  761. per la circonferenza descritta dalla rotazione del punto in questione.
  762.  
  763.  La rotazione intorno l'asse y si puo' immaginare come la traiettoria
  764. percorsa da un punto che giri intorno l'asse y senza modificare la propria
  765. ordinata, che rimane appunto inalterata.
  766.  
  767.  
  768. *****  1.4.1      rOTAZIONE 2D
  769.  
  770.  
  771. *****  1.4.2      rOTAZIONE 3D
  772.  
  773.  
  774. ==========================================================================
  775. ========================= pARTE 5 : aPPENDICI ============================
  776. ==========================================================================
  777.  
  778.  
  779. ****************\
  780. *****************> Appendice D : coordinate polari
  781. ****************/
  782.  
  783. *****  D.0        iNTRODUZIONE
  784.  
  785.  Le coordinate polari vengono utilizzate in questo tutor al fine di
  786. comprendere il principio che sta alla base dell'applicazione delle
  787. rotazioni.
  788.  
  789.  
  790. ****************\
  791. *****************> Appendice G : cenni di algebra lineare
  792. ****************/
  793.  
  794. *****  G.0        iNTRODUZIONE
  795.  
  796.  La seguente appendice e' orientata a tutti coloro che non possiedono le
  797. nozioni di algebra lineare necessarie alla comprensione del testo ed in
  798. particolare della parte 1, relativa al geometry engine.
  799.  
  800.  
  801. *****  G.1        i vETTORI
  802.  
  803.  Argomenti necessari alla comprensione: cenni di geometria piana (n.p.)
  804.  
  805.  
  806. *****  G.1.0      dEFINIZIONE dI vETTORE
  807.  
  808.  Un vettore non e' altro che un oggetto matematico che rappresena una
  809. quantita' di valore con una direzione e un verso, o in termini spicci una
  810. linea. Nel contesto verranno sempre specificati vettori che vanno da un
  811. punto di coordinate [0, 0, 0] (l'origine degli assi) verso un altro punto
  812. [x, y, z], cosi' facendo si puo' affermare che questo vettore ha come
  813. quantita' [x, y, z].
  814.                   _
  815.                |  /|
  816.                | /
  817.         [0,0,0]|/         x
  818.            ----+------------>
  819.               /|\
  820.            z / | \
  821.                |  \
  822.                |   \
  823.                |    \. V
  824.               y|
  825.                v
  826.  
  827.  Un vettore viene indicato con una lettera, ad esempio nella precedente
  828. figura e' rappresentato il vettore V[x, y, z]. Il sistema di riferimento
  829. usato e' dato da tre variabili, immaginabile come un semplice piano
  830. cartesiano perpendicolare all'asse z. Per semplicita' consideriamo la
  831. crescita della y verso il basso (e non verso l'alto) in modo da ridurre i
  832. calcoli da svolgere per la visualizzazione di un punto sullo schermo.
  833. L'asse z cresce quando esso si allontana dall'osservatore, mentre
  834. diminuisce nel caso si avvicini all'osservatore.
  835.  
  836.  Utilizzeremo i vettori per definire ogni punto nello spazio (e in
  837. particolare i vertici di ogni oggetto 3d), cio' non significa in termini
  838. pratici che un vettore costituisca una linea visualizzata su schermo,
  839. bensi' indica il segmento immaginario compreso tra l'origine degli assi ed
  840. un punto nello spazio.
  841.  
  842.  
  843. *****  G.1.1      oPERAZIONI tRA vETTORI
  844.  
  845.  Innanzitutto va specificato che faremo uso fondamentalmente di vettori 2d
  846. e 3d in quanto il nostro motore gestira' punti nello spazio piano (per
  847. il tracciamento di poligoni su monitor) e tridimensionale (per eventuali
  848. trasformazioni geometriche), quindi vettori del tipo:
  849.  
  850.         v2 = [ x , y ]        <---  generico vettore 2d
  851.         v3 = [ x , y , z ]    <---  generico vettore 3d
  852.  
  853.  Tutte le operazioni tra vettori sono possibili solo tra vettori che hanno
  854. la stessa dimensione, quindi una qualsiasi operazione tra un vettore 2d ed
  855. uno 3d sono impossibili. Di seguito vediamo algebricamente quali sono
  856. queste operazioni:
  857.  
  858.  - ADDIZIONE: il risultato della somma di due vettori e' ancora un vettore
  859. della stessa dimensione dei due operandi:
  860.  
  861.         v1 = [ x1 , y1 , z1 ]
  862.         v2 = [ x2 , y2 , z2 ]
  863.  
  864.         v3 = v1 + v2 = [ x1+x2 , y1+y2 , z1+z2 ]
  865.  
  866. dove nel nostro caso x1, x2, y1, y2, z1 e z2 sono ovviamente numeri
  867. reali (che possono essere rappresentati come numeri in virgola fissa
  868. oppure preferibilmente come numeri in virgola mobile).
  869.  
  870.  Da notare che il vettore [0, 0, 0] coincide con l'operando nullo di
  871. questa operazione, ovvero addizionando tale vettore ad un qualsiasi
  872. vettore V otteremo come risultato sempre il vettore V. Inoltre tale
  873. operazione gode della proprieta' commutativa.
  874.  
  875.  Fisicamente e' possibile immaginare la somma di due vettori 2d nella
  876. seguente maniera:
  877.  
  878.         v1 = [ x1 , y1 ]
  879.         v2 = [ x2 , y2 ]
  880.  
  881. Ora immaginiamo di trovarci sul vettore v1. Quindi il risultato della
  882. somma v1+v2 si puo' intendere come il risultato dello spostamento (dalla
  883. posizione indicata da v1) di x2 posizioni orizzontali e y2 posizioni
  884. verticali, queto implica la nostra posizione finale coincidera' col
  885. vettore [x1+x2 , y1+y2], ovvero il risultato dell'addizione.
  886.  
  887.  - NEGAZIONE: il risultato della negazione di un vettore v1 e' ancora un
  888. vettore della stessa dimensione di v1 e che e' equidistante dall'origine
  889. rispetto a v1, ma e' di verso opposto (in altre parole e' simmetrico
  890. rispetto alla retta passante per l'origine, la quale e' perpendicolare
  891. al vettore v1):
  892.  
  893.         v1 = [ x , y , z ]
  894.         v2 = - v1 = [ -x , -y , -z ]
  895.  
  896. Anche in questo caso l'elemento nullo di questa operazione coincide con il
  897. vettore dell'origine [0, 0, 0].
  898.  
  899.  - PRODOTTO CON UN FATTORE: il prodotto tra un vettore v1 ed un fattore
  900. (inteso come un numero reale o in virgola mobile) e' ancora un vettore
  901. delle stesse dimensioni di v1:
  902.  
  903.         v1 = [ x , y , z ]
  904.         k  = numero reale
  905.  
  906.         v2 = k * v1 = [ k*x , k*y , k*z ]
  907.  
  908. Il fattore k viene chiamato 'scalare' (da non confondere con il prodotto
  909. scalare!), ed il vettore risultante v2 conservera' lo stesso verso di v1
  910. ma la sua dimensione verra' scalata rispetto al valore di k. Questa
  911. operazione e' commutativa.
  912.  
  913.  - PRODOTTO SCALARE: il risultato prodotto scalare tra due vettori e' uno
  914. scalare (quindi un numero reale o in virgola mobile):
  915.  
  916.         v1 = [ x1 , y1 , z1 ]
  917.         v2 = [ x2 , y2 , z2 ]
  918.  
  919.         k  = v1 * v2 = x1*x2 + y1*y2 + z1*z2
  920.  
  921.  Geometricamente il prodotto scalare puo' essere definito come il prodotto
  922. tra le distanze dei due vettori con l'origine ed il coseno dell'angolo
  923. compreso tra i due vettori. Anche questa operazione gode della proprieta'
  924. commutativa.
  925.  
  926.  - PRODOTTO IN CROCE: il risultato del prodotto in croce tra due vettori
  927. e' uno scalare (quindi un numero reale o in virgola mobile):
  928.  
  929.         v1 = [ x1 , y1 ]
  930.         v2 = [ x2 , y2 ]
  931.  
  932.         k  = v1 x v2 = x1*y2 - x2*y1
  933.  
  934.  Per semplicita' citiamo solo la formula del prodotto in croce tra due
  935. vettori 2d dato che utilizzeremo questa operazione solo su tali vettori.
  936. Il prodotto in croce non gode della proprieta' commutativa.
  937.  
  938.  
  939. *****  G.1.2      vETTORE uNITARIO
  940.  
  941.  Un vettore unitario e' un vettore cui lunghezza pari all'unita'. La
  942. lunghezza di un vettore e' data dalla distanza del punto di coordinate
  943. specificate dal vettore stesso con l'origine.
  944.  
  945.  La lunghezza di un generico vettore si ricava dal teorema di Pitagora
  946. svolgendo la radice quadrata della sommatoria dei quadrati delle
  947. componenti del vettore, piu' semplicemente:
  948.  
  949.         v2 = [ x2 , y2 ]
  950.         v3 = [ x3 , y3 , z3 ]
  951.  
  952.         l2 = |v2| = sqrt(x2*x2 + y2*y2)
  953.         l3 = |v3| = sqrt(x3*x3 + y3*y3 + z3*z3)
  954.  
  955.  Rendere un generico vettore v1 come vettore unitario significa trovare quel
  956. vettore che conserva lo stesso verso di v1 ma che ha lunghezza pari ad 1.
  957. Nella pratica si traduce nel dividere ogni componente del vettore per la
  958. relativa lunghezza; in relazione al precedente esempio possiamo affermare:
  959.  
  960.         u2 = [ x2/l2 , y2/l2 ]
  961.         u3 = [ x3/l3 , y3/l3 , z3/l3 ]
  962.  
  963. Che coincide col moltiplicare il vettore per uno scalare equivalente
  964. all'inverso della lunghezza del vettore:
  965.  
  966.         v = [ x , y , z ]
  967.         k = 1 / |v| = 1 / sqrt(x*x + y*y + z*z)
  968.  
  969.         u = k * v
  970.  
  971.  
  972. *****  G.1.3      vETTORE nORMALE
  973.  
  974.  Un vettore 3d normale su di un piano (quindi su uno spazio
  975. bidimensionale) significa che la retta passante per tale vettore e'
  976. perpendicolare a quel piano.
  977.  Un vettore normale su una retta r significa che la retta passante per
  978. tale vettore e' perpendicolare alla retta r.
  979.  Un vettore normale ad altro vettore implica che il primo vettore sia
  980. normale alla retta passante per il secondo vettore.
  981.  
  982.  Vediamo un esempio di vettore normale ad un altro vettore:
  983.  
  984.         v1.           .v2
  985.            \         /
  986.             \       /
  987.              \     /
  988.               \   /
  989.                \./
  990.                 \
  991.                  \
  992.                   \. v3
  993.                
  994. In questo caso v1 e v3 sono vettori normali rispetto a v2, mentre
  995. quest'ultimo e' vettore normale sia rispetto a v1 che a v3. Infatti i
  996. vettori v1 e v3 giaciono sulla stessa retta.
  997.  
  998.  
  999. *****  G.2        lE mATRICI
  1000.  
  1001.  Argomenti necessari alla comprensione: i vettori [G.1]
  1002.  
  1003.  
  1004. *****  G.2.0      dEFINIZIONE dI mATRICE
  1005.  
  1006.  Una matrice e' una tabella di elementi (che nel nostro caso sono numeri
  1007. razionali) del tipo:
  1008.                   _                            _
  1009.                  |                              |
  1010.                  |  a(1,1)  a(1,2)  ...  a(1,m) |
  1011.                  |  a(2,1)  a(2,2)  ...  a(2,m) |
  1012.         M(n,m) = |  ...                         |
  1013.                  |  ...                         |
  1014.                  |  ...                         |
  1015.                  |  a(n,1)  a(n,2)  ...  a(n,m) |
  1016.                  |_                            _|
  1017.  
  1018. dove a(i,j) e' il generico elemento della matrice mentre n*m rappresenta
  1019. la dimensione della matrice, in cui valgono le seguenti regole:
  1020.  
  1021.         n, m > 0
  1022.         0 < i <= n
  1023.         0 < j <= m
  1024.  
  1025. n, m, i, j sono quindi numeri interi maggiori di zero. Il valore "n" e'
  1026. il numero di righe della matrice, mentre "m" e' il numero di colonne.
  1027. I valori "i" e "j" rappresentano rispettivamente gli indici di riga e
  1028. di colonna di un elemento della matrice.
  1029.  
  1030.  Nel nostro caso intendiamo ogni elemento a(i,j) come un numero reale o
  1031. un numero in virgola mobile.
  1032.  
  1033.  Per semplicita' si fara' spesso riferimento ad una matrice denotando
  1034. solo la lettera che la etichetta. Ovvero spesso si utilizzera' spesso la
  1035. notazione "M" al posto di "M(n,m)".
  1036.  
  1037.  Casi particolari di matrici:
  1038.  
  1039.  - MATRICE QUADRATA:  M(n,n)
  1040. il numero di righe e di colonne sono uguali. Esempio:
  1041.                   _       _
  1042.                  |         |
  1043.                  | 1  2  3 |
  1044.         M(n,n) = | 4  5  6 |
  1045.                  | 7  8  9 |
  1046.                  |_       _|
  1047.  
  1048.  La DIAGONALE principale della matrice quadrata e' definita come
  1049. quell'insieme di elementi della matrice stessa tali che gli indici di riga
  1050. e colonna siano uguali, quindi:
  1051.  
  1052.         D = tutti gli a(i,i) per 0 < i <= n
  1053.  
  1054. ovvero:
  1055.  
  1056.         D = { a(1,1) , a(2,2), ... , a(n,n) }
  1057.  
  1058. nel precedente esempio la diagonale principale e' uguale a:
  1059.  
  1060.         D = { 1 , 5 , 9 }
  1061.  
  1062.  - MATRICE TRASPOSTA:  Mt(n,n)
  1063. data una matrice quadrata M(n,n), la sua trasposta Mt(n,n) e' quella
  1064. matrice che contiene gli stessi elementi della M, ma con diverso ordine.
  1065. Tale ordine e' determinato dagli indici di riga e colonna, i quali vengono
  1066. scambiati. Piu' formalmente il generico elemento a(i,j) appartenente a M
  1067. coincide con l'elemento t(i,j) di Mt tale che:
  1068.  
  1069.         t(i,j) = a(j,i)
  1070.         0 < i <= n
  1071.         0 < j <= n
  1072.  
  1073.  Per chiarire le idee vediamo un esempio pratico relativo ad una matrice
  1074. di dimensione 3*3:
  1075.              _       _                                _       _
  1076.             |         |                              |         |
  1077.             | 1  2  3 |                              | 1  4  7 |
  1078.         M = | 4  5  6 |   la sua trasposta e'   Mt = | 2  5  8 |
  1079.             | 7  8  9 |                              | 3  6  9 |
  1080.             |_       _|                              |_       _|
  1081.  
  1082. Da notare che gli elementi posti sulla diagonale principale rimangono
  1083. nella posizione originaria.
  1084.  
  1085.  - MATRICE RIGA:  M(1,m)
  1086. una matrice riga e' coincide, a livello logico, con la definizione
  1087. di vettore [G.1], quindi tale matrice e' intesa come una matrice con una
  1088. sola riga, quindi nella forma:
  1089.  
  1090.         M(1,m) = [ a(1,1)  a(1,2)  ...  a(1,m) ]
  1091.  
  1092. o piu' semplicemente:
  1093.  
  1094.         M(m) = [ a(1)  a(2)  ...  a(m) ]
  1095.  
  1096. Le ultime due definizioni di matrice per righe sono logicamente
  1097. equivalenti, la differenza sostanziale sta nel secondo caso, in cui
  1098. non si fa specificatamente riferimento alla notazione matriciale, la quale
  1099. implica che si faccia riferimento ad entrambi gli indici di riga e
  1100. colonna (infatti abbiamo il solo indice di colonna).
  1101.  
  1102.  Etichettando gli elementi di una matrice per riga senza il relativo
  1103. indice si ottiene un oggetto matematico equivalente alla matrice per riga
  1104. che viene denominato ENNUPLA. Vediamone un esempio:
  1105.  
  1106.         E = ( a , b , c , d )
  1107.  
  1108.  - MATRICE COLONNA:  M(n,1)
  1109. e' un modo alternativo (rispetto alla matrice riga) di rappresentare un
  1110. vettore secondo la notazione matriciale, ovvero:
  1111.                   _       _
  1112.                  |         |
  1113.                  |  a(1,1) |
  1114.                  |  a(2,1) |
  1115.         M(n,1) = |  ...    |
  1116.                  |  ...    |
  1117.                  |  ...    |
  1118.                  |  a(n,1) |
  1119.                  |_       _|
  1120.  
  1121. o piu' semplicemente:
  1122.  
  1123.                 _     _
  1124.                |       |
  1125.                |  a(1) |
  1126.                |  a(2) |
  1127.         M(n) = |  ...  |
  1128.                |  ...  |
  1129.                |  ...  |
  1130.                |  a(n) |
  1131.                |_     _|
  1132.  
  1133.  
  1134.  - MATRICE NULLA:  M(n,m)
  1135. e' quella matrice in cui tutti gli elementi sono uguali a 0, ovvero:
  1136.  
  1137.         a(i,j) = 0
  1138.         0 < i <= n
  1139.         0 < j <= m
  1140.  
  1141.  - MATRICE IDENTITA':  I(n)
  1142. e' un particolare caso di matrice quadrata in cui tutti gli elementi sono
  1143. uguali a 0 fatta esclusione per quegli elementi presenti sulla diagonale
  1144. principale che sono uguali a 1. O in termini piu' formali:
  1145.  
  1146.         0 < i <= n
  1147.         0 < j <= n
  1148.         a(i,j) = 0   per i diverso da j
  1149.         a(i,j) = 1   per i uguale a j
  1150.  
  1151. Faremo riferimento alla generica matrice identita' n*n con la notazione
  1152. I(n) (basta specificare un solo indice in quanto la matrice identita' e'
  1153. sempre quadrata, quindi l'indice di colonna e' sottointeso). Vediamo un
  1154. esempio relativo alla matrice identita' 4*4:
  1155.                 _          _
  1156.                |            |
  1157.                | 1  0  0  0 |
  1158.         I(4) = | 0  1  0  0 |
  1159.                | 0  0  1  0 |
  1160.                | 0  0  0  1 |
  1161.                |_          _|
  1162.  
  1163.  
  1164. *****  G.2.1      oPERAZIONI tRA mATRICI
  1165.  
  1166.  In relazione all'obiettivo di realizzare un motore 3d possiamo anticipare
  1167. che principalmente ci occuperemo di applicare operazioni tra matrici 3*3,
  1168. 4*3 e 4*4.
  1169.  
  1170.  - SOMMA: la somma tra due matrici A e B e' applicabile se e solo se A e B
  1171. hanno lo stesso numero di righe e colonne. Il risultato e' ancora una
  1172. matrice, che per convenzione chiamiamo C, delle stesse dimensione degli
  1173. operandi. Ogni elemento di C e' la somma del corrispondente elemento di A
  1174. e di B (che quindi si trovano nella stessa posizione dell'elemento di C):
  1175.  
  1176.         A(n,m) = matrice cui generico elemento e' a(i,j)
  1177.         B(n,m) = matrice cui generico elemento e' b(i,j)
  1178.         C(n,m) = matrice cui generico elemento e' c(i,j)
  1179.  
  1180.         c(i,j) = a(i,j) + b(i,j)
  1181.         0 < i <= n
  1182.         0 < j <= m
  1183.                      _                                                _
  1184.                     |                                                  |
  1185.                     | a(1,1)+b(1,1)  a(1,2)+b(1,2)  ...  a(1,m)+b(1,m) |
  1186.                     | a(2,1)+b(2,1)  a(2,2)+b(2,2)  ...  a(2,m)+b(2,m) |
  1187.         C = A + B = | ...                                              |
  1188.                     | ...                                              |
  1189.                     | ...                                              |
  1190.                     | a(n,1)+b(n,1)  a(n,2)+b(n,2)  ...  a(n,m)+b(n,m) |
  1191.                     |_                                                _|
  1192.  
  1193. Questa operazione gode delle seguenti proprieta':
  1194.  
  1195.         commutativa  =>  A + B = B + A
  1196.         associativa  =>  (A + B) + C = A + (B + C)
  1197.  
  1198.  - NEGAZIONE: la negazione di una matrice A(n,m) e' una matrice di
  1199. dimensione ancora n*m in cui ogni elemento di A e' di segno opposto:
  1200.  
  1201.         A(n,m) = matrice cui elementi sono a(i,j)
  1202.         B(n,m) = matrice cui elementi sono b(i,j)
  1203.  
  1204.         B = - A
  1205.  
  1206.         b(i,j) = - a(i,j)
  1207.         0 < i <= n
  1208.         0 < j <= m
  1209.  
  1210.  - PRODOTTO PER UNO SCALARE: svolgere il prodotto di una matrice A per uno
  1211. scalare k significa moltiplicare ogni elemento di A con k, il quale non e'
  1212. altro che un numero reale. La matrice cosi' ottenuta e' ovviamente delle
  1213. stesse dimensioni della matrice originaria:
  1214.  
  1215.         k      = numero reale
  1216.         A(n,m) = matrice cui elementi sono a(i,j)
  1217.         B(n,m) = matrice cui elementi sono b(i,j)
  1218.  
  1219.         B = k * A
  1220.  
  1221.         b(i,j) = k * a(i,j)
  1222.         0 < i <= n
  1223.         0 < j <= m
  1224.  
  1225.  - PRODOTTO TRA MATRICI: il prodotto tra due matrici A e B e' applicabile
  1226. se il numero di colonne di A e' uguale al numero di righe di B. Il
  1227. risultato sara' una matrice C con un numero di righe equivalente a quello
  1228. di A ed un numero di colonne uguale al corrispondente di B. Ogni elemento
  1229. c(i,j) di C coincide con il prodotto scalare dei due vettori intesi come
  1230. la i-esima riga di A e la j-esima colonna di B:
  1231.  
  1232.         A(n,t) = matrice cui generico elemento e' a(i,k)
  1233.         B(t,m) = matrice cui generico elemento e' b(k,j)
  1234.         C(n,m) = matrice cui generico elemento e' c(i,j)
  1235.  
  1236.         C = A * B
  1237.  
  1238.         c(i,j) = a(i,1)*b(1,j) + a(i,2)*b(2,j) + ... + a(i,t)*b(t,j)
  1239.         0 < i <= n
  1240.         0 < j <= m
  1241.  
  1242. che equivale a:
  1243.  
  1244.         c(i,j) = a(i,k)*b(k,j)
  1245.         0 < k <= t
  1246.         0 < i <= n
  1247.         0 < j <= m
  1248.  
  1249. Questa operazione gode della proprieta' associativa, ovvero:
  1250.  
  1251.         (A * B) * C = A * (B * C)
  1252.  
  1253. che, come vedremo in seguito, risultera' particolarmente utile per la
  1254. progettazione del geometry engine. Inoltre il prodotto NON gode della
  1255. proprieta' commutativa. Da notare che l'elemento neutro di questa
  1256. operazione e' esattamente la matrice identita':
  1257.  
  1258.         A(n,m) = I(n) * A(n,m) = A(n,m) * I(m)
  1259.  
  1260. Inoltre il prodotto per la matrice identita' e' l'unico caso in cui
  1261. vale la proprieta' commutativa.
  1262.  
  1263.  
  1264. ==========================================================================
  1265. =========================== nOTE fINALI ==================================
  1266. ==========================================================================
  1267.  
  1268.  
  1269.  Spesso vagheggiano nella mia mente pensieri del tipo "Ne vale davvero la
  1270. pena consumare tanto tempo per realizzare un motore 3d?", oppure "Ora c'e'
  1271. da fare altro di piu' prioritario che starsene qui a scrivere questo
  1272. engine...", dubbi che affliggono molte di quelle persone che si propongono
  1273. di programmare un motore 3d, in particolare chi e' alle prime armi oppure
  1274. chi ne ha avuto a che fare per molto (forse troppo) tempo.
  1275.  
  1276.  Tristo e' quel discepolo che non avanza il suo maestro. Leonardo.
  1277.  
  1278.  Se qualcuno e' interessato a ricevere gratuitamente l'ultima versione
  1279. di questo file di testo o per comunicarmi eventuali errori puo'
  1280. contattarmi 
  1281.  
  1282.     per e-mail  :  acemann@tin.it
  1283.  
  1284.     su irc      :  #demo-ita
  1285.                    #coders
  1286.  
  1287.     scrivendo a :  Cristiano Tagliamonte
  1288.                    Via Maddaloni, 12
  1289.                    Interno B21
  1290.                    00177 Roma
  1291.                    Tel. 06-272815          
  1292.  
  1293.                    Cristiano Tagliamonte
  1294.                    Via Filippo Masci, 86/G
  1295.                    66100 Chieti
  1296.                    Tel. 0871-347826
  1297.  
  1298.  
  1299.        _____ _____ _____ __ __ _____ __ __
  1300.       :  _  Y  _  Y  _  Y  "  Y  _  Y  \  :
  1301. .-----|  _  |  l__j  ___|  Y  |  _  |  _  |---------------------------.
  1302. |:::::|  |  |     |     |  |  |  |  |  |  | ::::: aCEmAN/rAmJaM ::::::|
  1303. |.....|  |__j.____j_____j  l__j  |__j  |__j ::::: acemann@tin.it :::::|
  1304. `-----`--' -------------`--' -`--' -`--' -----------------------------'
  1305.  
  1306.        ______    ______    ____  _____
  1307.       /\_____\  /\_____\  /\___\/\____\    ___________  ____
  1308.      / /  __  \/ /  __  \/ /    \/    /\  /  __  \    \/    \
  1309.     / /  /_/\  \/  /_/\  \/          /  \/  /_/\  \          \
  1310.    / /  /__\/  /  /__\/  /          /\   \  \_\_\  \          \
  1311.   / /     ____/         /     _    /\ \   \         \    _     \
  1312.  / /  __  \/ /  ____   /   /\//   /__\_\   \   ____  \   \\/\   \
  1313. / /  /  \  \/  / / /  /   // /   //\        \  \__/\  \   \\ \   \
  1314. \/__/    \_/__/  \/__/___/ \/___/ \ \________\__\ \ \__\___\\ \___\
  1315.                                    \/________/__/  \/__/___/ \/___/
  1316.  
  1317.  
  1318.  
  1319.  
  1320.  
  1321.                                 rOTAZIONE
  1322. ==========================================================================
  1323.  
  1324.  Adesso vediamo realmente come effettuare rotazioni. Utilizzando come
  1325. sistema di riferimento un piano a 2 dimensioni e' possibile convertire le
  1326. coordinate cartesiane (x,y) di un punto in coordinate polari (r,t):
  1327.  
  1328.        _ V           V(x,y)=V'(r,t)             _ V'     r=distanza
  1329.   y|   /|                                   |   /|         punto-origine
  1330.    |  /              r=sqrt(x*x+y*y)        | r/
  1331.    | /               t=arctan(y/x)          | /          t=angolo compreso
  1332.    |/                                       |/) t          tra il vettore
  1333.    +------->         x=r*cos(t)             +------->      ed il semiasse
  1334.     x            y=r*sin(t)                            positivo x
  1335.  
  1336.  Dopodiche' per ruotare il punto intorno l'origine si dovra' sommare
  1337. l'angolo di cui si vuol ruotare il punto alla variabile t e convertire le
  1338. risultanti coordinate polari in cartesiane. Questo metodo risulta troppo
  1339. lento per applicazioni in real time in quanto sono presenti quadrati,
  1340. arcotangenti e radici quadrate; inoltre introducendo la variabile z si
  1341. andrebbe incontro ad una pesante gestione delle coordinate polari
  1342. (utilizzo di integrali tripli, ecc.): meglio trovare una soluzione piu'
  1343. semplice e veloce.
  1344.  Immaginiamo di avere un vettore con ordinata nulla V(x,0) e vogliamo
  1345. ruotarlo di un angolo a:
  1346.  
  1347.   y               vettore               y     _ Vr(xr,yr)   vettore
  1348.     |             coincidente             |   /|            ruotato
  1349.     |             l'ascissa               |  /              di a
  1350.     |                                     | /               radianti
  1351.     |    V(x,0)                           |/) a
  1352.     +----->->                             +------->
  1353.         x                                     x
  1354.  
  1355.  Se volessimo trasformare in polari le coordinate di V risulterebbe r=x
  1356. (x=sqr(x*x+0*0)) e t=0 (0=arctan(0/x). Per ruotare il vettore basta
  1357. sommare a t l'angolo di rotazione a. Quindi:
  1358.  
  1359. (V   = generico vettore in coordinate cartesiane)
  1360. (V'  = generico vettore in coordinate polari)
  1361. (Vr  = vettore ruotato di a radianti in coordinate cartesiane)
  1362. (Vr' = vettore ruotato di a radianti in coordinate polari)
  1363. (Vxr = vettore ruotato con la sola componente x in coordinate cartesiane)
  1364. (Vyr = vettore ruotato con la sola componente y in coordinate cartesiane)
  1365.  
  1366.     V(x,y) = V'(r,t)
  1367.     Vr(xr,yr) = Vr'(r,t+a)    ->    xr=r*cos(t+a)  yr=r*sin(t+a)
  1368. Andiamo a sostituire xr e yr con le relative formule:
  1369.     Vr(r*cos(t+a),r*sin(t+a)) ->    r=x  t=0
  1370. Andiamo a sostituire r con x e t con 0:
  1371.     Vxr(x*cos(a),x*sin(a))
  1372.  
  1373. E questa e' la formula per la rotazione di un vettore che non ha
  1374. la componente y. Ora cosideriamo un vettore che ha l'ascissa nulla V(0,y):
  1375.  
  1376.     r=y    (=sqr(0*0+y*y))  
  1377.     t=pi/2 (=arctan(y/0)=arctan(infinito))
  1378.     Vyr(y*cos(pi/2+a),y*sin(pi/2+a))
  1379.  
  1380. Un paio di formule trigonometriche ci dicono:
  1381.  
  1382.     cos(pi/2+a)=-sin(a)
  1383.     sin(pi/2+a)=cos(pi/2)
  1384.  
  1385. ma siccome utilizziamo un sistema di riferimento con l'asse y "rovesciato"
  1386. dobbiamo cambiare un segno ad entrambe le formule per poterle sfruttare,
  1387. che quindi diventano:
  1388.  
  1389.     cos(pi/2+a)=sin(a)
  1390.     sin(pi/2+a)=-cos(a)
  1391.  
  1392. Adesso la formula per ruotare un vettore senza componente x e' uguale:
  1393.  
  1394.     Vyr(y*sin(a),-y*cos(a))
  1395.  
  1396. Ma se guardiamo al caso generale, abbiamo un vettore V che ha entrambe le
  1397. componenti x e y. Difatti un generico vettore con con le componenti x e y
  1398. e' uguale a:
  1399.  
  1400.     V1(x,0)+V2(0,y)=V(x+0,0+y)=V(x,y)
  1401.  
  1402.  Ora possiamo usare le formule di rotazione dei singoli casi per calcolare
  1403. il caso generale con un'addizione tra vettori:
  1404.  
  1405.     Vxr(x*cos(a)         ,x*sin(a)         ) +
  1406.     Vyr(        +y*sin(a),        -y*cos(a)) =
  1407.     ------------------------------------------
  1408.     Vr (x*cos(a)+y*sin(a),x*sin(a)-y*cos(a))
  1409.  
  1410. Grazie a questa formula e' possibile ruotare un qualsiasi vettore su uno
  1411. spazio bidimensionale. In un ambiente 3D la formula appena descritta
  1412. coincide con la rotazione intorno l'asse z (non c'e' nessuna coordinata z
  1413. cambiata). Per ruotare il punto intorno un altro asse basta lasciar fuori
  1414. la relativa variabile e utilizzare le altre nella precedente espressione,
  1415. il che si puo' sintetizzare con:
  1416.  
  1417.     intorno l'asse z         intorno l'asse y         intorno l'asse x
  1418.   --------------------     --------------------     --------------------
  1419.   xr=x*cos(a)+y*sin(a)     xr=x*cos(a)+z*sin(a)     yr=y*cos(a)+z*sin(a)
  1420.   yr=x*sin(a)-y*cos(a)     zr=x*sin(a)-z*cos(a)     zr=y*sin(a)-z*cos(a)
  1421.  
  1422.  
  1423.                         oTTIMIZZAZIONE dELLE rOTAZIONI
  1424. ==========================================================================
  1425.  
  1426.  Dato un angolo di rotazione per ogni asse (x,y e z) con le precedenti
  1427. formule sarebbero occorse ben 12 moltiplicazioni per poter ruotare un solo
  1428. punto. Qui vedremo come effettuare rotazioni eseguendo 9 moltiplicazioni
  1429. per ogni punto. Consideriamo:
  1430.  
  1431.   ax = angolo di rotazione intorno l'asse x     s1 = sin(ax)  c1 = cos(ax)
  1432.   ay = angolo di rotazione intorno l'asse y     s2 = sin(ay)  c2 = cos(ay)
  1433.   az = angolo di rotazione intorno l'asse z     s3 = sin(az)  c3 = cos(az)
  1434.  
  1435.  Ognuna delle variabili x,y e z influenza le rotazioni intorno a due assi
  1436. (nella rotazione intorno al proprio asse la variabile rimane inalterata),
  1437. possiamo cosi' indicare con x' y' e z' le variabili parzialmente ruotate
  1438. (cioe' dopo la prima rotazione) e con x'' y'' e z'' le variabili
  1439. completamente ruotate. Detto cio' le formule viste in precedenza
  1440. corrispondono a:
  1441.  
  1442.    x' = x*c1 + y*s1
  1443.    y' = x*s1 - y*c1
  1444.  
  1445.    x''= x'*c2 + z*s2   <-  coordinata x ruotata completamente
  1446.    z' = x'*s2 - z*c2
  1447.  
  1448.    y''= y'*c3 + z'*s3  <-  coordinata y ruotata completamente
  1449.    z''= y'*s3 - z'*c3  <-  coordinata z ruotata completamente
  1450.  
  1451. che equivale a scrivere:
  1452.  
  1453.    x''= (x*c1+y*s1)*c2+z*s2=c2*c1 *x + c2*s1 *y + s2 *z
  1454.  
  1455.    y''= (x*s1-y*c1)*c3+((x*c1+y*s1)*s2-z*c2)*s3=
  1456.    c3*s1 *x - c3*c1 *y + s3*s2*c1 *x + s3*s2*s1 *y - s3*c2 *z=
  1457.    (s3*s2*c1+c3*s1) *x + (s3*s2*s1-c3*c1) *y + (-s3*c2) *z
  1458.  
  1459.    z''= (x*s1-y*c1)*s3-((x*c1+y*s1)*s2-z*c2)*c3=
  1460.    s3*s1 *x - s3*c1 *y - c3*s2*c1 *x - c3*s2*s1 *y + c3*c2 *z=
  1461.    (-c3*s2*c1+s3*s1) *x + (-c3*s2*s1-c3*c1) *y + (c3*c2) *z
  1462.  
  1463.    z''= (x*s1-y*c1)*s3-((x*c1+y*s1)*s2-z*c2)*c3=
  1464.         s3*s1 *x - s3*c1 *y - c3*s2*c1 *x - c3*s2*s1 *y + c3*c2 *z=
  1465.  
  1466.         (-c3*s2*c1+s3*s1) *x + (-c3*s2*s1-s3*c1) *y + (c3*c2) *z
  1467.         ^^^^^^^^^^^^^^^^^ zx   ^^^^^^^^^^^^^^^^^ zy   ^^^^^^^ zz
  1468.  
  1469.  
  1470.  Dall'ultimo passaggio di ognuna di queste formule si puo' notare che non
  1471. vengono calcolate coordinate parzialmente ruotate e che ogni coordinata
  1472. ruotata equivale alla somma delle variabili (non ruotate) moltiplicate
  1473. per un determinato fattore. Se precalcoliamo questi fattori potremo
  1474. utilizzarli per tutti quei punti che devono essere ruotati nella stessa
  1475. direzione. In questo modo avremo svolto semplicemente 9 moltiplicazioni
  1476. per ogni punto (esclusi i precalcoli per i fattori).
  1477. In sostanza dobbiamo calcolare prima queste costanti:
  1478.  
  1479.    xx =  c2*c1
  1480.    xy =  c2*s1
  1481.    xz =  s2
  1482.    yx =  c3*s1+s3*s2*c1
  1483.    yy = -c3*c1+s3*s2*s1
  1484.    yz = -s3*c2
  1485.    zx =  s3*s1-c3*s2*c1 = s2*c1+c3*s1
  1486.    zy = -s3*c1-s3*s2*s1 = c3*c1-s2*s1
  1487.    zz =  c3*c2
  1488.  
  1489. poi per ogni punto si devono svolgere questi calcoli (sfruttando gli
  1490. stessi fattori):
  1491.  
  1492.    x'' = xx * x + xy * y + xz * z
  1493.    y'' = yx * x + yy * y + yz * z
  1494.    z'' = zx * x + zy * y + zz * z
  1495.  
  1496. Ed otterremo le tre coordinate ruotate.
  1497.  
  1498.  Questo algoritmo risulterebbe meno efficiente del precedente se si
  1499. utilizzassero pochi punti, mentre nel caso di una elevata quantita' di
  1500. vettori e' possibile raggiungere notevoli risparmi in termini di calcolo.
  1501.  
  1502.  
  1503.                                 wIREFRAME
  1504. ==========================================================================
  1505.  
  1506.  Il wireframe e' la piu' semplice ed antica tecnica atta a riprodurre
  1507. poligoni. Consiste semplicemente nel tracciare linee che uniscano i
  1508. vertici del poligono da rappresentare, nient'altro. Il tracciamento delle
  1509. linee dovra' svolgersi sfruttando le coordinate proiettate dei punti (e
  1510. non quelle con tre variabili xyz).
  1511.  Vediamo quindi come tracciare linee nel caso si stia programmando in un
  1512. linguaggio che non permetta direttamente di svolgere questa funzione
  1513. (come il C e l'Assembly). Analizziamo l'algoritmo di Bresenham. Abbiamo
  1514. due punti P1(x1,y1) e P2(x2,y2) e vogliamo visualizzarne la linea che
  1515. possa unirli:
  1516.  
  1517.     P1(x1,y1)
  1518.         .------______                                 ^
  1519.                      ------______         P2(x2,y2)   | dy
  1520.                                  ------______.        v
  1521.                           dx
  1522.         <------------------------------------>
  1523.  
  1524. consideriamo:
  1525.                 x2 > x1
  1526.                 y2 > y1
  1527.                 dx = x2-x1
  1528.                 dy = y2-y1
  1529.                 dx > dy
  1530.  
  1531. Tutti gli altri tipi di linee sono derivarivabili da questo tipo.
  1532. Dopodiche' andiamo a calcolare questi valori:
  1533.  
  1534.                 xl = x1            -> ascissa attuale del punto
  1535.                 yl = y1            -> ordinata attuale del punto
  1536.                  d = 2*dx-dy       -> variabile di decisione
  1537.                 d1 = 2*dy          -> incremento di d (se d<0)
  1538.                 d2 = 2*(dy-dx)     -> incremento di d (se d=>0)
  1539.  
  1540. Finalmente vediamo l'algoritmo vero e proprio:
  1541.  
  1542.         > loop di dx iterazioni
  1543.            > visualizza pixel alla posizione (xl,yl)
  1544.               > xl=xl+1
  1545.               > se d<0 allora:
  1546.               > d=d+d1
  1547.            > altrimenti:
  1548.               > d=d+d2
  1549.               > yl=yl+1
  1550.         > prossima iterazione
  1551.  
  1552. Una linea e' formata da un insieme di pixel, nel nostro caso il numero di
  1553. pixel che compone la linea equivale a dx, quindi dobbiamo realizzare un
  1554. loop che si ripete dx volte in cui per ogni iterazione bisogna
  1555. visualizzare un punto. Ma quali coordinate dovra' avere questo punto?
  1556. Indichiamo con xl e yl le coordinate del punto da proiettare su video,
  1557. le quali inizialmente coincideranno con quelle di P1(x1,y1). Al termine di
  1558. ogni iterazione andiamo ad incrementare xa, in questo modo uscendo dal
  1559. ciclo xa coincidera' con x2 (perche' x2=x1+dx). Cosa succede invece alla
  1560. ordinata del pixel? La incrementiamo semplicemente quando la variabile d
  1561. e' positiva.
  1562.  E' possibile utilizzare anche un altro algoritmo per tracciare linee, il
  1563. quale risulta spesso piu' efficiente di quello di Bresenham (soprattutto
  1564. se realizzato in Assembly) che sfrutta il principio di interpolazione
  1565. lineare, lo vedremo piu' dettagliatamente nel paragrafo relativo al fill
  1566. e scan line.
  1567.  
  1568.  
  1569.                                 hIDDEN fACE
  1570. ==========================================================================
  1571.  
  1572.  Hidden face significa faccia nascosta, in questo paragrafo vedremo come
  1573. eliminarla. Difatti nella realta' in un solido non trasparente non sono
  1574. visibili tutte le facce per ovvi motivi. Visualizzare sul monitor solo le
  1575. facce visibili e' sicuramente piu' realistico rispetto a tracciarle tutte.
  1576.  
  1577.  Il piu' semplice e intuitivo algoritmo e' quello del "pittore". Consiste
  1578. nell'ordinare le facce che compongono l'oggetto in base alla componente z.
  1579. In seguito si devono disegnare le facce partendo da quella piu' lontana
  1580. sino a quella piu' vicina; in questo modo le facce disegnate per ultime
  1581. saranno visibili, mentre sulle nascoste verranno disegnate quelle
  1582. visibili. A suo discapito questo algoritmo include un pesante spreco di
  1583. tempo macchina, inoltre e' praticamente inutilizzabile per grafica in
  1584. wireframe, pero' permette a qualsiasi oggetto (che non sia wireframe) di
  1585. essere visualizzato correttamente.
  1586.  
  1587.  Un'altra via consiste nel calcolare la normale (la retta perpendicolare)
  1588. su ogni faccia, controllare se punta verso l'osservatore ed in caso
  1589. contrario non visualizzarla. Questo algoritmo e' valido solo se i vertici
  1590. che delimitano la faccia sono posti in memoria in senso orario, in quanto
  1591. i calcoli che vedremo sfruttano questa caratteristica. Inoltre gli oggetti
  1592. dovranno necessariamente essere convessi, ossia non devono esserci facce
  1593. che possano "oscurare" (non nascondere!) altre facce.
  1594.  La retta normale la possiamo considerare una grandezza vettoriale che
  1595. nello spazio viene indicata come un comune vettore con tre componenti.
  1596.  
  1597.  La visibilita' di un poligono dipende esclusivamente dalla sua
  1598. orientazione lungo l'asse z. Essendo piu' precisi possiamo dire che la
  1599. sola componente z e' necessaria per sapere se la faccia e' nascosta o meno.
  1600. Consideriamo la nostra faccia come la seguente:
  1601.  
  1602.             per individuare un piano bastano tre punti. Di
  1603.         A(x1,y1)        conseguenza, per ricavare la normale sul piano 
  1604.          /\             coincidente la nostra faccia, saranno sufficienti
  1605.         /  \            le coordinate dei primi tre vertici della faccia.
  1606.      D /    \ B(x2,y2)  Lasciando da parte eventuali dimostrazioni, e'
  1607.        \    /           possibile affermare che la componente z della
  1608.         \  /            normale e' equivalente a:
  1609.          \/
  1610.         C(x3,y3)          (x2-x1)*(y3-y1)-(x3-x1)*(y2-y1)
  1611.  
  1612.  Se il risultato e' minore o uguale a zero la faccia e' nascosta,
  1613. altrimenti e' visibile. Per sapere semplicemente se questa componente z e'
  1614. maggiore o minore di zero potremmo anche utilizzare le coordinate
  1615. proiettate, ovvero:
  1616.  
  1617.                           (xp2-xp1)*(yp3-yp1)-(xp3-xp1)*(yp2-yp1)
  1618.  
  1619. Questo e' quanto basta per sapere se un poligono e' o meno visibile.
  1620.  
  1621.  
  1622.                         aLGORITMO dEL pITTORE
  1623. ==========================================================================
  1624.  
  1625.  L'algoritmo del pittore consiste semplicemente nel visualizzare un 
  1626. oggetto (o una scena) 3D tracciando i poligoni partendo da quello piu'
  1627. distante verso quello piu' vicino al punto di vista. Il suo scopo e'
  1628. di risolvere il problema causato dal tracciamento delle facce
  1629. parzialmente oscurate da altre (ovvero visualizzare oggetti concavi).
  1630.  
  1631.  Questo semplice principio e' lo stesso che un qualsiasi pittore
  1632. utilizza quando deve dipingere un quadro. Difatti si inizia sempre col
  1633. disegnare (ad esempio) i monti (che si trovano piu' distanti dal punto
  1634. di vista) sino a disegnare gli elementi piu' vicini al pittore, quali
  1635. possono essere un ruscello o una persona.
  1636.  
  1637.  Per realizzare l'algoritmo del pittore  e' sufficiente utilizzare un 
  1638. array monodimensionale a cui ogni coppia di posizioni corrispondano
  1639. il puntatore o l'indice di ogni faccia e la componente z di quella faccia.
  1640. Consideriamo di poter numerare le facce del nostro oggetto, quindi di
  1641. poter indicare una singola faccia con un determinato numero (si consulti
  1642. l'Appendice C per ulteriori consigli). Il numero di elementi necessari
  1643. al questo buffer sono equivalenti a:
  1644.  
  1645.            Dimensione Buffer = 1 + (numero facce oggetto)*2
  1646.  
  1647. Nella prima posizione viene salvato il numero di facce da tracciare. 
  1648. Invece nelle successive posizioni vengono salvati (per ogni poligono) il 
  1649. numero identificatore della faccia (che puo' essere un indice o un 
  1650. puntatore e la componente z di quella faccia di cui abbiamo appena 
  1651. parlato. La coordinata z relativa alla faccia e' equivalente alla media 
  1652. aritmetica delle componenti z dei vertici di quel poligono.
  1653.  
  1654.  Successivamente dovremo ordinare in ordine crescente gli elementi di tale
  1655. struttura in base alla coordinata z. Una volta ordinato l'array
  1656. traccieremo i poligoni a video nella successione definita dagli
  1657. identificatori delle facce presenti nel nostro array gia' ordinato 
  1658. (partendo dallo specificatore con la z maggiore sino ad arrivare allo
  1659. specificatore della faccia con la z minore); cosi' facendo saremo sicuri 
  1660. di visualizzare le facce dalle piu' lontane alle piu' vicine; cio' ci 
  1661. permette di rappresentare correttamente sullo schermo anche oggetti 
  1662. concavi.
  1663.  
  1664.  Per snellire questo buffer possiamo calcolare la componente z della
  1665. normale di ogni faccia (Nz) e, solamente se Nz risultera' positiva,
  1666. salveremo l'identificatore e la z di quella faccia; in caso contrario
  1667. (se Nz<0) passiamo direttamente alla prossima faccia. In altre parole
  1668. andiamo a considerare solo le facce orientate verso l'osservatore
  1669. (consultare il paragrafo relativo all'hidden face per ulteriori
  1670. approfondimenti).
  1671.  
  1672.  Se applicato nella maniera dovuta (ovvero utilizzando un algoritmo di
  1673. ordinamento ottimizzato), l'algoritmo del pittore puo' considerarsi uno
  1674. dei metodi piu' veloci nella rimozione delle facce nascoste durante il
  1675. tracciamento di un oggetto o di una scena 3D. I suoi principali svantaggi
  1676. risiedono nella difficolta' di implementazione nel caso di intersezioni
  1677. tra poligoni; nell'imprecisione visiva in certi mondi 3D complessi e
  1678. nell'impossibilita' di tracciare correttamente i poligoni in alcuni casi
  1679. (si immagini di voler visualizzare una scena in cui vi siano un enorme
  1680. poligono che rappresenti un pavimento su cui giace un piccolo cubo.
  1681. Poniamo caso caso che la componente z del centro di tale cubo corrisponda
  1682. con quella del poligono coincidente il pavimento. Dopo aver tracciato i
  1683. poligoni su schermo puo' accadere che alcune facce del cubo vengano
  1684. disegnate prima del pavimento, quindi alcuni poligoni, che dovrebbero
  1685. essere visibili, non verrano visualizzati su schermo).
  1686.  
  1687.             
  1688.                         fILLED vECTOR e sCAN lINE
  1689. ==========================================================================
  1690.  
  1691.  I filled vector non rappresentano altro che poligoni "riempiti" di un
  1692. determinato colore. Realizzare una routine di fill significa colorare il
  1693. contenuto di un poligono conoscendone le coordinate proiettate dei
  1694. vertici. Immaginiamo che il poligono da riempire sia il seguente:
  1695.  
  1696. |        A|\            potremmo procedere nella seguente maniera:
  1697. |         | \           a partire dalla coordinata y minore del poligono
  1698. |         |  \          (in questo caso quella di A) e via via arrivando
  1699. |         |   \         all'ultima posizione verticale (ossia y di D)
  1700. |         |    \        coloriamo la riga delimitata dalle posizioni x
  1701. |         |     \ B     dei lati in quella posizione y.
  1702. |        D \     |
  1703. |           \    |      Il principio del fill si basa sul riempimento di
  1704. |            \   |      righe orizzontali partendo dalla riga superiore
  1705. |             \  |      del poligono sino a quella inferiore.
  1706. |y             \ |
  1707. v               \|C     Vediamo un esempio relativo ad una sola riga:
  1708.  
  1709. |        A|\
  1710. |         | \           dobbiamo riempire la riga indicata nella figura.
  1711. |         |  \          Partiamo dalla coordinata x del lato AD.
  1712. |  riga   |   \         Iniziamo col colorare questo punto.
  1713. |   da -->|****\        Passiamo al punto successivo (ossia quello a
  1714. | riempire|     \ B     destra) e coloriamo anch'esso; procediamo col
  1715. |        D \     |      colorare i pixel successivi fin quando arriviamo
  1716. |           \    |      a colorare il punto posto sul lato AB e passiamo
  1717. |            \   |      alla prossima riga.
  1718. |             \  |      Cio' significa che per ogni riga ci servono le
  1719. |y             \ |      coordinate x del punto estremo a destra e del
  1720. v               \|C     punto estremo a sinistra.
  1721.  
  1722. Ora che abbiamo capito cosa fare vediamo come realizzare il tutto.
  1723.  Bisogna utilizzare due tabelle (matrici unidimensionali) in memoria di
  1724. dimensioni equivalenti al numero di pixel verticali rappresentabili sullo
  1725. schermo (es.: in una risoluzione 320*200 dobbiamo avere due tabelle di
  1726. 200 valori ciascuna). Consideriamo ogni posizione delle tabelle una
  1727. posizione y nello schermo ed il contenuto del primo array come la
  1728. corrispondente componente x del punto estremo a sinistra mentre il valore
  1729. del secondo array come la componente x del punto estremo a destra.
  1730. Cosi' facendo bastera' colorare tutti i pixel alla riga corrispondente
  1731. la posizione delle tabelle partendo dalla posizione x contenuta dalla
  1732. prima tabella sino alla posizione x contenuta dalla seconda (tutti i punti
  1733. di una riga hanno la stessa ordinata). Facciamo un esempio pratico:
  1734.  
  1735.  0| x=0 ->A. <- x=0        per semplicita' consideriamo i segmenti AB e CD
  1736.  1| x=0 -> .. <- x=1       inclinati a 45 gradi . I vertici sono:
  1737.  2| x=0 -> . . <- x=2      A(0,0)  B(5,5)  C(5,10)  D(0,5)
  1738.  3| x=0 -> .  . <- x=3     le nostre due tabelle saranno:
  1739.  4| x=0 -> .   . <- x=4    +-------------------------------------------+
  1740.  5| x=0 ->D.    .B<- x=5   |TAB1| 0| 0| 0| 0| 0| 0| 1| 2| 3| 4| 5|..|..|
  1741.  6|  x=1 -> .   . <- x=5   +-------------------------------------------+
  1742.  7|   x=2 -> .  . <- x=5   |TAB2| 0| 1| 2| 3| 4| 5| 5| 5| 5| 5| 5|..|..|
  1743.  8|    x=3 -> . . <- x=5   +-------------------------------------------+
  1744.  9|     x=4 -> .. <- x=5   Per riempire il poligono bastera' colorare i
  1745. 10|y     x=5 -> .C<- x=5   pixel compresi tra i corrispondenti valori
  1746.   v                        delle due tabelle utilizzando come componente
  1747.                y l'indice delle tabelle (che e' lo stesso per
  1748. entrambe). A volte e' possibile eliminare i due valori estremi delle
  1749. tabelle, quando in quelle righe vi e' un solo pixel (come nel nostro
  1750. esempio). Adesso non ci rimane altro che ricavarci il contenuto di questi
  1751. due array.
  1752.  Per definizione si indica col termine "scanline" una linea orizzontale
  1753. su schermo del poligono. Un "edge" non e' altro che uno dei due pixel ai
  1754. bordi della scanline, quindi gli array di cui dovremo ricavarci il
  1755. contenuto possiamo chiamarli "edge buffer".
  1756.  Le tabelle contengono semplicemente le coordinate x di tutti i punti che
  1757. compongono i lati del poligono; inoltre queste ascisse sono ordinate in
  1758. base alla loro componente y. In pratica dobbiamo svolgere una routine di
  1759. tracciamento di linee per tutti i lati della faccia, in cui non
  1760. visualizziamo i pixel, bensi' salviamo la componente x in un array la cui
  1761. posizione equivale alla y di quel punto. Questa procedura e' denominata
  1762. "scan conversion" e praticamente consiste nel suddividere il poligono in un
  1763. insieme di righe e colonne.
  1764.  Per realizzare la scan conversion e' possibile utilizzare l'algoritmo di
  1765. Bresenham, pero' conviene sfruttare il procedimento di interpolazione
  1766. lineare, che risulta piu' efficiente. Vediamo sinteticamente in cosa
  1767. consiste.
  1768.  Consideriamo due generici punti A(x1,y1) e B(x2,y2) dove y2>y1.
  1769. Ora calcoliamo:
  1770.  
  1771.        dx = x2 - x1     <-- lunghezza della linea che unisce A e B
  1772.        dy = y2 - y1     <-- altezza della linea che unisce A e B
  1773.        stepx = dx / dy  <-- numero di pixel orizzontali su ogni riga
  1774.  
  1775. Mentre l'algoritmo generale e':
  1776.  
  1777.        > x = x1
  1778.        > y = y1
  1779.        > loop di dy iterazioni
  1780.           > se e' libera la posizione y della tab1:
  1781.              > salva x in posizione y della tab1
  1782.           > altrimenti:
  1783.              > salva x in posizione y della tab2
  1784.           > x = x + stepx
  1785.           > y = y + 1
  1786.        > prossima iterazione
  1787.  
  1788. Questo algoritmo permette di riempire parzialmente un edge buffer nel caso
  1789. y2>y1, se invece risulta y1>y2 bastera' scambiare entrambe le coordinate
  1790. dei due punti (ossia considerare y1 come y2 e x1 come x2). Per riempire
  1791. completamente l'edge buffer bastera' ripetere tale procedura per ogni lato
  1792. del poligono.
  1793.  La tab1 e' l'array che contiene i punti estremi a sinistra mentre la tab2
  1794. punti estremi a destra. Col nostro algoritmo puo' capitare che alcuni
  1795. i valori scritti nella tab1 appartengano alla tab2, e viceversa. Cio'
  1796. significa che le coordinate x presenti nella tab1 potranno essere gli edge
  1797. a destra mentre i valori presenti nella tab2 potranno essere gli edge a
  1798. sinistra. Vediamo cosa fare per evitare questo inconveniente.
  1799.  
  1800.  Se abbiamo i punti salvati in senso orario il tutto risulta piu' semplice
  1801. e veloce. Dati due punti A(x1,y1) e B(x2,y2) posti in senso orario, se y1
  1802. e' maggiore di y2 allora l'insieme dei punti che formano quel lato
  1803. appartiene alla tab1 (quella contenente le posizioni x minori), in caso
  1804. contrario quei punti apparterranno alla tab2 (contenente le x maggiori).
  1805. Ecco l'algoritmo completo per la scan conversion relativo ad un singolo
  1806. lato:
  1807.  
  1808.        > confronta y1 con y2
  1809.           > se y1=y2:
  1810.             > esci dalla procedura!
  1811.           > se y1>y2:
  1812.             > la giusta tab e' tab1
  1813.           > se y1<y2:
  1814.             > la giusta tab e' tab2
  1815.             > scambia y1 con y2
  1816.             > scambia x1 con x2
  1817.        > dy = y1 - y2
  1818.        > dx = x1 - x2
  1819.        > stepx = dx / dy
  1820.        > x = x2
  1821.        > y = y2
  1822.        > loop di dy iterazioni
  1823.           > salva x in posizione y nella giusta tab
  1824.           > x = x + stepx
  1825.           > y = y + 1
  1826.        > prossima iterazione
  1827.  
  1828.  Una volta eseguita la scan conversion del poligono dovremo calcolare la
  1829. componente y minore dei quattro punti che compongono i vertici del
  1830. poligono ed l'altezza in pixel del poligono stesso. La coordinata y minore
  1831. rappresenta l'indice delle tabelle da cui partire per riempire il poligono
  1832. e quindi la posizione y superiore del poligono. L'altezza del poligono e'
  1833. equivalente alla differenza tra la y maggiore e la y minore e ci serve
  1834. per sapere quante righe dovremo riempire per l'attuale poligono.
  1835.  
  1836.  Riassumendo, per effettuare il fill di un poligono si devono svolgere i
  1837. seguenti passi:
  1838.  
  1839.     - definire in memoria due tabelle dimensionate ad ys valori (dove ys
  1840.       rappresenta l'altezza in pixel dello schermo);
  1841.     - calcolare la y minore dei vertici e l'altezza del poligono;
  1842.     - svolgere la scan conversion del poligono salvando le coordinate x
  1843.       iniziali e finali (edge) di ogni scanline nell'edge buffer;
  1844.     - partendo dalla posizione y minore, riempire la riga delimitata dalle
  1845.       posizioni x contenute dall'edge buffer stesso per un numero di volte
  1846.       equivalente all'altezza del poligono.
  1847.  
  1848.                 
  1849.                                 fLAT sHADING
  1850. ==========================================================================
  1851.  
  1852.  Siamo arrivati all'analisi del primo (e piu' semplice) algoritmo di
  1853. shading, grazie al quale potremo attribuire ad ogni poligono comprendente
  1854. l'oggetto una precisa intensita' di luce, la quale verra' determinata in
  1855. base all'orientazione della faccia rispetto alla sorgente di luce.
  1856.  Il flat shading permette di attribuire ad ogni faccia un solo colore che
  1857. determinera' quanto il poligono sia illuminato. Facciamo un esempio:
  1858.  
  1859.              +-------------+
  1860.   vista      |             |
  1861. dall'alto    |             |         <--- oggeto in 3D teorico (teorico
  1862.              |             |              perche' in realta' non c'e', noi
  1863.              |             |              andiamo a visualizzare le
  1864.              +-------------+              coordinate proiettate)
  1865.                     |
  1866.                     |                <--- direzione della sorgente di luce
  1867.       ______________|_____________   <--- schermo del monitor
  1868.                     |
  1869.                     |
  1870.                     o                <--- punto di vista dell'osservatore
  1871.  
  1872.  Consideriamo che la sorgente di luce corrisponda con il punto di vista,
  1873. la sua direzione e' perpendicolare lo schermo. Definiamo l'angolo di
  1874. inclinazione della faccia rispetto la sorgente di luce come l'angolo
  1875. compreso tra la retta corrispondente la direzione della luce ed il versore
  1876. della faccia (ovvero la retta normale).
  1877.  Piu' questo angolo e' piccolo e piu' il poligono risulta orientato verso
  1878. l'osservatore. Possiamo intuire che tanto la faccia e' posta di fronte
  1879. l'osservatore e tanto sara' maggiore l'intensita' di luce applicata su
  1880. quella faccia. Di conseguenza ad un angolo minore corrispondera' una
  1881. luminosita' maggiore della faccia. Ecco un altro esempio:
  1882.  
  1883.                          /\
  1884.                         /  \         vista dall'alto
  1885.                        /    \
  1886.        oggetto 3D --> /      \
  1887.        immaginario   /        \
  1888.                     /          \
  1889.                     \          /
  1890.                      \        /   a = angolo compreso tra il versore della
  1891.                       \      /        faccia e la direzione della luce
  1892.                      /|\    /
  1893.                     /-| \  /
  1894.                    / a|  \/
  1895.       versore     /   |
  1896.       faccia --> /    | <-- direzione luce (in questo caso
  1897.                 /     |     coincidente il punto di vista)
  1898.                /      |
  1899.            -----------|------------- <-- schermo del monitor
  1900.                       o              <-- punto di vista dell'osservatore
  1901.  
  1902.  L'insensita' di luce attribuibile al poligono e' proporzionale al coseno
  1903. di questo angolo. Sappiamo che il generico risultato di cos(a) e' compreso
  1904. tra -1 e 1. Da notare che se il poligono e' visibile il nostro angolo
  1905. varia da 0 a 90 gradi, altrimenti la faccia risulta nascosta (e' possibile
  1906. utilizzare questa caratteristica per l'eliminazione dell'hidden face!).
  1907. Quindi il valore del coseno relativo al nostro angolo copre un range di
  1908. valori da 0 e 1.
  1909.  Inoltre, utilizzando 256 colori, bastera' moltiplicare il coseno per 256
  1910. (oppure effettuare uno shift di 8 bit a sinistra) e otterremo il pixel
  1911. chunky col quale dovremo riempire la relativa faccia!
  1912. Vediamo come ricavare questo valore.
  1913.  
  1914.  Innanzitutto dobbiamo specificare la palette dei colori da utilizzare.
  1915. Cio' e' possibile definendo nella memoria video una tavolozza che parte
  1916. dal colore di luminosita' minore sino ad arrivare gradualmente al colore
  1917. piu' chiaro.
  1918.  Per il calcolo del coseno conviene sfruttare la regola di Lambert, la
  1919. quale afferma che il prodotto scalare tra due rette espresse come
  1920. grandezze vettoriali e' equivalente al prodotto della lunghezza dei
  1921. relativi vettori e del coseno dell'angolo limitato dalle rette stesse,
  1922. ovvero l'angolo a. Quindi, per conoscere cos(a), non dovremo far altro che
  1923. svolgere questo prodotto scalare e dividere il risultato per il prodotto
  1924. delle lunghezze dei due vettori.
  1925.  
  1926.  Per calcolare il prodotto scalare di due vettori si esegue il prodotto
  1927. delle corrispondenti componenti e poi si sommano i risultati, ad esempio:
  1928.  
  1929.         H=(xh,yh,zh)  ;  K=(xk,yk,zk)
  1930.         H*K=xh*xk+yh*yk+zh*zk         <-- prodotto scalare
  1931.  
  1932.  Per ricavare una lunghezza di un vettore possiamo ricorrere al teorema di
  1933. Pitagora grazie al quale si puo' affermare che la lunghezza e' equivalente
  1934. alla radice quadrata della somma dei quadrati di ogni componente.
  1935.  
  1936. Verifichiamo come calcolare i coefficienti x, y e z del versore della
  1937. faccia:
  1938.  
  1939.         Nx=(y2-y1)*(z3-z1)-(y3-y1)*(z2-z1)    <-+--- i tre coefficienti
  1940.         Ny=(z2-z1)*(x3-x1)-(z3-z1)*(x2-x1)    <-|    della retta normale
  1941.         Nz=(x2-x1)*(y3-y1)-(x3-x1)*(y2-y1)    <-+
  1942.  
  1943.  N.B.: i punti devono essere posti in memoria in senso orario!
  1944.  
  1945.         x1, y1, z1 = componenti del primo punto del poligono
  1946.         x1, y2, z2 = componenti del secondo punto del poligono
  1947.         x3, y3, z3 = componenti del terzo punto del poligono
  1948.  
  1949.  Infine ecco la formula per calcolare il pixel chunky:
  1950.  
  1951.                             Nx*lx + Ny*ly + Nz*lz
  1952.         cos(a)=-------------------------------------------------
  1953.                sqrt(Nx*Nx+Ny*Ny+Nz*Nz) * sqrt(lx*lx+ly*ly+lz*lz)
  1954.  
  1955.         pixel chunky = 256*cos(a)
  1956.  
  1957.          a = angolo tra il versore e la direzione della sorgente di luce
  1958.         lx = componente x della sorgente di luce
  1959.         ly = componente y della sorgente di luce
  1960.         lz = componente z della sorgente di luce
  1961.  
  1962.  Le coordinate lx, ly e lz rappresentano la posizione della sorgente di
  1963. luce. Nel caso la luce coincida con il punto di vista dell'osservatore,
  1964. le relative coordinate risulteranno:
  1965.  
  1966.         lx=0  ;  ly=0  ;  lz=-256
  1967.  
  1968. zl e' uguale all'opposto della distanza tra l'osservatore e lo schermo
  1969. (nel nostro caso la distanza tra l'osservatore e lo schermo e' 256).
  1970.  
  1971.  
  1972.         oTTIMIZZAZIONI pER iL cALCOLO dELLA sORGENTE dI lUCE
  1973. ==========================================================================
  1974.  
  1975.  In questa parte vediamo come velocizzare il nostro motore 3D nel caso
  1976. comprenda l'implementazione di una sorgente di luce reale.
  1977.  
  1978.  Una prima ottimizzazione consiste nell'utilizzare un buffer dove andremo
  1979. a precalcolare tutte le normali di ogni faccia (o di ogni vertice nel caso
  1980. del gouraud shading); poi anziche' calcolare ad ogni frame tutte le
  1981. normali, ruotiamo i nostri versori precalcolati dello stesso angolo con
  1982. cui ruotiamo i vertici dell'oggetto sfruttando la stessa identica
  1983. procedura descritta in precedenza (utilizzando preferibilmente l'algoritmo
  1984. a 9 moltiplicazioni).
  1985.  
  1986.  Abbiamo detto che il prodotto scalare tra il vettore normale ed il
  1987. vettore corrispondente la sorgente di luce moltiplicato 256 ci permette
  1988. di conoscere il pixel chunky. Ora vediamo come eliminare subito le radici
  1989. quadrate e la divisione. Analizziamo di nuovo la formula:
  1990.  
  1991.                             Nx*lx + Ny*ly + Nz*lz
  1992.         cos(a)=-------------------------------------------------
  1993.                sqrt(Nx*Nx+Ny*Ny+Nz*Nz) * sqrt(lx*lx+ly*ly+lz*lz)
  1994.  
  1995. Per attuare questa ottimizzazione dobbiamo rendere unitario il vettore
  1996. normale ed il vettore corrispondente la sorgente di luce. Rendere unitario
  1997. un vettore significa dividere ognuna delle componenti per la sua distanza
  1998. dall'origine; cio' fa si' che le nuove componenti abbiano un range
  1999. compreso tra -1 e +1, ecco perche' si dice unitario.
  2000.  Vediamo algebricamente come rendere unitario un qualsiasi versore:
  2001.  
  2002.                                       Nx
  2003.                       uNx=---------------------------
  2004.                           sqrt(Nx*Nx + Ny*Ny + Nz*Nz)
  2005.  
  2006.                                       Ny
  2007.                       uNy=---------------------------
  2008.                           sqrt(Nx*Nx + Ny*Ny + Nz*Nz)
  2009.  
  2010.                                       Nz
  2011.                       uNz=---------------------------
  2012.                           sqrt(Nx*Nx + Ny*Ny + Nz*Nz)
  2013.  
  2014.  Piu' genericamente, dato un vettore V(x,y,z), per calcolare le
  2015. componenti del relativo vettore unitario uV(ux,uy,uz):
  2016.  
  2017.                                       x
  2018.                           ux=---------------------
  2019.                              sqrt(x*x + y*y + z*z)
  2020.  
  2021.                                       y
  2022.                           uy=---------------------
  2023.                              sqrt(x*x + y*y + z*z)
  2024.  
  2025.                                       z
  2026.                           uz=---------------------
  2027.                              sqrt(x*x + y*y + z*z)
  2028.  
  2029.  Per rendere unitaria la sorgente di luce possiamo anche evitare di
  2030. dividere ogni sua componente per la lunghezza della medesima, infatti
  2031. siamo noi a decidere dove si trova, quindi arbitrariamente possiamo
  2032. assegnare coordinate unitarie. Ad esempio tornando al caso che la luce
  2033. coincida col punto di vista:
  2034.  
  2035.                      ulx=0  ;  uly=0  ;  ulz=-1
  2036.  
  2037. Quindi la formula per calcolare l'intensita' di luce viene ridotta a:
  2038.  
  2039.                      cos(a) = uNx*ulx + uNy*uly + uNz*ulz
  2040.                      pixel chunky = 256*cos(a)
  2041.  
  2042.  Rendere unitario un vettore e poi ruotarlo o ruotare un vettore e poi
  2043. renderlo unitario significa svolgere la stessa funzione; quindi al momento
  2044. in cui andiamo a precalcolare le normali possiamo renderle subito unitarie,
  2045. in seguito andremo a ruotare i versori gia' resi unitari. In questo modo
  2046. evitiamo di eseguire 2 radici quadrate e una divisione per vertice ad ogni
  2047. frame!
  2048.  
  2049. N.B.: nel caso si voglia muovere la sorgente di luce, utilizzando questa
  2050.       ottimizzazione non si potranno attuare traslazioni della sorgente
  2051.       di luce, ma solo rotazioni, in quanto la distanza origine-luce deve
  2052.       restare costante.
  2053.  
  2054.  L'ultima ottimizzazione consiste nel mantere fissa in un punto la
  2055. sorgente di luce, piu' precisamente diciamo che deve coincidere sempre con
  2056. il punto di vista dell'ossevatore. Naturalmente utilizzeremo coordinate
  2057. unitarie per i versori e la luce. Vediamo la formula per calcolare il
  2058. pixel chunky in questo particolare caso:
  2059.  
  2060.             pixel chunky = 256*( uNx*ulx + uNy*uly + uNz*ulz) =
  2061.                          = 256*( uNx*0   + uNy*0   + uNz*(-1))=
  2062.                          = -256*uNz
  2063.  
  2064. Ora il nostro pixel chunky dipende solo da uNz, quindi possiamo
  2065. precalcolare per ogni vertice -256*uNz al posto del semplice uNz, ruotarlo
  2066. e utilizzare subito questo valore come pixel chunky. In questo modo
  2067. evitiamo 3 moltplicazioni e 2 addizioni. Inoltre siccome ci serve solo uNz
  2068. possiamo benissimo evitare di ruotare uNx e uNy, risparmiando la bellezza
  2069. di altre 6 moltiplicazioni per faccia (o per vertice nel caso del
  2070. gouraud). In totale risparmiamo ben 9 moltiplicazioni e 2 addizioni per
  2071. faccia (o per vertice nel gouraud)!
  2072.  Naturalmente dovremo precalcolare oltre a -256*uNz anche uNx e uNy
  2073. moltiplicati 256 (256*uNx, 256*uNy) che servono per poter ruotare -256*uNz.
  2074.  Inoltre se invertiamo la nostra palette potremo utilizzare 256*uNz al
  2075. posto di -256*uNz.
  2076.  
  2077.  
  2078.                                 gOURAUD sHADING
  2079. ==========================================================================
  2080.  
  2081.  Questo algoritmo di ombreggiatura permette di sfumare l'interno di ogni
  2082. poligono al contrario del flat shading col quale si assegna un unico
  2083. colore per faccia.
  2084.  
  2085.  Innanzitutto bisogna calcolare il versore di ogni vertice dell'oggetto
  2086. anziche' di ogni poligono. Le componenti della normale sul vertice sono
  2087. equivalenti alla media aritmetica delle componenti delle normali di tutte
  2088. le facce che toccano quel vertice. Facciamo un esempio:
  2089.  
  2090.             ____        sia V un generico vertice di un cubo appartenente
  2091.            /f2 /\       alle facce f1, f2 e f3. Consideriamo le normali di
  2092.           /___/V \      codeste facce, chiamiamo questi versori N1,N2,N3.
  2093.           \f1 \f3/      
  2094.            \___\/       N1(Nx1,Ny1,Nz1)  N2(Nx2,Ny2,Nz2)  N3(Nx3,Ny3,Nz3)
  2095.  
  2096.  
  2097. Allora la normale su V e' equivalente a:
  2098.  
  2099.            NV( (Nx1+Nx2+Nx3)/3, (Ny1+Ny2+Ny3)/3, (Nz1+Nz2+Nz3)/3 )
  2100.  
  2101. In questo caso le facce appartenenti a V sono 3, a seconda dell' oggetto
  2102. che si vuol utilizzare il numero di poligoni appartenenti ad un vertice
  2103. cambiano.
  2104.  
  2105.  Una volta precalcolate tutte le normali (preferibilmente gia' unitarie)
  2106. su ogni spigolo dovremo calcolare la quantita' di luce che cade su ognuno
  2107. dei vertici, ovvero il pixel chunky, utilizzando la legge gia' studiata
  2108. nel flat shading (magari sfruttando eventuali ottimizzazioni citate nel
  2109. precedente paragrafo).
  2110.  
  2111.  In seguito facciamo la scan conversion (spiegata nel paragrafo dedicato
  2112. al fill e scan line) di tutti i poligoni visibili.
  2113.  
  2114.  Adesso dobbiamo interpolare linearmente per ogni faccia i pixel chunky
  2115. appartenenti ai vertici di quella faccia. In pratica dovremo svolgere una
  2116. semplice scan conversion del poligono utilizzando i pixel chunky al posto
  2117. delle coordinate x dei vertici, tutto qui. Naturalmente cio' va svolto
  2118. solamente se la faccia risulta visibile.
  2119.  
  2120.  Non rimane altro che effettuare il fill vero e proprio dei poligoni. Come
  2121. per un normale fill bisogna eseguire un loop ad iterazioni equivalenti
  2122. all'altezza in pixel del poligono. Ad ogni iterazione preleviamo di volta
  2123. in volta le coordinate x iniziali e finali dalle tabelle delle scan line
  2124. (come per un normale fill), pero' stavolta preleviamo anche i pixel chunky
  2125. iniziali e finali.
  2126.  Ora dobbiamo interpolare il pixel chunky iniziale con quello finale
  2127. partendo dalla coordinata x iniziale sino ad arrivare a quella finale. Per
  2128. far cio' basta utilizzare l'algoritmo per tracciare una scan line con le
  2129. seguenti modifiche:
  2130.  
  2131.      - utilizzare il pixel chunky iniziale al posto della coordinata x1;
  2132.      - utilizzare il pixel chunky finale al posto della coordinata x2;
  2133.      - utilizzare la x iniziale al posto della coordinata y1;
  2134.      - utilizzare la x finale al posto della coordinata y2;
  2135.      - utilizzare la riga dello schermo chunky da "fillare" come tabella
  2136.        dove la scan line verra' salvata.
  2137.  
  2138. Ed ecco realizzato il gouraud shading!
  2139.  
  2140.  
  2141.                                 pHONG sHADING
  2142. ==========================================================================
  2143.  
  2144.  Il phong shading permette di assegnare ad ogni pixel la sua reale
  2145. intensita' di luce, al contrario del gouraud nel quale si generano
  2146. sfumature all'interno di ogni faccia tra le intensita' di luce di ogni
  2147. vertice dell'oggetto.
  2148.  La maggiore definizione che si ottiene col phong rispetto al gouraud
  2149. comporta allo stesso tempo un drastico aumento delle operazioni che il
  2150. processore deve svolgere. La pesantezza dei calcoli da eseguire e' tale da
  2151. impedire agli attuali elaboratori di tracciare soddisfacienti scene in
  2152. phong shading in tempo reale.
  2153.  
  2154.  Nel gouraud calcoliamo la reale intensita' di luce su ogni vertice,
  2155. dopodiche' ogni colore viene intepolato lungo ogni lato del poligono,
  2156. infine si interpolano i colori posti sui lati estremi a sinistra con i
  2157. colori posti sui lati estremi a destra in modo da riempire l'intero
  2158. poligono.
  2159.  Nel phong invece interpoliamo sempre le normali, non si interpolano mai
  2160. i colori. Una volta determinati i versori su ogni vertice, questi devono
  2161. essere interpolati lungo ogni lato; successivamente i versori posti lungo
  2162. i lati estremi a sinistra verranno interpolati con quelli posti lungo i
  2163. lati estremi a destra, quindi per ogni singolo verra' calcolato il colore
  2164. sfruttando la tradizionale formula piu' volte studiata.
  2165.  
  2166.  Il phong ci impedisce di sfruttare le diverse ottimizzazioni possibili
  2167. col gouraud shading e col flat shading. Infatti nel phong e' impossibile
  2168. utilizzare normali unitarie in quanto nel momento in cui queste vengono
  2169. interpolate la loro lunghezza (equivalente al risultato dell'espressione
  2170. sqrt(Nx*Nx+Ny*Ny+Nz*Nz)) puo' variare. Quindi occore eseguire almeno
  2171. una divisione ed una radice quadrata per pixel, il che non e' poco.
  2172.  
  2173.  
  2174.                         rEFLECTION mAPPING
  2175. ==========================================================================
  2176.  
  2177.  Se un solido riflette sempre e solo una singola immagine (in gergo
  2178. denominata "texture") allora possiamo affermare che su quel solido e'
  2179. stato applicato il reflection mapping.
  2180.  Nel caso la texture corrisponda approsimativamente alla rappresentazione
  2181. bidimensionale di una luce (es.: un cerchio il cui centro risulta molto
  2182. chiaro mentre agli orli viene sfumato in una tinta piu' scura), e'
  2183. possibile raggiungere effetti simili (e a volte superiori) al phong e al
  2184. gouraud.
  2185.  
  2186.  Spesso questo effetto viene erroneamente confuso con l'environment
  2187. mapping, il quale permette invece di riflettere un intero ambiente che
  2188. circonda l'oggetto (il quale ambiente e' spesso definito per semplicita'
  2189. come un cubo, quindi in questo caso sul solido verranno riflesse sei
  2190. immagini). Tuttavia e' possibile considerare il reflection mapping come
  2191. un'approssimazione dell'environment mapping.
  2192.  
  2193.  In questo paragrafo verra' descritto come realizzare il reflection
  2194. mapping utilizzando esclusivamente texture di dimensioni 256*256 pixel.
  2195. L'implementazione di texture di dimensioni differenti e' facilmente
  2196. derivabile.
  2197.  
  2198.  Vediamo dettagliatamente come realizzare un comune oggetto in reflection
  2199. mapping.
  2200.  Inizialmente ci andiamo a precalcolare tutti i versori unitari su ogni
  2201. vertice (come per il gouraud) e li moltiplichiamo per 128 (oppure
  2202. applichiamo un semplice scorrimento di 7 bit a sinistra), il che
  2203. matematicamente si traduce in:
  2204.             _                                            _
  2205.            |  PVx = 128*Nx / sqrt(Nx*Nx + Ny*Ny + Nz*Nz)  |
  2206.         PV |  PVy = 128*Ny / sqrt(Nx*Nx + Ny*Ny + Nz*Nz)  |
  2207.            |_ PVz = 128*Nz / sqrt(Nx*Nx + Ny*Ny + Nz*Nz) _|
  2208.  
  2209. Chiamiamo PV il vettore che ha come componenti questi 3 valori.
  2210. La normale unitaria ha come coordinate 3 valori che comprendono numeri
  2211. reali tra -1 e +1. Ora abbiamo PVx, PVy e PVz che rispetto ai versori
  2212. unitari sono molplicati 128, questo vuol dire che copriranno un raggio
  2213. di valori compresi tra -128 e +128 (anche se in realta' tali valori non
  2214. superano mai +127). E qui finisce la fase di precalcolo.
  2215.  
  2216.  In tempo reale dobbiamo ruotare il vettore PV per ogni vertice (casomai
  2217. sfruttando gli stessi fattori di rotazione dei punti se si ha realizzato
  2218. la rotazione a 9 moltiplicazioni). Del vettore PV ci servono solo le
  2219. componenti x e y ruotate, quindi possiamo anche non ruotare PVz evitando
  2220. cosi' di svolgere almeno 3 moltiplicazioni e 2 addizioni per vertice
  2221. (naturalmente e' necessario precalcolare PVz per ogni vertice per poter
  2222. ruotare PVx e PVy). Ad ognuna delle componenti ruotate x e y del vettore
  2223. PV addizioniamo il valore 128. Al termine di questi calcoli, il range dei
  2224. valori che PVx e PVy potranno comprire sara' compreso tra 0 e 255.
  2225.  
  2226.  In realta' ((PVx ruotato)+128) e ((PVy ruotato)+128) rappresentano le
  2227. coordinate della texture da mappare (ossia tracciare) sul poligono. Cio'
  2228. significa che se abbiamo un poligono delimitato da 4 punti, dobbiamo
  2229. mappare su quel poligono la parte di texture delimitata dai 4 relativi PVx
  2230. e PVy ruotati (e sommati con 128). Quindi bastera' mappare il "pezzo" di
  2231. texture su quel poligono e ripetere il tutto per ogni faccia visibile per
  2232. realizzare il reflection mapping!
  2233.  
  2234.  Ora vediamo come tracciare la parte di texture una volta calcolati i
  2235. nuovi PVx e PVy.
  2236.  Innanzitutto dobbiamo svolgere la scan conversion del poligono, in piu'
  2237. bisogna interpolare PVx e PVy lungo tutti i lati della nostra faccia, il
  2238. che significa fare 2 ulteriori scan conversion del poligono utilizzando
  2239. i PVx e i PVy al posto delle coordinate x dei vertici. Quindi in tutto
  2240. vi sono 3 scan conversion: la prima e' quella tradizionale, la seconda
  2241. viene fatta sostituendo PVx alle x dei vertici, mentre la terza utilizza i
  2242. PVy al posto delle x (facciamo esattemente come per le normali nel phong,
  2243. con la differenza che consideriamo 2 componenti (PVx e PVy) al posto di 3
  2244. (Nx, Ny e Nz)).
  2245.  Abbiamo eseguito la scan conversion del poligono, quel di cui abbiamo
  2246. bisogno e' un algoritmo che permetta di associare ad ogni punto
  2247. appartenente alla faccia un determinato pixel della texture.
  2248.  Consideriamo la seguente figura come la nostra faccia proiettata a video:
  2249.  
  2250.                .                 Dopo aver interpolato PVx e PVy e svolto
  2251.               . .                la scan conversion, per ogni coppia di
  2252.              .   .               punti sulla stessa posizione y dello
  2253.       P1 -> .     . <- P2        schermo, ad esempio P1 e P2, conosciamo
  2254.            .       .             la loro coordinata x assieme a PVx e PVy.
  2255.             .     .              Adesso dobbiamo interpolare PVx e PVy
  2256.              .   .               dal punto P1 al punto P2, in questo modo
  2257.               . .                sapremo il valore di PVx e PVy per tutti
  2258.                .                 i punti del poligono. Per interpolare
  2259.                                  questi 2 valori lungo una riga si deve
  2260.    P1 -> x1, y, PVx1, PVy1       applicare l'algoritmo generale per il
  2261.    P2 -> x2, y, PVx2, PVy2       tracciamento di una scan line utilizzando
  2262.    dPVx = PVx1-PVx2              dx al posto di dy,  dPVx (per interpolare
  2263.    dPVy = PVy1-PVy2              PVx) e dPVy (per interpolare PVy) al posto
  2264.      dx = x1-x2                  di dx, proprio come abbiamo fatto nel
  2265.                  gouraud per interpolare i pixel chunky.
  2266.  
  2267.  Come gia' accennato, PVx e PVy rappresentano le coordinate del pixel
  2268. texture da tracciare. Appena svolte le 3 scan conversion conoscevamo PVx e
  2269. PVy appartenenti ad ogni vertice della faccia. Quindi, interpolando PVx e
  2270. PVy lungo tutto il poligono, ricaveremo le coordinate dei punti della
  2271. texture per tutti i punti della faccia! Ora, con delle semplici operazioni
  2272. di copia, potremo mappare il poligono associando, ad ogni suo punto, il
  2273. pixel chunky della texture in posizione (PVx,PVy).
  2274.  
  2275.  
  2276.                         aFFINE tEXTURE mAPPING
  2277. ==========================================================================
  2278.  
  2279.  Questo effetto permette il tracciamento di un'intera immagine su di un
  2280. poligono, in pratica e' come se "incollassimo" su ogni faccia una
  2281. texture (ovvero una semplice immagine chunky posta in memoria).
  2282.  In questo paragrafo tratteremo del texture mapping senza prospettiva (il
  2283. cosidetto "affine texture mapping"), il quale risulta essere uno degli
  2284. algoritmi piu' veloci per mappare un'immagine su di un poligono, ma allo
  2285. stesso tempo anche il meno realistico.
  2286.  
  2287.  Per completezza definiamo gli assi relativi alla texture (posta in
  2288. memoria, non quella visualizzata su schermo) hanno come origine il punto
  2289. piu' in alto a sinistra. L'ascissa di tale sistema viene denominata <u>
  2290. mentre l'ordinata <v>; quindi un generico punto della texture puo' essere
  2291. indicato come P(u,v). Da notare che le componenti u e v non possono
  2292. assumere valori negativi.
  2293.  
  2294.  Consideriamo di utilizzare poligoni formati da 4 lati, ogni spigolo del
  2295. poligono coincide con uno spigolo della faccia, cio' che dobbiamo fare
  2296. e' tracciare tutta la texture sul poligono. In pratica e' come se
  2297. dovessimo realizzare il reflection mapping sapendo che le coordinate della
  2298. texture da mappare sono sempre costanti e coincidono esattamente con i 4
  2299. vertici della texture stessa. Se la texture e' 256*256 pixel realizzeremo
  2300. un algoritmo di reflection mapping sapendo che PV1(0,0), PV2(255,0),
  2301. PV3(255,255), PV4(0,255) sono gli stessi per ogni poligono. In altre
  2302. parole andiamo ad interpolare le coordinate x e y della texture (ovvero u
  2303. e v)lungo tutto il poligono, in modo tale da sapere per ogni pixel
  2304. appartenente alla faccia da tracciare quale sia il relativo punto della
  2305. texture. Tutto qui.
  2306.  
  2307.  Per chiarire meglio le idee osserviamo un semplice esempio:
  2308.  
  2309.     A +-----------------+ B         Abbiamo il nostro poligono ABCD in cui
  2310.       |                 |           intendiamo applicarci una texture.
  2311.       |                 |           Vogliamo che al punto piu' in alto a
  2312.       |                 |           sinistra della texture corrisponda il
  2313.       |                 |           vertice A, al punto in alto a destra 
  2314.       |                 |           il vertice B, a quello in basso a
  2315.       |                 |           destra C, mentre al punto in basso a
  2316.       |                 |           sinistra il vertice D.
  2317.       |                 |           Facciamo finta che le coordinate del
  2318.     D +-----------------+ C         poligono siano gia' state ruotate.
  2319.                                     Al punto A corrisponde il punto (0,0)
  2320. della texture, al punto B le coordinate (255,0), a C corrisponde (255,255)
  2321. ed infine a D corrisponde il pixel (0,255) della texture.
  2322.  
  2323. Quindi svolgiamo 2 ulteriori scan conversion della faccia (oltre a quella
  2324. classica per "fillare" il poligono) utilizzando una volta le <u> iniziali
  2325. e finali al posto delle x, mentre nella seconda volta si devono sfruttare
  2326. le <v> iniziali e finali al posto delle x; in questo modo (interolando la
  2327. <u> e la <v> per ogni scanline) sapremo le coordinate (u,v) della texture
  2328. di ogni pixel della faccia.
  2329.  
  2330.  
  2331.                         fREE dIRECTION tEXTURE mAPPING 
  2332. ==========================================================================
  2333.  
  2334.  
  2335.                         tEXTURE mAPPING bILINEARE
  2336. ==========================================================================
  2337.  
  2338.  
  2339.                         tEXTURE mAPPING bIQUADRATICO
  2340. ==========================================================================
  2341.  
  2342.  
  2343.                         oTTIMIZZAZIONE dEL fILL
  2344. ==========================================================================
  2345.  
  2346.  In questo paragrafo studieremo un alternativo algoritmo per realizzare il
  2347. fill dei poligoni nel caso avessimo bisogno di interpolare una o piu'
  2348. variabili lungo tutto il poligono (come nel caso del texture mapping, del
  2349. gouraud e del phong shading). L'unico vero svantaggio nell'utilizzare tale
  2350. metodo consiste nel fatto che tutti poligoni dovranno per forza essere
  2351. dei triangoli.
  2352.  
  2353.  Consideriamo di dover mappare una texture (o parte di essa) su di un
  2354. poligono. Applicando i metodi precedentemente descritti avremmo eseguito 2
  2355. divisioni per scanline, ovvero:
  2356.  
  2357.       du = u2 - u1        dove (u1,v1) rappresentano le coordinate
  2358.       dv = v2 - v1        della texture per l'edge a sinistra, mentre
  2359.       stepu = du / dx     (u2,v2) valgono per l'edge a destra.
  2360.       stepv = dv / dx
  2361.  
  2362.  L'ottimizzazione notevole che si ottiene con l'algoritmo che vedremo, sta
  2363. nel calcolare stepu e stepv, detti constant slope, una sola volta per
  2364. tutto il poligono. In altre parole al posto di svolgere 2 divisioni per
  2365. scanline, eseguiremo 2 divisioni per poligono, non male!
  2366.  
  2367.  In un triangolo qualsiasi, gli stepu e stepv sono sempre costanti lungo
  2368. tutto il poligono, quindi potremo calcolarci i costant slope una volta
  2369. sola (per faccia) e riutilizzarli per tutte le scanline di tale poligono.
  2370. Per ottenere risultati il piu' precisi possibile, calcoleremo gli stepu e
  2371. stepv relativi alla scanline piu' lunga di tutto il poligono, poi
  2372. utilizzeremo tali constant slope per tutte le scanline. Questo e' il
  2373. concetto fondamentale per poter ottimizzare il fill.
  2374.  
  2375.  Consideriamo un generico triangolo:
  2376.  
  2377.                 A(x1,y1) .              A(x1,y1) e' il vertice superiore,
  2378.                        _/ \             che sta piu' in alto;
  2379.                      _/    \            B(x2,y2) e' il vertice che sta a
  2380.                    _/       \ L1        media altezza;
  2381.               L3 _/          \          C(x3,y3) e' il vertice inferiore,
  2382.                _/             \         che sta piu' in basso;
  2383.              _/            ____\.       L1 e' il lato AB; L2 e' il lato BC
  2384.            _/      ____----   B(x2,y2)  mentre L3 e' il lato CA.
  2385. C(x3,y3) ./____----  L2
  2386.             
  2387. La scanline piu' lunga e' sempre quella che giace sul vertice B, ossia
  2388. quel vertice vediamo           
  2389. come calcorarne la relativa lunghezza assieme ai constant slope:
  2390.  
  2391.   temp = (y2-y1) / (y3-y1)
  2392.   scan =  temp * (x3-x1) + (x1-x2)          <- lunghezza scanline maggiore
  2393.  stepu = (temp * (u3-u1) + (u1-u2)) / scan  <- constant slope u
  2394.  stepv = (temp * (v3-v1) + (v1-v2)) / scan  <- constant slope v
  2395.  
  2396. Il valore di <scan> puo' essere positivo o negativo, il che ci permette di 
  2397. determinare se il vertice B e' il punto estremo a destra o a sinistra 
  2398. dell'intero poligono:
  2399.  
  2400.    se (scan = 0) allora il poligono e' vuoto, non tracciare il poligono;
  2401.    se (scan > 0) allora il vertice B e' posto a sinistra, quindi i lati L1
  2402.                         e L2 appartengono all'edge di destra, mentre solo
  2403.                         L3 appartiene all'edge di sinistra;
  2404.    se (scan < 0) allora il vertice B e' posto a destra, quindi i lati L1 e
  2405.                         L2 appartengono all'edge di sinistra, mentre solo
  2406.                         L3 appartiene all'edge di destra;
  2407.  
  2408.             
  2409.                         cORREZIONE pROSPETTICA
  2410. ==========================================================================
  2411.  
  2412.  
  2413.                                 cLIPPING 2d
  2414. ==========================================================================
  2415.  
  2416.  Nella visualizzazione di un oggetto o un mondo 3D, bisogna considerare il
  2417. problema del tracciamento di poligoni o linee parzialmente visibili sullo
  2418. schermo, ovvero di facce o linee che "escono" (anche parzialmente) fuori 
  2419. dal monitor. Il procedimento atto a risolvere questo problema e' chiamato
  2420. clipping 2D. Questo tipo di clipping e' 2D in quanto ci si propone di
  2421. "tagliare" i nostri oggetti solo rispetto al singolo piano coincidente
  2422. lo schermo.
  2423.  
  2424.  Come condizioni iniziali consideriamo i valori XMIN e XMAX come le 
  2425. componenti x estreme a sinistra e a destra dello schermo, mentre YMIN e
  2426. YMAX come le componenti y estreme in alto e in basso dello schermo.
  2427. Quindi i 4 vertici immaginari dello schermo avranno le seguenti 
  2428. coordinate:
  2429.  
  2430.            (XMIN,YMIN)                   (XMAX,YMIN)
  2431.                       +-----------------+
  2432.                       |                 |
  2433.                       |                 |
  2434.                       |                 |
  2435.                       |                 |
  2436.                       |                 |
  2437.                       |                 |
  2438.                       |                 |
  2439.                       +-----------------+
  2440.            (XMIN,YMAX)                   (XMAX,YMAX)
  2441.  
  2442. Ad esempio, nell'ipotetico caso di uno schermo di risoluzione 320x200:
  2443.  
  2444.            XMIN = 0  ;  XMAX = 319  ;  YMIN = 0  ;  YMAX = 199
  2445.  
  2446.  Analizziamo subito come clippare una singola linea, in seguito vedremo il 
  2447. clipping relativo ai poligoni (il quale si basa sullo stesso principio per
  2448. tagliare una linea).
  2449.  
  2450.  Caso 1: la linea esce dal bordo superiore dello schermo:
  2451.  
  2452.         P1 .                consideriamo P1 il punto che sta piu' in alto
  2453.             \               e P2 il punto che sta piu' in basso.
  2454.              \ P1c          Quel che dobbiamo fare e' calcolarci le nuove
  2455.         +-----+-------+     coordinate (clippate) di P1 in modo tale che
  2456.         |      \      |     risulti posizionato sul bordo superiore dello
  2457.         |       \.    |     schermo, chiamiamo questo punto P1c.
  2458.         |         P2  |
  2459.         |             |     P1c(xclip,yclip)
  2460.         |             |     P1(x1,y1)  ;  P2(x2,y2)
  2461.         +-------------+
  2462.                             xclip = x1 + (YMIN-y1)*(x1-x2)/(y1-y2)
  2463.                             yclip = YMIN
  2464.  
  2465. Una volta ricavate le coordinate di P1c non dovremo far altro che 
  2466. tracciare la linea tra P1c e P2.
  2467.  
  2468.  Caso 2: la linea esce dal bordo inferiore dello schermo:
  2469.  
  2470.         +-------------+     P1 e' il punto superiore mentre P2 e' il punto
  2471.         |             |     inferiore. P2c e' il punto di cui dobbiamo
  2472.         |         .P1 |     calcolarci le coordinate.
  2473.         |        /    |
  2474.         |       /     |     P2c(xclip,yclip)
  2475.         |  P2c /      |     P1(x1,y1)  ;  P2(x2,y2)
  2476.         +-----+-------+
  2477.          P2 ./              xclip = x2 + (YMAX-y2)*(x2-x1)/(y2-y1)
  2478.                             yclip = YMAX
  2479.  
  2480. La linea clippata sara' compresa tra i punti P1 e P2c.
  2481.  
  2482.  Caso 3: la linea esce dal bordo sinistro dello schermo:
  2483.  
  2484.         +-------------+     P1 e' il estremo della linea a sinistra e P2
  2485.   P1.___|P1c          |     e' il punto estremo a destra della linea.
  2486.         |---___. P2   |
  2487.         |             |     P1c(xclip,yclip)
  2488.         |             |     P1(x1,y1)  ;  P2(x2,y2)
  2489.         |             |
  2490.         +-------------+     xclip = XMIN
  2491.                             yclip = y1 + (XMIN-x1)*(y1-y2)/(x1-x2)
  2492.  
  2493. La linea clippata sara' compresa tra i punti P1c e P2.
  2494.  
  2495.  Caso 4: la linea esce dal bordo destro dello schermo:
  2496.  
  2497.         +-------------+     P1 e' il estremo della linea a sinistra e P2
  2498.         |             |     e' il punto estremo a destra della linea.
  2499.         |          ___|_.P2
  2500.         | P1.___---P2c|     P2c(xclip,yclip)
  2501.         |             |     P1(x1,y1)  ;  P2(x2,y2)
  2502.         |             |
  2503.         +-------------+     xclip = XMAX
  2504.                             yclip = y2 + (XMAX-x2)*(y2-y1)/(x2-x1)
  2505.  
  2506. La linea clippata sara' compresa tra i punti P1 e P2c.
  2507.  
  2508.  Dato che l'argomento relativo al clipping 2D dei poligoni e' stato gia'
  2509. affrontato esaurientemente da CDS/DeathStar, ho preferito evitare di
  2510. rispiegare tale argomento e ho deciso di riportare qui di seguito il testo 
  2511. che ha scritto CDS.
  2512.  
  2513.  L'algoritmo di Sutherland-Hodgman permette di eseguire il clipping di
  2514. un poligono convesso o concavo rispetto ad una finestra di clipping 
  2515. convessa. Solitamente la finestra di clipping e' un rettangolo (lo schermo 
  2516. video), ma in alcuni casi e' necessario "clippare" un poligono rispetto ad 
  2517. un altro (ad esempio nel calcolo delle ombre portate). Nel caso di 
  2518. finestra rettangolare SH ha bisogno di 4 passi, uno per ogni lato della 
  2519. finestra di clipping. SH lavora su una lista di vertici e produce in 
  2520. output, ad ogni passo, una nuova lista di vertici che si trovano dalla 
  2521. parte visibile rispetto al lato di clipping. Siano P1 il vertice attuale 
  2522. nella lista, P2 il vertice seguente e T l'eventuale punto di intersezione 
  2523. fra la retta passante per P1 e P2 e il lato di clipping.
  2524.  
  2525. Bisogna considerare 4 casi:
  2526.  
  2527. 1) P1 interno P2 interno
  2528.    Nella lista di destinazione si inserisce P2.
  2529. 2) P1 interno P2 esterno
  2530.    Nella lista di destinazione si inserisce T.
  2531. 3) P1 esterno P2 interno
  2532.    Nella lista di destinazione si inseriscono T e P2.
  2533. 4) P1 esterno P2 esterno
  2534.    Non si inserisce nessun vertice.
  2535.  
  2536.  Le condizioni interno/esterno nel caso di clipping con una finestra
  2537. rettangolare sono dei semplici confronti, nel caso di finestra convessa 
  2538. non rettangolare e' necessario eseguire un prodotto scalare. Ad esempio 
  2539. il test P1 interno nel caso di clipping con il lato sinistro dello
  2540. schermo e' semplicemente P1.x>0 .
  2541.  
  2542.  Un esempio di clipping di un triangolo con una finestra rettangolare:
  2543.  
  2544.                              /| A
  2545.         +---------------+  /  |
  2546.         |               |/    |
  2547.         |              /|     |
  2548.         |            /  |     |
  2549.         |          /    |     |
  2550.         |        /      |     |
  2551.         +---------------+     |
  2552.               /               |
  2553.              +----------------+
  2554.             B                   C
  2555.  
  2556.  Passo 1 (clipping con il lato superiore) :
  2557.  
  2558.                           T1   T2
  2559. --------+---------------+--x--x-----
  2560.         |               |/    |
  2561.         |              /|     |
  2562.         |            /  |     |
  2563.         |          /    |     |
  2564.         |        /      |     |
  2565.         +---------------+     |
  2566.               /               |
  2567.              +----------------+
  2568.  
  2569.        A e' esterno e B e' interno, si inseriscono T1 e B.
  2570.        B e' interno e C e' interno, si inserisce C.
  2571.        C e' interno e A e' esterno, si inserisce T2.
  2572.  
  2573. Si ottiene un nuovo poligono con i due vertici temporanei T1 e T2.
  2574.  
  2575.  Passo 2 (clipping con il lato sinistro):
  2576.  
  2577.         |
  2578.         |                 T1   T2
  2579.         +---------------+--x--x
  2580.         |               |/    |
  2581.         |              /|     |
  2582.         |            /  |     |
  2583.         |          /    |     |
  2584.         |        /      |     |
  2585.         +---------------+     |
  2586.         |     /               |
  2587.         |    +----------------+
  2588.         |
  2589.  
  2590. Passo 3 (clipping con il lato inferiore) :
  2591.  
  2592.                           T1   T2
  2593.         +---------------+  x--x
  2594.         |               |/    |
  2595.         |              /|     |
  2596.         |            /  |     |
  2597.         |          /    |     |
  2598.         |        /      |     |
  2599. --------+------x--------+-----x-------
  2600.         T3              T4
  2601.  
  2602. Si ottiene un nuovo poligono  con i due vertici temporanei T3 e T4.
  2603.  
  2604.  Passo 4 (clipping con il lato destro) :
  2605.  
  2606.                         |
  2607.                         |
  2608.         +---------------+
  2609.         |               |
  2610.         |              /x  T6
  2611.         |            /  |
  2612.         |          /    |
  2613.         |        /      |
  2614.         +------x--------x T5
  2615.                 T3      |
  2616.                         |
  2617.                         |
  2618.  
  2619. Il poligono definitivo ha per vertici T3, T5 e T6.
  2620.  
  2621. CDS/DeathStar
  2622.  
  2623.  
  2624.                                 z-bUFFER
  2625. ==========================================================================
  2626.  
  2627.  Lo Z-Buffer e' un'area di memoria dedicata all'eliminazione delle facce
  2628. completamente o parzialmente nascoste. E' particolarmente utile per
  2629. tracciare poligoni che si intersecano e non comporta limitazioni nella
  2630. scena da tracciare (al contrario dell'algoritmo del pittore).
  2631.  
  2632.  Lo spazio occupato in memoria dallo Z-Buffer coincide con le dimensioni
  2633. dello schermo (es.: per uno schermo 320*200 avremo bisogno di un buffer
  2634. di 64000 valori). Consideriamo lo Z-Buffer come il nostro schermo chunky,
  2635. con la differenza che ogni posizione non rappresenta un pixel, bensi' la
  2636. componente Z di quel pixel; in questo modo possiamo sapere la coordinata Z
  2637. di tutti i punti visualizzati su schermo.
  2638.  
  2639.  Ora si procede nella seguente maniera. Per una ed una sola volta per ogni
  2640. frame andiamo ad inizializzare lo Z-Buffer copiandoci su ogni posizione un
  2641. valore arbitrario piuttosto elevato.
  2642.  
  2643.  In seguito ci andiamo ad interpolare la coordinata Z di ogni vertice
  2644. lungo tutto il poligono (ossia facciamo come nel gouraud, ma consideriamo
  2645. Z al posto di Nz). 
  2646.  
  2647.  Nel loop nel quale tracciamo i pixel (ovvero il loop piu' interno del 
  2648. fill), confrontiamo la Z che stiamo interpolando con il valore 
  2649. corrispondente nello Z-Buffer (la cui posizione deve coincidere con quella 
  2650. dello schermo chunky) e, soltanto nel caso la nostra Z sia minore rispetto 
  2651. a quella dello Z-Buffer, copiamo la nostra Z nello Z-Buffer e scriviamo il 
  2652. pixel nello schermo chunky. Nel caso in cui la nostra Z sia maggiore o 
  2653. uguale rispetto al valore dello Z-Buffer, allora non dobbiamo ne' 
  2654. aggiornare lo Z-Buffer, ne' tracciare il pixel su schermo (comunque in 
  2655. ogni caso dobbiamo continuare ad interpolare sia la Z che altre eventuali
  2656. variabili).
  2657.  
  2658.  
  2659.                 gESTIONE dELLE tELECAMERE iN uNA sCENA 3d
  2660. ==========================================================================
  2661.  
  2662.  
  2663.                                 bSP tREES
  2664. ==========================================================================
  2665.  
  2666.  
  2667.                                 bUMP mAPPING 2d
  2668. ==========================================================================
  2669.  
  2670.  Nel texture mapping abbiamo la possibilta' di rivestire un oggetto con
  2671. un'immagine qualunque, quindi la superficie dell'oggetto sara' data dalla
  2672. texture ad essa applicata, quindi la superficie puo' considerarsi liscia,
  2673. ovvero piana, completamente piatta. Il bump mapping ha lo scopo di rendere
  2674. una texture "pseudotridimensionale", il che significa che l'immagine
  2675. e' solida (e non piatta) a livello ottico, ma in realta' viene elaborata
  2676. come uno strumento 2D (in pratica non c'e' nessun asse z per la texture).
  2677.  Per creare l'effetto "simil 3D" applichiamo sull'immagine ombre e luci,
  2678. utilizzando casomai una sorgente di luce in movimento.
  2679.  
  2680.  Per realizzare il bump mapping dovremo utilizzare una texture un po'
  2681. particolare, la quale deve avere una determinata disposizione dei colori.
  2682. Mettiamo caso che la nostra immagine utilizzi 256 colori, partendo dal
  2683. primo colore sino al duecentocinquantaseiesimo specifichiamo le relative
  2684. componenti RGB; nel nostro caso il primo colore dovra' essere il piu'
  2685. scuro, il successivo dovra' essere quello un po' meno scuro, il terzo
  2686. quello poco piu' chiaro del secondo e cosi' via, in modo tale che l'ultimo
  2687. colore sia il piu' chiaro di tutti.
  2688.  La texture dovra' trovarsi in memoria secondo una successione continua e
  2689. ordinata di righe (ovvero: ogni riga di pixel dovra' trovarsi esattamente
  2690. nella locazione di memoria successiva rispetto alla locazione in cui e'
  2691. presente l'ultimo pixel della riga precedente).
  2692.  Inoltre ogni pixel dovra' essere rappresentato come un singolo byte.
  2693.  
  2694.  Premesso cio', possiamo fare alcune osservazioni. Facciamo finta che la
  2695. texture da "bumpare" sia la rappresentazione di una catena montuosa vista
  2696. da un aereo, la cui tavolozza di colori e' composta da 256 tonalita' di
  2697. grigio. Prendiamo un pixel qualunque di questa immagine, il pixel chunky
  2698. e' un byte, quindi un valore compreso tra 0 e 255; tanto questo valore
  2699. sara' maggiore e tanto quel pixel risultera' chiaro (come colore). Un
  2700. pixel piu' e' chiaro e piu' riflettera' luce; i punti che riflettono piu'
  2701. luce sono quelli maggiormente vicini alla sorgente di luce: in sostanza
  2702. possiamo affermare che piu' e' alto il valore del pixel chunky e piu'
  2703. questo risultera' vicino alla sorgente di luce; al contrario possiamo dire
  2704. che piu' e' basso il valore del pixel e piu' sara' lontano dalla sorgente
  2705. di luce.
  2706.  
  2707.  Adesso vediamo come realizzare praticamente il bump mapping.
  2708. L'algoritmo si puo' suddividere principalmente in 2 parti: nella prima
  2709. svolgiamo un precalcolo, ovvero calcoliamo una tabella in memoria; la
  2710. seconda parte consiste nel calcolo vero e proprio della texture "bumpata",
  2711. la quale parte (a differenza della prima) viene svolta in tempo reale.
  2712. Per una singola texture, il precalcolo della tabella viene svolto
  2713. solamente una volta.
  2714.  
  2715.  La tabella occupa esattamente 4 volte lo spazio occupato dalla texture.
  2716. Per ricavarla dobbiamo fare l'intera scansione della nostra immagine.
  2717. Supponiamo che P1 sia l'attuale pixel chunky scansito, che P2 sia il
  2718. successivo punto sulla destra di P1 e che P3 sia esattamente quello posto
  2719. al di sotto di P1, quindi le coordinate di questi 3 punti saranno:
  2720.  
  2721.                 P1( x   , y   )    <- punto attuale
  2722.                 P2( x+1 , y   )    <- punto sulla destra di P1
  2723.                 P3( x   , y+1 )    <- punto al di sotto di P1
  2724.  
  2725. Ecco i calcoli da eseguire:
  2726.  
  2727.                 px = P1-P2         <- differenza di pixel chunky
  2728.                 py = P1-P3         <- differenza di pixel chunky
  2729.  
  2730.                 ox = h*sin(px)
  2731.                 oy = h*sin(py)
  2732.  
  2733.                 of = oy*tx+ox      <- valore della tabella da precalcolare
  2734.  
  2735.                 tx = 256           <- larghezza della texture in pixel
  2736.                 h                  <- valore arbitrario tra 0 e 255
  2737.  
  2738. Da notare che px e py coprono un range di valori tra -255 e +255.
  2739. Nel nostro caso -255 rappresenta -pigreco/2 (ovvero -90 gradi), mentre
  2740. +255 indica pigreco/2 (+90 gradi). Se avessimo una funzione seno che
  2741. accettasse in ingresso un angolo in radianti, sin(px) sarebbe
  2742. equivalente a:  sin(px*512/PI). E' consigliabile calcolare una tabella di
  2743. sin(x) in cui x varia da -90 gradi a +90 gradi formata da 512 valori.
  2744.  
  2745.  La tabella e' composta semplicemente da un insieme di of, e precisamente
  2746. uno per ogni pixel dell'immagine. Of e' un valore a 32 bit, quindi per una
  2747. texture 256*256 la tabella risulta lunga 256kb.
  2748.  Il valore <h> e' il coefficiente di perturbazione, cioe' indica il grado
  2749. dell'effetto bump. In altre parole tanto h sara' maggiore e tanto si
  2750. notera' che la texture e' tridimensionale, tanto h sara' minore e tanto la
  2751. texture risultera' piatta. E qui finisce la fase di precalcolo.
  2752.  
  2753.  Vediamo la parte da svolgere in tempo reale. Innanzitutto ci serve una
  2754. ulteriore texture, la quale verra' utilizzata per "simulare" la luce
  2755. applicata alla texture da bumpare. Questa immagine e' la riproduzione vera
  2756. e propria della luce; in pratica rappresenta una serie di cerchi
  2757. concentrici, di cui il cerchio piu' interno (quindi il piu' piccolo) e'
  2758. riempito con il massimo valore attribuibile ad un pixel chunky (ovvero
  2759. 255), mentre il cerchio piu' esterno (il piu' grande) e' riempito con il
  2760. minor valore attribuibile al pixel chunky (che sarebbe 0). E' possibile
  2761. utilizzare la stessa texture che si applica nel reflection mapping per
  2762. simulare una sorgente di luce. Un buon esempio di questo tipo di immagini
  2763. e' una sfera (di dimensioni arbitrarie) piu' luminosa al centro e resa
  2764. scura ai bordi.
  2765.  
  2766.  Consideriamo di utilizzare 3 variabili (o registri) che puntano
  2767. rispettivamente al buffer dove salvare la bump-texture (che puo' anche
  2768. essere lo schermo chunky), alla texture che rappresenta la luce
  2769. e alla tabella precalcolata.
  2770.  Per ogni byte del buffer della texture da calcolare noi preleviamo
  2771. sequenzialmente un valore dalla tabella precalcolata (ossia un valore
  2772. della tabella per ogni byte del buffer). In seguito utilizziamo questo
  2773. valore come offset da applicare al puntatore della texture che rappresenta
  2774. la luce (ovvero addizioniamo al valore della tabella il puntatore della
  2775. texture coincidente la sorgente di luce) e copiamo il relativo byte nel
  2776. buffer destinazione. Dopo aver svolto il tutto per ogni pixel avremo
  2777. ottenuto il bump mapping.
  2778.  
  2779.  
  2780.                 aPPENDICE a: nOTAZIONE iN vIRGOLA fISSA
  2781. ==========================================================================
  2782.  
  2783.  Nell'elaborazione di oggetti 3D si ha spesso a che fare con numeri reali,
  2784. non interi. La maggior parte dei linguaggi evoluti permettono la diretta
  2785. manipolazione di tali numeri, casomai sfruttando un eventuale
  2786. coproccessore matematico oppure emulandoli via software. L'emulazione
  2787. che apportano gli attuali compilatori risulta decisamente lenta per
  2788. applicazioni in tempo reale, inoltre se si lavora in Assembly non si ha
  2789. a disposizione la diretta gestione di numeri reali a meno che non venga
  2790. utilizzato il coprocessore matematico. In alternativa alla FPU (che
  2791. sfrutta numeri in virgola mobile) si puo' optare per il formato in virgola
  2792. fissa che, nonostante una minore precisione rispetto al formato in virgola
  2793. mobile, rimane la scelta migliore in quanto le operazioni svolte in tale
  2794. formato risultano piu' veloci.
  2795.  In linea di principio, in un elaboratore elettronico, tutti i numeri
  2796. (anche quelli reali) vengono rappresentati come interi, la notazione in
  2797. virgola fissa si basa sulla diretta semplificazione di tale
  2798. rappresentazione, vediamo come.
  2799.  
  2800.  Un numero reale viene rappresentato come quel valore intero dato dal
  2801. prodotto del numero reale moltiplicato per una costante definita a priori.
  2802. E' proprio da questa costante che dipende la precisione con la quale
  2803. possono essere rappresentati numeri non interi. Ecco un esempio:
  2804.  
  2805.            3.25             <- numero reale
  2806.            256              <- costante
  2807.  
  2808.            3.25*256 = 832   <- 3.25 in virgola fissa
  2809.  
  2810. In questo modo possiamo rappresentare tutti i numeri reali con un discreto
  2811. margine di errore che per le nostre applicazioni risulta praticamente
  2812. ininfluente. E' conveniente utilizzare come costante una potenza di 2
  2813. (es.: 256, 65536), con la quale e' possibile velocizzare la manipolazione
  2814. di numeri in tale notazione. Difatti e' noto che il computer rappresenta
  2815. un qualsiasi numero come una sequenza di bit, quindi, utilizzando come
  2816. costante una potenza di 2, e' possibile definire 2 campi di bit per ogni
  2817. cifra: una dedicata alla parte intera mentre l'altra dedicata alla parte
  2818. frazionaria.
  2819.  Se un numero in virgola fissa ha un numero di bit dedicati alla parte
  2820. intera uguale ad <a> e un numero di bit dedicati alla parte frazionaria
  2821. uguale a <b>, allora si dice che quel numero e' nel formato "a:b". Bisogna
  2822. inoltre sfecificare che la parte intera di una cifra in virgola fissa
  2823. appartiene al campo di bit piu' alto, mentre la parte frazionaria
  2824. appartiene al campo di bit piu' basso.
  2825.  
  2826.            3.25        <- numero reale
  2827.            256=2^8     <- costante
  2828.            832         <- 3.25 in virgola fissa (ovvero 3.25*256)
  2829.            8:8         <- formato del numero in virgola fissa. Se
  2830.               utilizziamo 1 word (16 bit) abbiamo gli 8 bit
  2831.               piu' significativi dedicati alla parte intera e
  2832.               gli altri 8 bit meno significativi per quella
  2833.               frazionaria.
  2834.  
  2835.  Vediamo come convertire un numero intero nel formato in virgola fissa e
  2836. viceversa:
  2837.  
  2838.            numero intero        = (numero virgola fissa) / (costante)
  2839.            numero virgola fissa = (numero intero)        * (costante)
  2840.  
  2841.  Infine occupiamoci di comprendere come effettuare le 4 operazioni con
  2842. tali numeri:
  2843.  
  2844.            (a:b) + (c:d) = impossibile!!
  2845.            (a:b) + (a:b) = a:b
  2846.            (a:b) * (c:d) = (a+c):(b+d)
  2847.            (a:b) / (c:d) = (a-c):(b-d)
  2848.  
  2849. Ci accorgiamo subito che risulta impossibile sommare 2 numeri in virgola
  2850. fissa di diverso formato, bisogna prima rendere omogenee le 2 cifre
  2851. (significa che le 2 cifre devono avere lo stesso formato). Da notare che
  2852. una qualsiasi cifra intera puo' essere intesa come un numero in virgola
  2853. fissa nel formato "a:0"; quindi e' possibile svolgere direttamente
  2854. moltiplicazioni e divisioni tra numeri in virgola fissa ed interi.
  2855.  
  2856.  
  2857.                         aPPENDICE b: cOORDINATE pOLARI
  2858. ==========================================================================
  2859.  
  2860.  Come gia' sappiamo, per rappresentare un generico punto su di un piano
  2861. possiamo utilizzare gli assi cartesiani. Le componenti x ed y non
  2862. rappresentano altro che le proiezioni del nostro punto sull'ascissa e
  2863. sull'ordinata.
  2864.  Immaginiamo invece di voler indicare un punto utilizzando un altro
  2865. sistema di riferimento, nel nostro caso le coordinate polari.
  2866.  
  2867.   ^                    Consideriamo r la distanza tra il punto P e
  2868. y |     .P(x,y)        l'origine, mentre t come l'angolo compreso tra il
  2869.   |    /               segmento OP ed il semiasse positivo x.
  2870.   |   /                E' possibile indicare qualsiasi punto utilizzando
  2871.   | r/                 queste 2 variabili (r e t), le quali rappresentano
  2872.   | /                  proprio le coordinate polari.
  2873.   |/) t                Per ogni P(x,y) corrisponde un P'(r,t). Vediamo
  2874.   +------------>       come effettuare queste conversioni.
  2875.  O            x
  2876.                        Sia R(x,0) la proiezione di P(x,y) sull'asse x
  2877.   ^                    (ovvero il punto di ordinata 0 e di equivalente
  2878. y |     .P(x,y)        ascissa di P). Il triangolo ORP e' rettangolo in R,
  2879.   |    /|              quindi per il teorema di Pitagora:
  2880.   |   / |
  2881.   | r/  |                         r = OP = sqrt( x*x + y*y )
  2882.   | /   |
  2883.   |/) t |              Possiamo anche affermare che:
  2884.   +-----+------>
  2885.  O      R     x        PR = r*sin(t)   =>   sin(t) = PR/r
  2886.                        OR = r*cos(t)   =>   cos(t) = OR/r
  2887.  
  2888. Se facciamo attenzione possiamo affermare che (considerando il punto
  2889. P(x,y)) PR=y e OR=x. La tangente di un angolo e' equivalente al rapporto
  2890. del seno di quell'angolo col relativo coseno, quindi:
  2891.  
  2892.         tan(t) = sin(t)/cos(t) = (PR/r)/(OR/r) = PR/OR = x/y
  2893.         x/y = tan(t)
  2894.  
  2895.         t = arctan(x/y)
  2896.         r = sqrt(x*x+y*y)
  2897.  
  2898.         x = r*cos(t)
  2899.         y = r*sin(t)
  2900.  
  2901. Ora sappiamo come convertire le coordinate cartesiane in polari e
  2902. viceversa, cio' risultera' utile per comprendere come effettuare la
  2903. rotazione di un punto.
  2904.  
  2905.  
  2906.                         aPPENDICE c: gESTIONE dEGLI oGGETTI
  2907. ==========================================================================
  2908.  
  2909.  Vogliamo tracciare il nostro oggetto su schermo sia esso in wireframe,
  2910. gouraud shading, texture mapping o in altra tecnica di rendering che piu'
  2911. ci va a genio; sappiamo perfettamente come visualizzare una singola
  2912. faccia, ma come potremmo gestire tutte le facce che compongono il solido
  2913. tridimensionale?
  2914.  Bisogna definire un "formato" con cui l'oggetto risiede in memoria, in
  2915. base al quale potremo visualizzare qualsiasi figura 3D utilizzando sempre
  2916. le stesse routine che compongono il nostro motore 3D. Iniziamo col vedere
  2917. un esempio pratico:
  2918.  
  2919.         V5 _____________ V6     teniamo conto di dover definire come
  2920.          /|            /|       oggetto un semplice cubo, per prima
  2921.         / |           / |       cosa potremmo indicarne il numero di
  2922.        /  |          /  |       vertici e di facce di cui e' composto;
  2923.       /   |         /   |       in seguito elenchiamo tutte le
  2924.   V1 /____|_______ /V2  |       coordinate (x,y,z) dei vertici del cubo;
  2925.     |     |       |     |       infine indichiamo le caratteristiche di
  2926.     |   V8|_______|_____|V7     tutte le facce. Nel caso piu' semplice
  2927.     |    /        |    /        per definire una faccia basta indicarne
  2928.     |   /         |   /         i vertici (casomai ordinati in senso
  2929.     |  /          |  /          orario per facilitare la rimozione
  2930.     | /           | /           dell'hidden face e il calcolo della
  2931.     |/____________|/            normale). Ecco come e' possibile
  2932.   V4               V3           definire in memoria un cubo:
  2933.  
  2934.            8            <- numero di vertici dell'oggetto
  2935.            6            <- numero di facce dell'oggetto
  2936.            -50,-50,-50  <- coordinate x,y,z del vertice V1
  2937.            +50,-50,-50  <- coordinate x,y,z del vertice V2
  2938.            +50,+50,-50  <- coordinate x,y,z del vertice V3
  2939.            -50,+50,-50  <- coordinate x,y,z del vertice V4
  2940.            -50,-50,+50  <- coordinate x,y,z del vertice V5
  2941.            +50,-50,+50  <- coordinate x,y,z del vertice V6
  2942.            +50,+50,+50  <- coordinate x,y,z del vertice V7
  2943.            -50,+50,+50  <- coordinate x,y,z del vertice V8
  2944.            1,2,3,4      <- indici al buffer dei vertici della faccia 1
  2945.            2,6,7,3      <- indici al buffer dei vertici della faccia 2
  2946.            6,5,8,7      <- indici al buffer dei vertici della faccia 3
  2947.            5,1,4,8      <- indici al buffer dei vertici della faccia 4
  2948.            5,6,2,1      <- indici al buffer dei vertici della faccia 5
  2949.            4,3,7,8      <- indici al buffer dei vertici della faccia 6
  2950.  
  2951. Analizziamo come vengono definiti i poligoni, prendiamo la faccia 1:
  2952.  
  2953.            1,2,3,4      <- significa che la faccia e' composta dai primi 4
  2954.                            punti posti nella lista dei vertici, ovvero:
  2955.  
  2956.            -50,-50,-50  <- coordinate x,y,z del vertice V1
  2957.            +50,-50,-50  <- coordinate x,y,z del vertice V2
  2958.            +50,+50,-50  <- coordinate x,y,z del vertice V3
  2959.            -50,+50,-50  <- coordinate x,y,z del vertice V4
  2960.  
  2961.            2,6,7,3      <- significa che la faccia e' composta dai lati
  2962.                            delimitati dai punti 2 e 6, 6 e 7, 7 e 3 e dai
  2963.                            punti 3 e 2.
  2964.  
  2965. Ogni lato e' rappresentato dalla congiunzione lineare di 2 vertici,
  2966. nell'esempio appena proposto i lati della faccia 1 sono i segmenti
  2967. delimitati dal primo al secondo punto (V1 e V2), dal secondo al terzo (V2
  2968. e V3), dal terzo al quarto (V3 e V4) e dal quarto al primo punto (V4 e
  2969. V1).
  2970.  Nel nostro caso ogni faccia e' un quadrilatero, naturalmente e' possibile
  2971. realizzare il proprio motore 3D che utilizzi un diverso numero di lati,
  2972. l'importante e' che ogni poligono sia convesso, altrimenti l'algoritmo di
  2973. scan conversion risulterebbe decisamente piu' complesso di quello studiato
  2974. nel paragrafo relativo al fill e scan line.
  2975.  
  2976.  
  2977.  
  2978.