Computererzeugte strukturierte Wandfarbe

48

Die Farbe an den Wänden in meinem Zimmer hat eine zufällige, fast fraktale, dreidimensionale Textur:

Bild A

In dieser Herausforderung werden Sie ein Programm schreiben, das zufällige Bilder erzeugt, die aussehen, als könnten sie Teil meiner Wände sein.

Unten habe ich 10 Bilder von verschiedenen Stellen an meinen Wänden gesammelt. Alle haben ungefähr die gleiche Beleuchtung und alle wurden mit der Kamera einen Fuß von der Wand entfernt aufgenommen. Die Ränder wurden gleichmäßig auf 2048 x 2048 Pixel beschnitten und dann auf 512 x 512 skaliert. Das obige Bild ist Bild A.

Dies sind nur Vorschaubilder, klicken Sie auf die Bilder, um sie in voller Größe anzuzeigen!

A: B: C: D: E:Bild A Bild B Bild C Bild D Bild E

F: G: H: I: J:Bild F Bild G Bild H Bild I Bild J

Ihre Aufgabe ist es, ein Programm zu schreiben, das eine positive ganze Zahl von 1 bis 2 16 als zufälligen Startwert aufnimmt und für jeden Wert ein bestimmtes Bild erzeugt, das aussieht, als hätte es das "elfte Bild" meiner Wand sein können. Wenn jemand, der sich meine 10 Bilder und einige von Ihnen ansieht, nicht wissen kann, welche computergeneriert wurden, dann haben Sie es sehr gut gemacht!

Führen Sie einige Ihrer generierten Bilder vor, damit die Betrachter sie sehen können, ohne den Code ausführen zu müssen.

Mir ist klar, dass die Beleuchtung in meinen Bildern in Bezug auf Intensität oder Farbe nicht perfekt gleichmäßig ist. Das tut mir leid, aber es ist das Beste, was ich ohne eine bessere Beleuchtung tun kann. Ihre Bilder müssen nicht variabel beleuchtet sein (obwohl dies möglich ist). Die Textur ist das Wichtigste, worauf es ankommt.

Einzelheiten

  • Sie können Bildverarbeitungstools und -bibliotheken verwenden.
  • Nehmen Sie die Eingabe wie gewünscht vor (Befehlszeile, stdin, offensichtliche Variable usw.).
  • Das Ausgabebild kann in jedem gängigen verlustfreien Bilddateiformat oder einfach in einem Fenster / Browser angezeigt werden.
  • Sie können meine 10 Bilder programmgesteuert analysieren, aber nicht davon ausgehen, dass jeder, der Ihren Code ausführt, Zugriff darauf hat.
  • Sie müssen die Bilder programmgesteuert generieren. Möglicherweise haben Sie eine geringfügige Variante eines meiner Bilder oder eines anderen Archivbilds nicht fest codiert. (Die Leute würden dich sowieso dafür ablehnen.)
  • Sie können integrierte Pseudozufallszahlengeneratoren verwenden und davon ausgehen, dass die Periode 2 16 oder mehr beträgt .

Wertung

Dies ist ein Beliebtheitswettbewerb, bei dem die am höchsten bewertete Antwort gewinnt.

Calvins Hobbys
quelle
PerlinNoise + Kürzung + Schattierung
Octopus
21
Ich kann keine Wandbilder machen, habe also stattdessen einen Comic !
Sp3000,
8
@ Sp3000 So ist es mehr oder weniger passiert. Wenn ich nach oben geschaut hätte, hätte ich wahrscheinlich meine Decke ausgewählt , die auch funktionieren könnte ...
Calvins Hobbys

Antworten:

65

GLSL (+ JavaScript + WebGL)

Bildbeschreibung hier eingeben

Live-Demo | GitHub-Repository

Wie benutzt man

Laden Sie die Seite neu, um ein neues zufälliges Bild zu erhalten. Wenn Sie einen bestimmten Startwert eingeben möchten, öffnen Sie die Konsole Ihres Browsers und rufen Sie auf drawScreen(seed). Die Konsole sollte den bei Belastung verwendeten Startwert anzeigen.

Ich habe dies auf vielen Plattformen nicht wirklich getestet. Lassen Sie mich wissen, wenn es bei Ihnen nicht funktioniert. Natürlich muss Ihr Browser WebGL unterstützen. Fehler werden entweder in der linken Spalte oder in der Browserkonsole angezeigt (abhängig von der Art des Fehlers).

Neu: Sie können die Wände jetzt ein wenig zum Leben erwecken, indem Sie das Kontrollkästchen "Bewegliche Lichtquelle" aktivieren.

Was ist das für eine Zauberei?

Ich habe diesen WebGL-Code in meinem GitHub-Konto , den ich gelegentlich verwende , um schnell einige 2D-Grafiksachen in WebGL zu prototypisieren. Mit etwas Shader-Magie können wir es auch leicht dreidimensional wirken lassen, daher dachte ich, das wäre der schnellste Weg, um ein paar nette Effekte in Gang zu bringen. Der Großteil des Setups stammt aus diesem Code, und ich denke, dass es sich um eine Bibliothek für diese Einreichung handelt, die in diesem Beitrag nicht enthalten ist. Wenn Sie interessiert sind, schauen Sie sich die Datei main.js auf GitHub (und die anderen Dateien in diesem Ordner) an.

Alles, was Sie mit JavaScript tun müssen, ist, einen WebGL-Kontext einzurichten, den Startwert in einer Uniform für den Shader zu speichern und dann ein einzelnes Quad über den gesamten Kontext zu rendern. Der Vertex-Shader ist ein einfacher Passthrough-Shader, sodass die ganze Magie im Fragment-Shader abläuft. Deshalb habe ich dies eine GLSL-Einreichung genannt.

Der größte Teil des Codes besteht darin, das Simplex-Rauschen zu erzeugen, das ich auf GitHub gefunden habe . Also lasse ich das auch in der Code-Auflistung unten weg. Der wichtige Teil ist, dass eine Funktion definiert wird, snoise(vec2 coords)die Simplex-Rauschen ohne Verwendung einer Textur oder eines Array-Lookups zurückgibt. Es ist überhaupt nicht geimpft, daher besteht der Trick, um ein anderes Geräusch zu erhalten, darin, den Keim zu verwenden, um zu bestimmen, wo die Suche durchgeführt werden soll.

Also los geht's:

#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;
}

Das ist es. Ich könnte morgen eine Erklärung hinzufügen, aber die Grundidee ist:

  • Wählen Sie eine zufällige Lichtposition.
  • Addieren Sie einige Oktaven Rauschen, um das fraktale Muster zu erzeugen.
  • Quadrieren Sie das Geräusch, um den Boden rau zu halten.
  • Führen Sie das Geräusch durch, um es tanhnach oben abzugleichen.
  • Fügen Sie zwei weitere Oktaven hinzu, um der obersten Ebene etwas mehr Textur zu verleihen.
  • Berechnen Sie die Normalen der resultierenden Oberfläche.
  • Führen Sie eine einfache Phong-Schattierung mit spiegelnden und diffusen Lichtern über diese Oberfläche. Die Farben werden anhand einiger zufälliger Farben ausgewählt, die ich aus dem ersten Beispielbild ausgewählt habe.
Martin Ender
quelle
17
Dies ist realistischer als die Mauer selbst: o
Quentin
1
Einige weitere "Venen" / "Schlangen" / "Würmer" würden dieses Bild passender für die "Wand" machen. Aber immer noch schön.
Nova
33

Mathematica Spackling

Die App unten wendet Speckling auf ein zufälliges Bild an. Wenn Sie auf "Neuer Patch" klicken, wird ein neues zufälliges Bild erstellt, mit dem Sie arbeiten können, und die Effekte werden entsprechend den aktuellen Einstellungen angewendet. Die Effekte sind Ölgemälde, Gauß-Filter, Posterisierung und Prägung. Jeder Effekt kann unabhängig eingestellt werden. Der Startwert für den Zufallszahlengenerator kann eine beliebige Ganzzahl von 1 bis 2 ^ 16 sein.

Update : Der Gauß-Filter, der die Kanten weicher macht, ist jetzt der letzte angewendete Bildeffekt. Mit dieser Modifikation wurde der Posterisierungseffekt nicht mehr benötigt und somit entfernt.

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}])]

Endresultat


Erläuterung

Die Erklärung basiert auf einer etwas anderen Version, in der die Posterisierung angewendet und GaussianFilterfrüh angewendet wurde. Es dient jedoch weiterhin zur Verdeutlichung, wie jeder Bildeffekt ein Bild verändert. Das Endergebnis ist eine Farbtextur mit schärferen Kanten. Wenn der Gauß-Filter erst am Ende angewendet wird, wird das Ergebnis glatter, wie das obige Bild zeigt.

Schauen wir uns nacheinander einige Bildeffekte an.

Erzeugen Sie ein Startbild.

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

Zufälliges Bild


Lena wird uns zeigen, wie jeder Bildeffekt ein lebensechtes Bild verwandelt.

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

Lena


Ein Ölgemäldeeffekt auf Lena.

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

Lena Oil

Ein Ölgemäldeeffekt, der auf unser zufälliges Bild angewendet wird. Der Effekt wurde verstärkt (16 statt 8).

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

Öl


Ein Gauß-Filtereffekt, der auf Lena angewendet wurde (nicht auf die Ölgemälde-Effektversion von Lena). Der Radius beträgt 10 Pixel. (In der endgültigen Version oben in diesem Eintrag wird GaussianFilter als endgültiger Effekt angewendet.)

 GaussianFilter[Lena, 10]

Lena Gauß.


Ein etwas milderer Gauß-Filtereffekt wurde auf r1 angewendet. Der Radius beträgt 5 Pixel.

 r2 = GaussianFilter[r1, 5]

Gauß


Ein intensiver Posterisierungseffekt auf Lena. (In der endgültigen Version der App habe ich die Posterisierung entfernt. Wir werden sie jedoch in der Analyse belassen, da die Beispiele in der Analyse auf einer früheren Version mit Posterisierung basierten.)

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

lena posterize


Ein Posterisierungseffekt, der auf r2 angewendet wird.

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

posterisieren


Prägung Lena

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

Lena Prägung


Die Prägung r3 vervollständigt die Bildverarbeitung. Dies soll so etwas wie die Obergrenze des OP aussehen.

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

prägen


Für die Neugierigen ist hier Lena mit den gleichen Bildeffekten angewendet.

lena4

DavidC
quelle
... es eine integrierte Funktion gibt, um das Lena-Image zu erhalten? LOL.
user253751
7
Ja. In Mathematica sind ca. 30 Bilder für Bildverarbeitungstests integriert.
DavidC
Mit einer kleinen Modifikation könnte man RandomIntegerein Saatgut füttern und so eine bestimmte Leistung garantieren. Oder meinen Sie etwas anderes, wie ein nicht zufälliges Startbild, auf das Effekte angewendet werden?
DavidC
1
Es akzeptiert jetzt einen Startwert von 1 bis 2 ^ 16.
DavidC
1
+1, weil Lena will show us how each image effect transforms a life-like picturemich LOL gemacht hat. Das Seltsame ist, dass auf dem letzten Lena-Bild ein Azteke oder Inka zu sehen ist, der einen Kopfschmuck trägt und einen Zweig wie eine Waffe weilded.
Level River St
13

POV-Ray

Viel Golfpotential, mit dem man laufen kann povray /RENDER wall.pov -h512 -w512 -K234543 Bildbeschreibung hier eingeben

Zuerst wird eine zufällige Textur erstellt, aber statt dort anzuhalten, wird die Textur in ein 3D-Höhenfeld umgewandelt, um die radialen Schatten des Kamerablitzes realistischer zu machen. Und für ein gutes Maß fügt es eine weitere Textur von kleinen Beulen hinzu.
Abgesehen von der Hardcodierung des Zufallskeims besteht der einzige Weg darin, die clockfür Animationen vorgesehene Variable zu verwenden. Dies wird mit dem -K{number}Flag übergeben

#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>
}
DenDenDo
quelle
Es ist überhaupt nicht nötig, Golf zu spielen. Gute Ergebnisse! :)
Martin Ender