Pittura murale strutturata generata da computer


48

La vernice sulle pareti della mia stanza ha una trama tridimensionale casuale, quasi frattale:

Immagine A

In questa sfida scriverai un programma che genera immagini casuali che sembrano far parte delle mie mura.

Di seguito ho raccolto 10 immagini di diversi punti sulle mie pareti. Tutti hanno all'incirca la stessa illuminazione e tutti sono stati ripresi con la fotocamera a un piede di distanza dal muro. I bordi sono stati uniformemente ritagliati per renderli 2048 per 2048 pixel, quindi sono stati ridimensionati a 512 per 512. L'immagine sopra è l'immagine A.

Queste sono solo miniature, fai clic sulle immagini per visualizzarle a dimensioni intere!

A: B: C: D: E:Immagine A Immagine B Immagine C Immagine D Immagine E

F: G: H: I: J:Immagine F Immagine G Immagine H Immagine I. Immagine J

Il tuo compito è scrivere un programma che includa un numero intero positivo da 1 a 2 16 come seme casuale, e per ogni valore genera un'immagine distinta che sembra che potrebbe essere stata "l'undicesima immagine" del mio muro. Se qualcuno che guarda le mie 10 immagini e alcuni dei tuoi non riesce a capire quali sono stati generati dal computer, allora hai fatto molto bene!

Mostra alcune delle tue immagini generate in modo che gli utenti possano vederle senza dover eseguire il codice.

Mi rendo conto che l'illuminazione delle mie immagini non è perfettamente uniforme in intensità o colore. Mi dispiace per questo, ma è il massimo che potrei fare senza una migliore attrezzatura per l'illuminazione. Non è necessario che le tue immagini abbiano un'illuminazione variabile (anche se potrebbero). La trama è la cosa più importante su cui concentrarsi.

Dettagli

  • È possibile utilizzare strumenti e librerie di elaborazione delle immagini.
  • Prendi l'input nel modo che desideri (riga di comando, stdin, variabile ovvia, ecc.).
  • L'immagine di output può essere in qualsiasi formato di file di immagine lossless comune, oppure può essere semplicemente visualizzata in una finestra / bowser.
  • Puoi analizzare a livello di codice le mie 10 immagini ma non dare per scontato che chiunque esegua il tuo codice abbia accesso ad esse.
  • È necessario generare le immagini a livello di codice. Non è possibile codificare una leggera variante di una delle mie immagini o di qualche altra immagine di riserva. (Le persone ti voterebbero comunque per questo.)
  • È possibile utilizzare generatori di numeri pseudocasuali integrati e supporre che il periodo sia 2 16 o più.

punteggio

Questo è un concorso di popolarità, quindi vince la risposta più votata.


PerlinNoise + troncamento + ombreggiatura
Polpo

21
Non riesco a fare immagini da parete, quindi invece ho un fumetto !
Sp3000,

8
@ Sp3000 È più o meno così. Anche se Se avessi guardato in alto avrei probabilmente scelto il mio soffitto , che potrebbe funzionare anche ...
Calvin's Hobbies

Risposte:


65

GLSL (+ JavaScript + WebGL)

inserisci qui la descrizione dell'immagine

Demo live | Repository GitHub

Come usare

Ricarica la pagina per una nuova immagine casuale. Se desideri inserire un seed specifico, apri la console del browser e chiama drawScreen(seed). La console dovrebbe visualizzare il seme utilizzato al caricamento.

Non l'ho davvero testato su molte piattaforme, quindi fammi sapere se non funziona per te. Ovviamente, il tuo browser deve supportare WebGL. Gli errori vengono visualizzati nella colonna a sinistra o nella console del browser (a seconda del tipo di errore).

Novità: ora puoi ravvivare un po 'le pareti spuntando la casella "sorgente luminosa mobile".

Cos'è questa stregoneria?

Ho questo codice WebGL di plateplate fluttuante attorno al mio account GitHub , che utilizzo ogni tanto per prototipare rapidamente alcuni elementi grafici 2D in WebGL. Con un po 'di magia shader, possiamo anche renderlo leggermente 3D, quindi ho pensato che fosse il modo più veloce per ottenere alcuni effetti piacevoli. La maggior parte dell'installazione proviene da quel codice boilerplate, e sto considerando che una libreria per questa presentazione e non la includerà in questo post. Se sei interessato, dai un'occhiata a main.js su GitHub (e agli altri file in quella cartella).

Tutto ciò che JavaScript fa è impostare un contesto WebGL, archiviare il seme in uniforme per lo shader e quindi eseguire il rendering di un singolo quad sull'intero contesto. Lo shader di vertice è un semplice shader passthrough, quindi tutta la magia avviene nello shader di frammenti. Ecco perché ho chiamato questo un invio GLSL.

La maggior parte del codice è in realtà generare il rumore Simplex, che ho trovato su GitHub . Quindi lo sto omettendo anche nell'elenco di codice qui sotto. La parte importante è che definisce una funzione snoise(vec2 coords)che restituisce rumore simplex senza usare una trama o una ricerca di array. Non è affatto seminato, quindi il trucco per ottenere un rumore diverso consiste nell'utilizzare il seme nel determinare dove effettuare la ricerca.

Quindi ecco qui:

#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable

uniform float uSeed;
uniform vec2 uLightPos;

varying vec4 vColor;
varying vec4 vPos;

/* ... functions to define snoise(vec2 v) ... */

float tanh(float x)
{
    return (exp(x)-exp(-x))/(exp(x)+exp(-x));
}

void main() {
    float seed = uSeed * 1.61803398875;
    // Light position based on seed passed in from JavaScript.
    vec3 light = vec3(uLightPos, 2.5);
    float x = vPos.x;
    float y = vPos.y;

    // Add a handful of octaves of simplex noise
    float noise = 0.0;
    for ( int i=4; i>0; i-- )
    {
        float oct = pow(2.0,float(i));
        noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
    }
    // Level off the noise with tanh
    noise = tanh(noise*noise)*2.0;
    // Add two smaller octaves to the top for extra graininess
    noise += sqrt(abs(noise))*snoise(vec2(mod(seed,13.0)+x*32.0,mod(seed*seed,11.0)+y*32.0))/32.0*3.0;
    noise += sqrt(abs(noise))*snoise(vec2(mod(seed,13.0)+x*64.0,mod(seed*seed,11.0)+y*64.0))/64.0*3.0;

    // And now, the lighting
    float dhdx = dFdx(noise);
    float dhdy = dFdy(noise);
    vec3 N = normalize(vec3(-dhdx, -dhdy, 1.0)); // surface normal
    vec3 L = normalize(light - vec3(vPos.x, vPos.y, 0.0)); // direction towards light source
    vec3 V = vec3(0.0, 0.0, 1.0); // direction towards viewpoint (straight up)
    float Rs = dot(2.0*N*dot(N,L) - L, V); // reflection coefficient of specular light, this is actually the dot product of V and and the direction of reflected light
    float k = 1.0; // specular exponent

    vec4 specularColor = vec4(0.4*pow(Rs,k));
    vec4 diffuseColor = vec4(0.508/4.0, 0.457/4.0, 0.417/4.0, 1.0)*dot(N,L);
    vec4 ambientColor = vec4(0.414/3.0, 0.379/3.0, 0.344/3.0, 1.0);

    gl_FragColor = specularColor + diffuseColor + ambientColor;
    gl_FragColor.a = 1.0;
}

Questo è tutto. Potrei aggiungere qualche spiegazione domani, ma l'idea di base è:

  • Scegli una posizione di luce casuale.
  • Aggiungi alcune ottave di rumore, per generare il modello frattale.
  • Square il rumore per mantenere il fondo ruvido.
  • Fai passare il rumore tanhper livellare la parte superiore.
  • Aggiungi altre due ottave per un po 'più di trama sul livello superiore.
  • Calcola le normali della superficie risultante.
  • Esegui una semplice ombreggiatura Phong su quella superficie, con luci speculari e diffuse. I colori sono scelti in base ad alcuni colori casuali che ho scelto dalla prima immagine di esempio.

17
Questo è più realistico del muro stesso: o
Quentin

1
Altre "vene" / "serpenti" / "vermi" renderebbero questa immagine più adatta al "muro". Ma comunque carino.
Nova

33

Mathematica Spackling

L'app in basso applica le macchioline a un'immagine casuale. Fare clic su "nuova patch" genera una nuova immagine casuale con cui lavorare, quindi applica gli effetti in base alle impostazioni correnti. Gli effetti sono pittura a olio, filtro gaussiano, posterizzazione e goffratura. Ogni effetto può essere modificato in modo indipendente. Il seme per il generatore di numeri casuali può essere qualsiasi numero intero compreso tra 1 e 2 ^ 16.

Aggiornamento : il filtro gaussiano, che ammorbidisce i bordi, è ora l'ultimo effetto dell'immagine applicato. Con questa modifica, l'effetto posterizzazione non era più necessario e quindi rimosso.

Manipulate[
 GaussianFilter[ImageEffect[ImageEffect[r, {"OilPainting", o}], {"Embossing", e, 1.8}], g],
 Button["new patch", (SeedRandom[seed] r = RandomImage[1, {400, 400}])], 
 {{o, 15, "oil painting"}, 1, 20, 1, ContinuousAction -> False, Appearance -> "Labeled"}, 
 {{e, 1.64, "embossing"}, 0, 5, ContinuousAction -> False, Appearance -> "Labeled"},
 {{g, 5, "Gaussian filter"}, 1, 12, 1, ContinuousAction -> False, Appearance -> "Labeled"},
 {{seed, 1}, 1, 2^16, 1, ContinuousAction -> False, Appearance -> "Labeled"}, 
 Initialization :> (SeedRandom[seed]; r = RandomImage[1, {400, 400}])]

risultato finale


Spiegazione

La spiegazione si basa su una versione leggermente diversa, in cui è stata impiegata la posterizzazione ed è GaussianFilterstata applicata all'inizio. Serve comunque a chiarire come ogni effetto di un'immagine altera un'immagine. Il risultato finale è una trama di vernice con bordi più nitidi. Quando il filtro gaussiano viene applicato solo alla fine, il risultato sarà più uniforme, come mostrato nella figura sopra.

Diamo un'occhiata ad alcuni effetti di immagine, uno alla volta.

Genera un'immagine iniziale.

 r = RandomImage[1, {200, 200}]

immagine casuale


Lena ci mostrerà come ogni effetto dell'immagine trasforma un'immagine realistica.

Lena = ExampleData[{"TestImage", "Lena"}]

Lena


Un effetto di pittura ad olio applicato a Lena.

ImageEffect[Lena, {"OilPainting", 8}]

olio di lena

Un effetto di pittura ad olio applicato alla nostra immagine casuale. L'effetto è stato intensificato (16 anziché 8).

 r1 = ImageEffect[r, {"OilPainting", 16}]

olio


Un effetto filtro gaussiano applicato a Lena (non alla versione effetto pittura a olio di Lena). Il raggio è di 10 pixel. (Nella versione finale, all'inizio di questa voce, GaussianFilter viene applicato come effetto finale.)

 GaussianFilter[Lena, 10]

lena gaussiana.


Un effetto filtro gaussiano leggermente più lieve applicato a r1. Il raggio è di 5 pixel.

 r2 = GaussianFilter[r1, 5]

Gauss


Un intenso effetto di posterizzazione applicato a Lena. (Nella versione finale dell'app, ho rimosso la posterizzazione. Ma la lasceremo nell'analisi, poiché gli esempi nell'analisi erano basati su una versione precedente con posterizzazione.)

 ImageEffect[Lena, {"Posterization", 2}]

lena posterizza


Un effetto posterizzazione applicato a r2.

r3 = ImageEffect[r2, {"Posterization", 4}]

Posterizza


Lena in rilievo

 ImageEffect[Lena, {"Embossing", 1.2, 1.8}]

goffratura lena


La goffratura r3 completa l'elaborazione dell'immagine. Questo ha lo scopo di assomigliare al soffitto del PO.

 ceilingSample = ImageEffect[r3, {"Embossing", 1.2, 1.8}]

rilievo


Per i curiosi, ecco Lena con gli stessi effetti immagine applicati.

lena4


... c'è un built-in per ottenere l'immagine di Lena? LOL.
user253751

7
Sì. Ci sono circa 30 immagini integrate in Mathematica utilizzate per i test di elaborazione delle immagini.
David C

Con una leggera modifica, si potrebbe alimentare RandomIntegerun seme, garantendo così una produzione particolare. O intendi qualcos'altro, come un'immagine iniziale, non casuale a cui vengono applicati gli effetti?
DavidC,

1
Ora accetta un seme da 1 a 2 ^ 16.
DavidC

1
+1 perché Lena will show us how each image effect transforms a life-like picturemi ha fatto LOL. La cosa strana è che l'immagine finale di Lena sembra avere un azteco o un inca rivolto verso sinistra, con un copricapo e un ramoscello come se fosse una pistola.
Level River St

13

POV-Ray

Un sacco di potenziale di golf, correre con povray /RENDER wall.pov -h512 -w512 -K234543 inserisci qui la descrizione dell'immagine

Innanzitutto crea una trama casuale, ma invece di fermarsi lì trasforma la trama in un campo di altezza 3D per rendere più realistiche le ombre radiali del flash della fotocamera. E per buona misura aggiunge un'altra trama di piccoli dossi in cima.
L'unico modo a parte il hardcoding del seme casuale è usare la clockvariabile pensata per le animazioni, questa viene passata con la -K{number}bandiera

#default{ finish{ ambient 0.1 diffuse 0.9 }} 

camera {location y look_at 0 right x}
light_source {5*y color 1}

#declare R1 = seed (clock); // <= change this

#declare HF_Function  =
 function{
   pigment{
     crackle turbulence 0.6
     color_map{
       [0.00, color 0.01]
       [0.10, color 0.05]
       [0.30, color 0.20]
       [0.50, color 0.31]
       [0.70, color 0.28]
       [1.00, color 0.26]
     }// end color_map
    scale <0.25,0.005,0.25>*0.7 
    translate <500*rand(R1),0,500*rand(R1)>
   } // end pigment
 } // end function

height_field{
  function  512, 512
  { HF_Function(x,0,y).gray * .04 }
  smooth 
  texture { pigment{ color rgb<0.6,0.55,0.5>}
            normal { bumps 0.1 scale 0.005}
            finish { phong .1 phong_size 400}
          } // end of texture  
  translate< -0.5,0.0,-0.5>
}

Non è necessario giocare a golf. Bei risultati! :)
Martin Ender,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.