Salve a tutti!
Ricapitoliamo un pò il nostro operato: per il momento siamo capaci di disegnare figure e di assegnare ad esso dei colori base. Al momento però la nostra scena è piuttosto statica: a parte qualche variazione di colore proposto nell’esercizio, tutto rimane fisso ed invariato, come porvi rimedio?

ESEMPIO TRE: DISEGNARE UN POLIGONO IN MOVIMENTO

Per dare l’idea di movimento , l’idea più intuitiva che viene in mente è di ridisegnare ogni tot secondi il nostro poligono leggermente spostato rispetto al frame precedente, se l’intera operazione viene eseguita con con una velocità abbastanza ragionevole, il nostro occhio ci farà risultare il tutto con un momento continuo.
Per tale ragione dovremmo modificare sia la funzione DrawScene(), sia introdurre una nuova funzione che chiameremo Animation().Nel nostro esempio dovremmo disegnare un triangolo colorato che ruota su se stesso in senso antiorario.

Prima di tutto introduciamo una nuova variabile:


var alfa=0;


Guardando la figura, si capisce che alfa rappresenta l’angolo ( definito in gradi) tra l’altezza “a” e l’asse orizzontale: al suo incremento, il triangolo ruoterà in senso antiorario. La variazione di tale elemento passerà attraverso una funzione che verrà richiamata come funzione ROUTINE DEL WEBGL:


function IncrementRotation()
{
alfa+=2;
}

function tick()
{

drawScene();
IncrementRotation();
}

La funzione IncrementRotation() non fa altro che incrementare regolarmente di due gradi il nostro angolo ad ogni chiamata dlele funzioni tick ( la routine).
Consiglio di aumentare l’intervallo di tempo tra una routine e l’altra per non appensantire troppo l’applicazione, passando per esempio da 15ms a 45-50ms:

function Start() {
var canvas = document.getElementById(“Mycanvas”);
initGL(canvas);
initShaders();
initBuffers();
setInterval(tick, 45);
}

Adesso toccherebbe ricalcolare la posizione dei vertici del nostro poligono a causa della rotazione, un ‘operazione piuttosto onerosa se fatta manualmente. Fortunatamente esiste già una serie di metodi che ci semplificano la vita ( al momento ci conviene sorvolare su queste funzioni, le useremo e basta). Questa più altre funzioni correlate all’animazione dovranno essere implementate dentro il nostro codice: consiglio di inserire tale definizioni nel nostro file MatrixUtilityFunc.js, di seguito riporto il codice che dovrete copia & incollare; se non toccare il codice del file, sul repository Lab è stato aggiornato la nuova versione del file MatrixUtilityFunc.js, vi consiglio di utilizzare quello :

var mvMatrixStack = [];

function mvPushMatrix(m) {
if (m) {
mvMatrixStack.push(m.dup());
mvMatrix = m.dup();
} else {
mvMatrixStack.push(mvMatrix.dup());
}
}

function mvPopMatrix() {
if (!mvMatrixStack.length) {
throw(“Can’t pop from an empty matrix stack.”);
}

mvMatrix = mvMatrixStack.pop();
return mvMatrix;
}

function mvRotate(angle, v) {
var inRadians = angle * Math.PI / 180.0;

var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4();
multMatrix(m);
}

La funzione mvRotate non fa altro che convertire l’angolo da gradi a radianti ed invocare la rotazione attorno ad una retta che fa da asse (la cui direzione è rappresentata dall’array v=array[a,b,c]  ed il poligono ci gira intorno a tale segmento come  se fosse una trottola).


E le funzioni Push e Pop a cosa servono? Per dirla in poche parole, poiché esiste una sola matrice che identifica la trasformazione da 3d(scena) a 2d(viewport), si utilizza il metodo Push per salvare lo stato della matrice prima che vengano eseguite delle modifiche su essa ,mentre il metodo Pop è chiamato per riportare la matrice allo stato precedentemente salvato. Semplificando:

1. PUSH ( salvo matrice)
2. FACCIO LE MIE TRASLAZIONI, ROTAZIONI,ETC…
3. POP ( rimetto la vecchia matrice)

Per fare un altro esempio molto semplice, se il mondo da rappresentare fosse solo 2d e concentrato su grande quadrato, e se volessi posizionare due oggetti su due vertici opposti di tale poligono , dovrei seguire tale schema:

1.LoadIdentity()-> carico matrice Identità, in pratica il mio poligono è posizionato nel punto di coordinate {0;0;0} .
2.Push-> salvo stato
3. translate(Oggetto1,PosizioneSulVerticeArispettoAlCentro)
4. Disegno Oggetto1
5.Pop → ricarico vecchia matrice (Identità)
6.Push-> salvo
7. translate(Oggetto2,PosizioneSulVerticeArispettoAlCentro)
8.Disegno Oggetto2
9.Pop → ricarico vecchia matrice (Identità)

Insomma, la nostra drawScene sarà più o meno la seguente:

function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);

loadIdentity();

mvPushMatrix();
mvRotate(alfa, [0,0,1]);

mvTranslate([0.0, 0.0, -6.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER,triangleVertexColorBuffer);
gl.vertexAttribPointer(vertexColorAttribute,4,gl.FLOAT,false,0,0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);

mvPopMatrix();

}

Guardando la funzione, ad alcuni potrebbe sorgere una domanda: come mai, nonostante la richiesta sia solo di vedere il triangolo ruotare, è stato inserito una funzione di traslazione( mvTranslate)?
Il motivo è semplice:  senza tale spostamento, noi non lo potremmo vedere il triangolo ruotare , perchè ruoterebbe attorno a noi.
Un modo parallelo per capire la situazione è immaginare che l’osservatore ( cioè noi) sia sdraiato fermo su un pavimento triangolare che rispetto a noi si muove e il nostro sguardo è rivolto in cielo! Perciò i metodi necessari per vedere il poligono sono due: o cambiamo direzione in cui volgiamo il nostro sguardo oppure “alziamo il pavimento al cielo”.
Con quella traslazione noi optiamo per la secondo ipotesi: spostando il triangolo verso l’asse Z negativo non facciamo altro che spingere il triangolo verso il “fondo” dello schermo.
Carichiamo la nostra pagine e dovremmo avere un output del genere:

Di seguito il link al nostro esempio appena llustrato:
Esempio tre

-Proposta di Lavoro:
Realizzate una pagina html di esempio dove vi è un triangolo colorato che ruota. In essa un utente può variare:

1.la direzione  ( oraria o antioraria, per esempio  tramite checkbox)
2.velocità di rotazione ( da 1 a  10, per esempio tramite  selectbox)

Inoltre, il triangolo dovrebbe avere la possibilità di  spostarsi anche lungo la componente Z in maniera ciclica, cioè andando avanti e indietro a velocità costante ( magari inserire una checkbox per abilitare o no tale spotamento).

Per potere vedere la soluzione dell’esercizio ed il suo codice, collegarsi al solito repository Lab , lo troverete nella sezione Esercizi salvato con il nome exercise02.html.

Grazie per l’attenzione,

Andrea