Computer Graphics Università dell Insubria Corso di Laurea in Informatica Anno Accademico / Marco Tarini Modellazione procedurale di semplici forme JavaScript ha molti difetti non tipato sintassi molto disinvolte molto facile sbagliare (tradizionalmente) interpretato, no compilato Allora perchè adottarlo WebGL modo migliore di ottenere D sul Web! no plug-ins! su browser! estrema semplicità di deployment cross platform classi e istanze Cenni storici: origins: Netscape non parente di Java (neanche alla lontana) targeted at non-professionist programmers JavaScript non è parte degli scopi del corso Cercheremo di usare solo i costrutti che siano più facili da comprendere partendo da Java / C++ In Java faremmo: class Person public int age; public string name; Person caio; caio.age = ; caio.name = caio ; In JavaScript faremo: var Person age :, name : var caio = Object.create( Person ); caio.age = ; caio.name = caio ;
dichiarazione funzioni (e metodi) Java: una classe (Persona), e le sue istanze (caio) (ogni istanza avrà i suoi campi) JavaScript: definiamo un oggetto stampino (Persona) istanziato, con i suoi campi e lo duplichiamo per avere oggetti istanze (non è l unico modo, o il più diffuso, o il migliore) (solo il più Java-like) nota: istanze e classi sono entrambi oggetti JavaScript per approfondire: prototypes, funzioni come oggetti. In Java faremmo: void invecchia( Persona p ) p.age ++; il compilatore: sa che il paramatro p è di tipo Persona controlla che la classe persona abbia il campo age In JavaScript faremo: function invecchia( p ) p.age ++; il compilatore: non sa cosa sia p (oggetto, numero, array?) l interprete (in esecuz): se la funzione invecchia viene chiamata su qualcosa che non è un oggetto (es, è un int) o non ha il campo age, genera un errore Java: il complitatore sa il tipo (classe, o tipo base) di ogni variabile (o espressione, parametro, campo, etc) JavaScript: a tempo di compilazione non è noto il tipo (oggetto, o tipo base) di una var (o espressione, parametro, campo, etc) (il tipo di una var può anche variare nel corso della sua vita) (es aggiungendo dinamicamente campi) (ma noi non lo faremo vogliamo essere Java-like) a tempo di esecuzione si genera un errore se una variabile non è usata in modo coerente col suo tipo attuale Questo modo di fare le cose si chiama «duck typing» «When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck» James W. Riley, poeta (89 96) «Se vedo (in esecuz) che una varpha un campo age, e vedo questo campo capace di subire l op++, allora è ok passare p alla funzione invecchia (che sia unapersona o no)» JavaScript interpreter (99 )
member functions (cioè methods) In Java faremmo: class Person public int age; public string name; public void setbirthdate( int k ) this.age = k; Person caio; caio.setbirthdate( 98 ); In JavaScript faremo: var Person age :, name : "", setbirthdate: function( k ) this.age = k; var caio = Object.create( Person ); caio.setbirthdate( 98 ); Oggetti stampino e oggetti copie sì: si copiano anche i metodi (membri che sono funzioni). Pazienza (non ci interessa imparare a far meglio su JS) no: non sarebbe necessario mettere i campi nello stampino. caio.age = ; funzionerebbe anche se age non esistesse prima (verrebbe aggiunto al volo, solo in questa istanza). (lo facciamo solo per chiarezza, e per manteresi Java-like) convenzione: useremo nomi con la Maiuscola per gli oggetti che fanno da classi (usati come stampini) useremo nomi con la minuscola per gli oggetti che fanno da istanze (usati come copie) dividere il progetto in più files <html> <head> <script src="person.js"></script> <script> var caio = Object.create( Person ); caio.setbirthdate( 98 ); </script> </head> <body> </body> </html> file: index.html var Person age :, name :, setbirthdate: function( k ) this.age = k; file: person.js es: un file per classe (alla Java) Una classe per le mesh (anzi due) La prima: per mesh in system memory usata per lavorarci in CPU (es, costruzione procedurale, preprocessing) var CpuMesh /* campi */ verts : new FloatArray, // geom + attr tris : new Uint6Array, // connetività /* metodi per riempire i campi */ makecube: function() makecone: function( res ) makecylinder: function( res ) file: mesh.js
Una classe per le mesh (anzi due) La seconda: per mesh in video memory usata per madarla al pipeline GPU contiene: indici per riferirsi ai buffer allocati in Video RAM si costruisce a partire dalla classe precedente var GpuMesh file: mesh.js (proseguo) /* campi */ bufferverts :, buffertris :, ntris :, // devo ricordare quanti sono /* metodi */ draw: function() storefromcpu: function( cpumesh ) Classe CpuMesh var CpuMesh /* costruzione procedurale: funz che fanno diventare questa mesh un */ makecube: function() // solo sup laterale (per ora) // approssimando con un prisma a res lati makecylinder: function( res ) // idem makecone: function( res ) // etc Funzioni helpers (per aiutarci nella costruzione procedurale) var CpuMesh /* metodi (che sarebbero) privati */ Un cubo: geometria e connettività 6 7 y // prepara spazio per tot vertici e triangoli allocate: function( nverts, ntris ) // setta il triangolo i-esimo come (va,vb,vc) settri: function( i, va,vb,vc ) // setta due triangoli, di indici i e i+ setquad: function( i, va,vb,vc,vd ) // setta il vertice i-esimo setvert: function( i, x,y,z, r,g,b) quad = tris (diagonal split) 6 7 x 6 7 z
Un cono: geometria e connettività Note: N N- y = + y = - y (N vertici di base, + di apex) x z N vertici nella base (numerati da a N - ) vertice di apex (di indice N ) sulla base, chi è il successore del vertice i? risposta: ( con i < N ) ( i + ) modulo N (i+)%n Un cilindro: geometria e connettività Scena con oggetti multipli (piano) N+ N+ N+ N+ N- N- N y = + y = - y (N vertici base inf, + N in base sup) x z Nella preparewhattodraw Creare tre GpuMesh (cono, cilindro, cubo) (attraverso CpuMesh usa-e-getta) Aggiungere matrice di modellazione cumularla nella matrice MVP (reminder: porterà ciascuna istanza di mesh dal suo spazio oggetto, in cui l abbiamo definito, al world space comune) Nella draw: ( set MVP, draw object ) x enne volte
Effetto «fog» (aspettando il lighting) Idea: tanto più un frammento è in profondità, (Z in spazio vista, o clip, o viewport) tanto più lo coloriamo scuro Implementazione (un hack): // fragment shader void main() float fog = gl_fragcoord.z; fog *= fog; fog *= fog; fog =.6-fog; gl_fragcolor = vec( fog,fog,fog,.); in coord Viewport. x e y: pos del pixel z: profondità ( = min, = max) per escerbare l effetto Linking di vertex e fragment shader Vertex Shader: input: gli attributes gli uniforms LINK output: i varyings gl_position (vec) Fragment Shader: input: i varyings gli uniforms (gli stessi) gl_fragcoord (vec) output: gl_fragcolor (vec) gl_depth (float) (opzionale) Per i dettagli, vedere l implementazione sul sito (come al solito): lez 6