Computer Graphics
Marco Tarini
Università dell’Insubria Corso di Laurea in Informatica Anno Accademico 2014/15
Modellazione procedurale di semplici forme
Preambolo: piccolo prontuario di JavaScript
• JavaScript ha molti difetti – non tipato
– sintassi molto disinvolte – molto facile sbagliare
– (tradizionalmente) interpretato, no compilato
• Allora perchè adottarlo – WebGL
• modo migliore di ottenere 3D sul Web!
• no plug-ins!
– su browser!
• estrema semplicità di deployment
• cross platform
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
Preambolo: piccolo prontuario di JavaScript
• 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++
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
Piccolo prontuario di JavaScript:
classi e istanze
• In Java faremmo: • In JavaScript faremo:
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
class Person {
public int age;
public string name;
}
{ ...
Person caio;
caio.age = 32;
caio.name = “caio”;
...
}
var Person {
age : 0, name : “”
}
{ ...
var caio = Object.create( Person );
caio.age = 32;
caio.name = “caio”;
...
}
Preambolo: piccolo prontuario di JavaScript
• 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.
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
Piccolo prontuario di JavaScript:
dichiarazione funzioni (e metodi)
• In Java faremmo:
– il compilatore:
• sa che il paramatro p è di tipo Persona
• controlla che la classe persona abbia il campo age
• In JavaScript faremo:
– 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
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
void invecchia( Persona p )
{
p.age ++;
}
function invecchia( p ) {
p.age ++;
}
Preambolo: piccolo prontuario di JavaScript
• 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
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
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 (1849–1916)
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
« Se vedo (in esecuz) che una var p ha un campo age , e vedo questo campo capace di subire l’op ++ , allora è ok passare p alla funzione invecchia (che sia una Persona o no) »
JavaScript interpreter (1995– )
Piccolo prontuario di JavaScript:
member functions (cioè methods)
• In Java faremmo: • In JavaScript faremo:
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
class Person {
public int age;
public string name;
public
void setBirthDate( int k ) { this.age
= 2015 –k;
} }
{
...Person caio;
caio.setBirthDate( 1985 );
...
}
var Person {
age : 0 , name : "" ,
setBirthDate: function( k ) { this.age
= 2015 –k;
} }
{
...var caio
= Object.create(Person );
caio.setBirthDate( 1985 );
...
}
Preambolo: piccolo prontuario di JavaScript
• 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 = 32;
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)
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
<html>
<head>
<script src="person.js"></script>
<script>
</script>
</head>
<body> … </body>
</html>
Piccolo prontuario di JavaScript:
dividere il progetto in più files
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a ...
var caio = Object.create( Person );
caio.setBirthDate( 1985 );
...
var Person {
age : 0 , name : “” ,
setBirthDate: function( k ) { this.age = 2015 – k;
} }
file: person.js
es: un file per ”classe” (alla Java)
file:index.html
Una classe per le mesh (anzi due)
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
var CpuMesh {
/* campi */
verts : new Float32Array, // geom + attr tris : new Uint16Array, // connetività
/* metodi per riempire i campi */
makeCube: function() { … } makeCone: function( res ) { … } makeCylinder: function( res ) { … }
}
file: mesh.js
• La prima: per mesh in system memory – usata per lavorarci in CPU
– (es, costruzione procedurale, preprocessing)
Una classe per le mesh (anzi due)
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
var GpuMesh {
/* campi */
bufferVerts : 0 , bufferTris : 0 ,
nTris : 0 , // devo ricordare quanti sono
/* metodi */
draw: function() { … }
storeFromCpu: function( cpuMesh ) { … } }
file: mesh.js (proseguo)
• 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
Classe CpuMesh
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
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)
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
var CpuMesh {
…
/* metodi (che sarebbero) “privati” */
// 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+1 setQuad: function( i, va,vb,vc,vd ) { … }
// setta il vertice i-esimo
setVert: function( i, x,y,z, r,g,b) { … } }
1 quad = 2 tris (diagonal split)
Un cubo: geometria e connettività
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a 1 4 5
2 6
0 7
2
6 7
3 1 4 5
0 y
z x
2
7
3 1 6
5
0
2
6 7
3 1 5
0
2
6 7
1 4 5
0
Un cono: geometria e connettività
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
2 1
0
y
z x
… 3
y = -1 y = +1
N-1
(N vertici di base, +1 di apex) N
…
Note:
• N vertici nella base
– (numerati da 0 a N -1 )
• 1 vertice di apex
– (di indice N )
• sulla base, chi è il successore del vertice i ? risposta:
( i + 1 ) modulo N (i+1)%N
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
( con 0 ≤ i < N )
Un cilindro: geometria e connettività
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
2 1
0
y
z x
4 3
y = -1 y = +1
N-1
(N vertici base inf, N in base sup) + N+2 N+1
N N+4 N+3
2N-1
Scena con oggetti multipli (piano)
• 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
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
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):
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
// fragment shader
void main() {
float fog = gl_FragCoord.z;
fog *= fog; fog *= fog;
fog = 1.6-fog;
gl_FragColor = vec4( fog,fog,fog, 1.0);
}
in coord Viewport.
x e y: pos del pixel z: profondità
(0 = min, 1 = max) per escerbare l’effetto
Linking di vertex e fragment shader
• Vertex Shader:
– input:
• gli attributes
• gli uniforms
– output:
• i varyings
• gl_Position (vec4)
• Fragment Shader:
– input:
• i varyings
• gli uniforms (gli stessi)
• gl_FragCoord (vec4) – output:
• gl_FragColor (vec4)
• gl_Depth (float) (opzionale)
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a
LINK
Per i dettagli,
vedere l’implementazione sul sito (come al solito):
lez 20
M a r c o T a r i n i ‧ C o m p u t e r G r a p h i c s ‧ 2 0 1 4 / 1 5 ‧ U n i v e r s i t à d e l l ’ I n s u b r i a