Arte Generativa

( generative art )

Stella inattivaStella inattivaStella inattivaStella inattivaStella inattiva
::cck::72::/cck::
::introtext::

Questo Programma, che ho scritto per studio e ricerca, rientra nella categoria della "Generative Art" ovvero algoritmi scritti per generare immagini nelle arti visive digitali.

Potete usarlo in modo interattivo nel riquadro sottostante.

Nel seguito del post, una rapida guida all'uso del programma e la descrizione del modello matematico e del codice.

::/introtext::
::fulltext::

Rapida Guida agli oggetti interattivi del programma: 

Nota: gli oggetti mostrano la loro icona (rappresentata da una ellisse e una descrizione) quando passate il mouse sopra e la nascondono quando spostare gli oggetti o osservate il disegno creato.

L'oggetto principale è il generatore di Onde, appare al centro della finestra ma potete spostarlo con il mouse. E' facile vederlo perché è il primo oggetto che emette le onde colorate. 

Esistono altri due oggetti che non emettono onde ma le "distorcono". Li ho chiamati "Lente Gravita" che genera un effetto di lente gravitazionale in quanto attira i raggi sul bordo dell'area e li repelle nel centro, e L'altro "Attrattore" che attira verso il centro i raggi. Nelle figure seguenti li potete vedere quando ci passate sopra con il mouse, si trovano nella finestra del programma e li riconoscerete per l'effetto che generano sui raggi. Con il Mouse potere strascinarli nella finestra e osservare l'effetto che hanno sull'immagine.

 Se li sovrapponete i loro effetti si contrastano creando disegni differenti.

 Versione con un ulteriore oggetto: "Repulsore sui bordi" lo potete lanciare da qui: istanza "ONDA ELETTRONICA CON BORDI A 100 PIXEL"

Descrizione Algoritmo.

Il modello matematico implementato è molto semplice. L'idea è di generare un raggio colorato dalla posizione x,y che si proietta verso una direzione definita da un angolo 'a'.

Tale angolo viene generato in modo continui da 0 a 4PI e il raggio invece di essere una semplice linea connessa da due vertici e una curva connessa da un certo numero di vertici che si animano seguendo il seguente modello matematico:

Ogni vertice è spostato nel vettore direzione definito da 'a' di un certo 'step' e shiftato in funzione di un valore di "phase" che viene generato in modo "casuale morbido" ovvero usando il metodo di Perlin Noise.

I valori di colore sono allocati usando il modello "luminosita,saturazione,blillantezza,alfa" e varinao in funzione della phase. Le classi che ho scritto per questo programma sono le seguenti:

public Class EmettitoreRaggiElettronici

public Class RaggioElettronico

public abstract class Attrattore 

public class MouseAttractor extends Attrattore

public class LenteGravitaAttractor extends MouseAttractor

public class InverseAttractorFrameBorder extends Attrattore

Questo attrattore è in realtà un repulsore ovvero un attrattore all'inverso e lo uso per controllare che i raggi restino dentor i limiti della finestra del programma.

Nell'istanza precedento non l'ho abilitata ma potete vederla in esecuzione in questa istanza "ONDA ELETTRONICA CON BORDI A 100 PIXEL". Provate a spostare l'emettitore verso i bordi per notare che i raggi restano entro i 100 pixel dai bordi e cambiano la loro phase diventando simili a stringhe di plasma elettrico.

::/fulltext::

Write comment (0 Comments)

Stella inattivaStella inattivaStella inattivaStella inattivaStella inattiva
::cck::69::/cck::
::introtext::
Per usare la simulazione vai nel seguito di questo post e usa il mouse per seminare la sostanza B nella sostanza A.

Intro

Questo Programma simula la reazione di tre sostanze chimiche A,B,e C che interagiscono tra loro in modi differenti. Rientra nel genere software della "GENERATIVE ART" che spesso si avvale di modelli matematici e rientra nei software si simulazione scientifica che generano immagini in un certo senso "artistiche" o anche definite come "arte procedurale". L'ho scritto usanto un linguaggio  p5js e lo potete usare qui on line. Nel seguioto del post trovate il modello matematico e il codice velocemente commentato. Potete lasciare un commento inviando email a Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.

 

 

::/introtext::
::fulltext::

Usa il mouse (pulsante sinistro e trascina) sulla seguente immaggine per seminare la sostanza B (sarà di colore blu elettrico) dento la sostanza A (che è lo sfondo) e osserva la simulazione.

Scritto da Giuseppe Gi.

Il modello matematico originale della "REACTION DIFFUSION" utilizza solo due sostanze la A e la B. In pratica una matrice piena di sostanza A (in cui ogni elemento della matrice contiene valori che vanno da 0.0 a 1.0 per indicare che percentuale di A è presente nel singolo elemento della matrice) viene fatta interagire con una matrice di sotanza B inizialmente vuota. Seminando alcuni elementi della matrice B l'algoritmo calcola l'espansione della Sostanza B dentro la sostanza A. Tale espansione però è sfumata e dinamica in quanto per ogni elemento della matrice viene calcolata la percentuale di sostanza B che si espande (usanto il laplaciano degli elemenenti adiacenti e una velocita di espansione) e la percentuale di A (che si contrare opponendo una certa resistenza e usando il laplace dei suoi adiacenti (per dettagli sulla convoluzione puoi vedere questo mio artico sul trattamento immagini).

A questo ho aggiunto una terza sostanza C che interaggisce ritraendosi in presenza di B e dissolvendosi lentamente in presenza di A. Ho inoltre creato delle zone in cui la "velocità di espansione" e la "resistenza di contrazione" non sono uniformi ma variano sia nello spazio della matrice sia nel tempo tra i fotogrammi generando disegni più complessi. Tale variazione l'ho generata usando il Perlin Noise che assegna valori alle matrici di espansione e contrazione, e aggiornando tali matrici ogni n secondi in modo che gli schemi dei disegni continuino a modificarsi.

Riferimenti bibliografici e articoli sul modello matematico e formule da cui questo programma trae ispirazione:

https://en.wikipedia.org/wiki/Reaction%E2%80%93diffusion_system 

http://www.karlsims.com/rd.html

 

 

Commento del codice sorgente:

Le matrici che contengono, per ogni elemento_ij, la percentuale di sostanza presente al tempo t sono innestate nell'oggetto grigliaAttuale[][] che contiene tre puntatori a tre matrici di sostanze chiamate a,b,c;

var grigliaAttuale=[];

Al tempo t+1 verrà calcolata la nuova configurazione delle sostanze che tra loro hanno reagito. Tali matrici sono innestate nell'oggetto grigliaProssima[][].

var grigliaProssima=[];

Ogni sostanza si espande o si contrare dal tempo t al tempo t+1 con una certa velocità di espansione e un certo valore di resistenza che chiamiamo feed e k e che, nel modello matematico originale sono delle costanti che si applicano o a tutti gli elementi della matrice o che si applicano solo a certe zone della matrice.

Nella mia implementazione invece ogni elemento della matrice ha un feed e un k dedicato ed esso è calcolato utilizzando l'algoritmo di "perlin noise" che permette di transitare da un valore a uno adiacente in modo casuale ma morbido ovvero senza una transisione brusca. Ciò consente nel mio programma di avere delle zone con rapida espansione che "sfumano" in zone con maggiore resistenza all'espansione. Tale matrice di Velocità e resistenza poi viene aggiornata ogni n fotogrammi in modo da rendere ancora più dinamica l'animazione delle sostanze che continuano sempre a "mescolarsi" tra loro, come se lo spazio in cui sono immerse avesse correnti o meglio avvallamenti che implodono ogni n fotogrammi.

Quindi nel codice dichiaro un puntatore alle matrici feed e k che vanno viste come la velocità di espansione della sostanza A e la velocità di rimozione della sostanza B.

var grigliaVelocita=[];

Il fattore di diffusione della sostanza A e B non sono uguali e li dichiaro come segue:
var dA=1;
var dB=0.5;

Definisco alcuni oggetti per gestire il perlin noise che mi permetteranno sia di riempire le matrici feed e k di valori che vanno da 0.0 a 1.0 e sia variabili che mi permettono di animare tale perlin noise a intervalli abbastanza lunghi da poter apprezzare le animazioni delle sostanze diffondersi tra loro.

var timerRicreazioneNoise=[];
var xoff=0.0;
var yoff=0.0;
var noiseScale=0.004;

 

La funzione setup inizializza sia le variabili che le matrici che il contesto grafico

function setup() {
createCanvas(400, 400);
pixelDensity(1);
background(100);

definisco due costanti per il feed e per il k che indicano il valore minimo e massimo che il perlin noise potrà usare per generare le griglie di velocità.
feed={ min:0.01,max:0.09};
k={min:0.052,max:0.07};
timerRicreazioneNoise={counter:0,time:1000};

inizializzo le matrici
for (var x=0;x<width;x++){
grigliaAttuale[x]=[];
grigliaProssima[x]=[];
grigliaVelocita[x]=[];
for(var y=0;y<height;y++){

ogni elemento delle griglie delle sostanze punta a tre sostanze differenti la A, la B e la C

grigliaAttuale[x][y]={ a: 1 , b:0, c:1};
grigliaProssima[x][y]={ a:0 , b:0, c: 0};
la griglia delle velocità viene riempita con valori generati dal perlin noise e mappati tra il minimo e massimo valore di feed e di k in base al valore che il perlin noise restituisce che è compreso tra 0.0 e 1.0


grigliaVelocita[x][y]={feed:map(noise(x*noiseScale,y*noiseScale),0.0,1.0,feed.min,feed.max) , k:map(noise(x*noiseScale,y*noiseScale),0.0,1.0,k.min,k.max)};

se volessi visualizzare la griglia delle velocità dove i valori più scuri sono i minimi e i valori più chiari sono i massimi assomiglierebbe alla seguente immagine. La scala per perlin noise può essere variata, in questo caso abbiamo una scala abbastanza grande ovvero 0.02. La seconda immagine è quella con una scala più piccola quindi appare come ingrandita ed è 0.004

perlinNoise1perlinoise2

 Le zone più chiare applicheranno una differente resistenza e espansione rispetto alle zone più scure quindi ogni elemento delle matrici avrà la sua personale resistenza e espansione da appicare alle sostanze che interaggiscono tra di loro ma le variazioni di tale resistenza e espansione sono sfumate tra le varie zone.

 

 

 

 

 

 

 

 

 

}

}

}

Il programma ha la funzione per disegnare la sostanza B dentro la sostanza A e aggiungere anche la sostanza C con il tasto centrale e quello Destro.

function mouseDragged(){
if(mouseButton==LEFT){
for (var x=mouseX;x<mouseX+1;x++){
for(var y=mouseY;y<mouseY+1;y++){
grigliaAttuale[x][y].b=1;
}
}
}else if(mouseButton==RIGHT){
for (var x=mouseX-20;x<mouseX+20;x++){
for(var y=mouseY-20;y<mouseY+20;y++){
grigliaAttuale[x][y].c=1;
grigliaVelocita[x][y].feed-=0.001;//random(feed.min,feed.max);
grigliaVelocita[x][y].k+=0.001;//random(k.min,k.max);
}
}
}else if(mouseButton==CENTER){
for (var x=mouseX-20;x<mouseX+20;x++){
for(var y=mouseY-20;y<mouseY+20;y++){
grigliaAttuale[x][y].c=1;
grigliaVelocita[x][y].feed+=0.001;//random(feed.min,feed.max);
grigliaVelocita[x][y].k-=0.001;//random(k.min,k.max);
}
}
}

}

La funzione di disegno si occupa anche di calcolare la reazione chimica e di scambiare la griglia attuale con la griglia successiva che al tempo t diventa quella attuale e viene visualizzata.

Ogni sostanza contrubuisce a generare il colore del pixel su cui si trova e ho scelto di miscelare i colori e quindi le sostanze in modo tale che si abbiano effetti tipo neon blu su fondali di noise verdini

function draw() {
var velRitiroA;
var velEspansioneB;
timerRicreazioneNoise.counter++;
if(timerRicreazioneNoise.counter>=timerRicreazioneNoise.time){
xoff+=timerRicreazioneNoise.time;//0.5;
yoff+=timerRicreazioneNoise.time;//0.5;
for (var x=1;x<width-1;x++){
for(var y=1;y<height-1;y++){
grigliaVelocita[x][y].feed=map(noise((x+xoff)*noiseScale,(y+yoff)*noiseScale),0.0,1.0,feed.min,feed.max) ;
grigliaVelocita[x][y].k=map(noise((x+xoff)*noiseScale,(y+yoff)*noiseScale),0.0,1.0,k.min,k.max);
}
}
timerRicreazioneNoise.counter=0;
}

for (var x=1;x<width-1;x++){
for(var y=1;y<height-1;y++){

velRitiroA= grigliaVelocita[x][y].feed;
velEspansioneB=grigliaVelocita[x][y].k;


var a=grigliaAttuale[x][y].a;
var b=grigliaAttuale[x][y].b;
var c=grigliaAttuale[x][y].c;
grigliaProssima[x][y].a= a+
(dA * laplaceA(x,y)) -
(a * b * b) +
(velRitiroA * (1-a)) ;
grigliaProssima[x][y].b= b+
(dB * laplaceB(x,y)) +
(a * b * b) -
((velEspansioneB + velRitiroA) * b);
grigliaProssima[x][y].c=c+
((laplaceC(x,y))-
(b)+
(velRitiroA + velEspansioneB)*(1-c)* (1-c) *(1-b)*(1-a));




}
}
var i;
loadPixels();
var coloreA,coloreB,coloreC;
var coloreFeed,coloreK;

for (var x=0;x<width;x++){
for(var y=0;y<height;y++){
coloreA=grigliaProssima[x][y].a;
coloreB=grigliaProssima[x][y].b;
coloreC=grigliaProssima[x][y].c;
coloreFeed=grigliaVelocita[x][y].feed*1.5;
coloreK=grigliaVelocita[x][y].k*1.5;

i=(x+y*width)*4;
pixels[i]=floor((coloreB+(coloreK+coloreFeed))*255);
pixels[i+1]=floor((coloreB+(coloreK+coloreFeed))*255);
pixels[i+2]=floor((((coloreA-coloreC)+coloreB))*255);
pixels[i+3]=255;//floor(coloreFeed*255);//floor(coloreA*255);

}
}
updatePixels();
swap();
esempi di immagini generate:



}

function swap(){
var t=grigliaAttuale;
gigliaAttuale=grigliaProssima;
grigliaProssima=t;
}

function laplaceA(x,y){
var sum=0;
sum += grigliaAttuale[x][y].a * -1;
sum += grigliaAttuale[x-1][y].a * 0.2;
sum += grigliaAttuale[x+1][y].a * 0.2;
sum += grigliaAttuale[x][y+1].a * 0.2;
sum += grigliaAttuale[x][y-1].a * 0.2;
sum += grigliaAttuale[x-1][y-1].a * 0.05;
sum += grigliaAttuale[x+1][y-1].a * 0.05;
sum += grigliaAttuale[x+1][y+1].a * 0.05;
sum += grigliaAttuale[x-1][y+1].a * 0.05;
return sum;
}

function laplaceB(x,y){
var sum=0;
sum += grigliaAttuale[x][y].b * -1;
sum += grigliaAttuale[x-1][y].b * 0.2;
sum += grigliaAttuale[x+1][y].b * 0.2;
sum += grigliaAttuale[x][y+1].b * 0.2;
sum += grigliaAttuale[x][y-1].b * 0.2;
sum += grigliaAttuale[x-1][y-1].b * 0.05;
sum += grigliaAttuale[x+1][y-1].b * 0.05;
sum += grigliaAttuale[x+1][y+1].b * 0.05;
sum += grigliaAttuale[x-1][y+1].b * 0.05;
return sum;
}

function laplaceC(x,y){
var sum=0;
sum += grigliaAttuale[x][y].c * -1;
sum += grigliaAttuale[x-1][y].c * 0.2;
sum += grigliaAttuale[x+1][y].c * 0.2;
sum += grigliaAttuale[x][y+1].c * 0.2;
sum += grigliaAttuale[x][y-1].c * 0.2;
sum += grigliaAttuale[x-1][y-1].c * 0.05;
sum += grigliaAttuale[x+1][y-1].c * 0.05;
sum += grigliaAttuale[x+1][y+1].c * 0.05;
sum += grigliaAttuale[x-1][y+1].c * 0.05;
return sum;
}

::/fulltext::

Write comment (0 Comments)

Stella inattivaStella inattivaStella inattivaStella inattivaStella inattiva
::cck::54::/cck::
::introtext::
Algoritmo Codice (e Video Tutorial <-)

In questo esempio si genera un albero 2D proceduralmente, ed è uno studio nell'ambito del campo di interesse della "Generative Art".

Il programma è scritto in Java e nella versione non on line (scaricabile nell'area download di questo sito) usa i Threads per gestire l'esecuzione concorrente della generazione dei rami e delle foglie.

Nella versione web, riscritta per poter essere eseguita on line (nelle due finestre seguenti) gli oggetti Thread non sono supportati appieno al momento, e quindi la programmazione concorrente l'ho dovuta simulare in modo alternativo. Un'altra caratteristica interessante è l'uso del double buffering in entrambi gli esempi che seguono ma nel secondo programma la tecnica del doppio buffer è stata ulteriormente estesa per accellerare il ridisegno dell'intero albero dopo il suo completamento, ovvero nel primo esempio un thread disegna direttamente a schermo nel secondo esempio un thread disegna su due immagini non a schermo e le disegna sovrapponendole per creare l'immagine finale, il vantaggio sta che una volta completato lalbero esso diventa una immagine raster con canale alfa da incollare in tutti i fotogrammi successivi e non un oggetto vettoriale calcolato e ridisegnato completamente ogni fotogramma, quindi l'animazione dei fiori risulta più veloce con lo sfondo dell'albero generato.

Un maggiore approfondimento delle tecniche e dei dettagli sono espressi nei video tutorial parte1 e parte 2 accessibili dal menu Video o dal Video Tutorial in questo articolo.

::/introtext::
::fulltext::

 Ecco i due esempi. NOTA: se si usano entrambi contemporaneamente la velocita di ridisegno va ripartita tra i due programmi.

Per usarli fai click all'interno della finestra in una parte bassa in modo che il disegno dell'albero entrarà completamente. Per vedere e modificare il codice sorgente clicca sul pultante della finestra </>

 

Questo secondo esempio usa il doppio buffering e risulta più veloce nel disegno delle animazioni e meno nella costruzione iniziale dell'albero.

 

::/fulltext::

Write comment (0 Comments)
© 2018 sito prototipale studio di GiuseppeGi