Come nelle precedenti vacanze estive dove ho giocato con l’elemento canvas per creare un effetto demo old-school, anche per questo periodo di feste che, ahimè, per il sottoscritto feste non sono affatto state, ho volute rifarmi con un secondo effetto grafico classico che visualizzi i mostri che la mia ragione ha creato dopo la 48 ore Natale-Santo Stefano a tavola.
Sto parlando del cosiddetto plasma effect. Si tratta di un classico della computer grafica che ancora non ho visto implementato con canvas, il che è di per sè un sacrilegio. Ma al di là di questa disquisizione puramente personale vorrei passare ai fatti e dimostrare che in qualche modo canvas può essere usato a fini decorativi come si può vedere nell’esempio finale animato per creare titoli ad effetto. Qui di seguito uno screenshot:
Non essendo affatto un genio matematico mi sono basato su implementazioni di altri programmatori ben più esperti di me portando il codice in Javascript. Chissà che a furia di leggere codice degli altri riesca a prendere dimestichezza su certe cose.
Implementazione teorica
Come per il precedente esempio anche in questo caso si tratta basilarmente di lanciare una funzione che “spazzoli” il nostro canvas creando proceduralmente (ovvero calcolati matematicamente) tutti i punti della nuova immagine. Ovvero prendiamo ogni punto dell’immagine e gli assegnamo un colore, per approfondimento su come accedere ai pixel rimando all’articolo di Mozilla Hack.
L’effetto plasma a grandi linea si basa sul calcolo dei colori di ogni pixel in base ad una funzione di rumore generata dalla somma di diverse curve. Informandomi da profano sull’argomento, questo genere di rumore è chiamato Perlin Noise che per chi ha dimestichezza di computer grafica la si incrocia spesso nella generazione delle texture in 3D.
Come tutti ci ricordiamo dai nostri studi di trigonometria, una sinusoide è una curva che va da -1 a 1 in un determinato periodo di tempo. Perciò se agli estremi mettiamo invece di -1 e 1, i valori 0 e 255 abbiamo un parametro che varia il suo valore da 0 a 255 a seconda del momento in cui lo leggiamo, il che se applicato ad uno dei canali RGB, crea un gradiente ripetuto.
Se poi mentre avanziamo variamo la frequenza della sinusoide, il nostro gradiente comincia ad avere una aspetto meno ripetitivo. Infine se calcoliamo R, G e B con parametri differenti l’uno dall’altro a frequenze differenti, creiamo un bel casino che è proprio quello che vogliamo fare!.
Ovviamente se la trasformazione viene legata alla posizione di x e di y l’effetto prende una forma bidimensionale più o meno a macchie. Continuando a ripetere più volte al secondo l’operazione incrementando il valore che indica il tempo passato, abbiamo il nostro effetto plasma.
Javascript
A livello di implementazione in Javascript avevo paura che come per l’effetto fuoco, il tutto risultazze di una lentezza esasperante. Fortunatamente in questo caso il valore dei colori non è legato al valore precedente del canvas così che non dobbiamo leggere il valore del pixel da computare ma solamente calcolare il valore nella determinata posizione. Ci evitiamo in questo modo quella che sembra l’operazione più dispendiosa del canvas, ovvero l’accesso all’array dei pixel in lettura (ricordiamo che si tratta di un array monodimensionale).
Comunque sia, onde evitare sorprese mi sono premurato, per prima cosa di evitare l’accesso diretto all’array dei pixel facendone una copia (come da commento di Massimiliano nell’articolo scorso) e riassegnandolo prima di aggiornare il canvas:
var bufferData = buffer.data;
//... manipoalazione dell'array bufferData //
buffer.data = bufferData;
b.putImageData(buffer, 0, 0);
Secondariamente invece di disegnare su un canvas di grandi dimensioni, utilizzo un canvas secondario nascosto come buffer di dimensioni molto più piccole ma proporzionali. Una volta calcolato il nuovo frame ingrandisco il risultato copiandolo nel canvas più grande. Essendo la copia una operazione relativamente veloce, ho lavorato su un numero esiguo di pixel “spalmando” il risultato su una superficie più grande:
// buffer: width = 10, height = 10
// canvas: width = 300, height = 300
// manipolazione del buffer (vedi sopra)
b.putImageData(buffer, 0, 0);
// copia del buffer ne canvas
c.drawImage(buffCanvas, 0, 0, width, height);
Per fare più scena ed ingrandire ulteriormente l’area del canvas, ho voluto integrare una specie di effetto caleidoscopio. Invece di copiare l’immagine una volta l’ho copiata 4 volte facendo un flip della stessa sull’asse centrale con un effetto un po’ “video anni ’60”.
Il tocco dell’artista panciuto
Ora che abbiamo il nostro sogno psichedelico al plasma nel canvas. Cosa ci possiamo fare? Per farla veloce: ho creato una png bucata in Photoshop mettendoci un po’ di fantasia ispirata dalla mia situazione mangereccia e tramite un CSS l’ho posizionata al di sopra del canvas stesso mascherandolo (avrei potuto farlo attraverso javascript con effetti ancora più strabilianti, ma a voi l’implementazione).
Ed ecco quello che potrebbe essere il titolo del nostro prossimo post dedicato alla generazione freak e all’acido lisergico da panettone. A voi l’esempio dove potete togliere e mettere la maschera. Nel sorgente pagina c’è il codice completo.