Erstellen Sie nahtlose Geräuschkarten

Bild

Das Erstellen eines nahtlosen Bildes in Photoshop ist ganz einfach: Schneiden Sie das Bild zu, greifen Sie nach den zugeschnittenen rechten und unteren Teilen und kleben Sie sie dann mit dem Überblendungswerkzeug nach links und oben. Für die ordnungsgemäße Implementierung nahtloser Rauschkarten müssen Sie jedoch sorgfältig überlegen.

Wenn Sie ein grundlegendes Verständnis des Perlin-Rauschens haben , wissen Sie, dass es aus interpolierten Zufallszahlen besteht. Es wird hauptsächlich in zwei Dimensionen verwendet. Es ist aber auch nützlich in einer Dimension (zum Beispiel beim Bewegen), in drei Dimensionen (zylindrische und sphärische Transformation von 3D-Objekten) und sogar in vier oder fünf Dimensionen.

Mit vierdimensionalem Rauschen kann ein nahtloses 2D-Bild erstellt werden. Es ist nicht sehr üblich, dass wir in vier Dimensionen denken, daher werden wir jeweils eine Dimension annehmen.

In meinen Beispielen habe ich Simplex-Rauschen mit zwei Oktaven verwendet. Simplex-Rauschen ist in großen Dimensionen schneller und sieht aufgrund seiner Dreiecksform besser aus.

Ich habe eine kleine Funktion geschrieben drawNoise, um eine Leinwand zu erstellen und ein Pixelarray in einer Schleife zu verarbeiten.

Eindimensionales nahtloses Rauschen


In einer Dimension ist Rauschen eine unendliche glatte Linie (meine Implementierung von Rauschen beginnt mit zwei, daher verwende ich eine Konstante als zweiten Parameter). Hier sehen wir, dass dies nur interpolierte Zufallszahlen sind.

// one dimensional line
fNoiseScale = .02;
drawNoise(function(i,x,y){
    var v = Simplex.noise(
         123+x*fNoiseScale
        ,137 // we just need one dimension so this parameter is a constant
    );
    return v*iSize>y?255:0;
}).img();


Eindimensionales Rauschen

Sie können dies in der Animation verwenden und den Rauschwert jede Millisekunde neu berechnen. Sie können jedoch auch eine Schleife erstellen und alle Werte im Voraus berechnen. Die Werte im obigen Bild werden nicht um die Kanten geschlungen. Die Implementierung der Wiederholbarkeit ist jedoch recht einfach, nur eine weitere Dimension und Schleife ... oder ein Kreis dafür.

Eindimensionale Schleife


Für die meisten von Ihnen sieht Perlins Rauschen ungefähr so ​​aus wie auf dem Bild unten.


Wenn wir hier einen Kreis zeichnen und die Rauschwerte auf diesem Kreis zählen würden, würden wir eine eindimensionale Schleife erhalten.


Rauschen mit einem Kreis, um eine eindimensionale Schleife zu erstellen.

Im Code sieht es so aus:

// one dimensional loop
drawNoise(function(i,x,y){
    var fNX = x/iSize // we let the x-offset define the circle
        ,fRdx = fNX*2*Math.PI // a full circle is two pi radians
        ,a = fRdsSin*Math.sin(fRdx)
        ,b = fRdsSin*Math.cos(fRdx)
        ,v = Simplex.noise(
             123+a*fNoiseScale
            ,132+b*fNoiseScale
        )
    ;
    return v*iSize>y?255:0;
}).img().div(2);


Sie haben wahrscheinlich schon verstanden, was wir machen werden. Um ein zweidimensionales Bild zu schleifen, benötigen wir eine dreidimensionale (mindestens) Rauschkarte.

Zylindrische Karte


Noise Perlin wurde ursprünglich für die kontinuierliche 3D-Texturierung entwickelt (der Film "Tron"). Die Imagemap ist kein Blatt Papier, das um ein Objekt gewickelt ist, sondern wird anhand seiner Position in einem dreidimensionalen Rauschfeld berechnet. Daher können wir beim Schneiden des Objekts weiterhin die Karte für die neu erstellte Oberfläche berechnen.

Bevor wir unser endgültiges Ziel eines nahtlosen Bildes erreichen, erstellen wir zunächst ein Bild, das sich nahtlos nach links und rechts verbindet. Dies ähnelt einem zweidimensionalen Kreis für eine eindimensionale Schleife, jedoch mit einer zusätzlichen Dimension: einem Zylinder.

// three dimensional cylindrical map
drawNoise(function(i,x,y){
    var fNX = x/iSize
        ,fRdx = fNX*2*Math.PI
        ,a = fRdsSin*Math.sin(fRdx)
        ,b = fRdsSin*Math.cos(fRdx)
        ,v = Simplex.noise(
             123+a*fNoiseScale
            ,132+b*fNoiseScale
            ,312+y*fNoiseScale // similar to the one dimensional loop but we add a third dimension defined by the image y-offset
        )
    ;
    return v*255<<0;
}).img().div(2);


Zylinderrauschkarte

Sphärisches Kartenbild


Sie könnten denken, dass es bequem wäre, eine Kugel zu verwenden, um ein nahtloses Bild zu erstellen, aber Sie irren sich.

Ich werde einen kleinen Exkurs machen und zeigen, wie die sphärische Bildkarte berechnet wird und wie sie aussieht.

// three dimensional spherical map
document.body.addChild('h2').innerText = 'three dimensional spherical map';
fNoiseScale = .1;
var oSpherical = drawNoise(function(i,x,y){
    var  fNX = (x+.5)/iSize // added half a pixel to get the center of the pixel instead of the top-left
        ,fNY = (y+.5)/iSize
        ,fRdx = fNX*2*Math.PI
        ,fRdy = fNY*Math.PI // the vertical offset of a 3D sphere spans only half a circle, so that is one Pi radians
        ,fYSin = Math.sin(fRdy+Math.PI) // a 3D sphere can be seen as a bunch of cicles stacked onto each other, the radius of each of these is defined by the vertical position (again one Pi radians)
        ,a = fRdsSin*Math.sin(fRdx)*fYSin
        ,b = fRdsSin*Math.cos(fRdx)*fYSin
        ,c = fRdsSin*Math.cos(fRdy)
        ,v = Simplex.noise(
             123+a*fNoiseScale
            ,132+b*fNoiseScale
            ,312+c*fNoiseScale
        )
    ;
    return v*255<<0;
}).img();


Sphärische Rauschkarte


Kugel mit Lärm

Kubische Panoramakarte


Die von uns erstellte Kugel kann auch als Panorama verwendet werden, wenn Sie eine Kamera in der Mitte der Kugel platzieren. Der beste Weg wäre jedoch, ein kubisches Panorama zu verwenden, da es viel weniger Gesichter hat. Die Kugel wird wie in dieser Skizze gezeigt auf die sechs Seiten des Würfels projiziert.


Anwenden einer Kugel auf einen Würfel

Für jedes Pixel auf der Oberfläche des Würfels müssen wir den Schnittpunkt zwischen dem Ansichtspunkt C in der Mitte und der Kugel berechnen. Es mag kompliziert erscheinen, aber es ist eigentlich ziemlich einfach.

Wir können die CA-Linie als Vektor betrachten. Und Vektoren können normalisiert werden, so dass sich ihre Richtung nicht ändert, aber die Länge auf 1 abnimmt. Aus diesem Grund sehen alle Vektoren zusammen wie eine Kugel aus.

Die Normalisierung ist auch recht einfach. Wir müssen nur die Werte des Vektors durch xyz durch die Gesamtlänge des Vektors teilen. Die Länge des Vektors kann mit dem Satz von Pythagoras berechnet werden.

Im folgenden Code wird die Normalisierungsberechnung zuerst auf einer Seite durchgeführt. Dann wird das Rauschen gleichzeitig für alle sechs Kanten berechnet, da Sie nur die Werte entlang xyz spiegeln müssen, um die Position der nächsten Fläche zu erhalten.

// 3D panoramical cube map
document.body.addChild('h2').innerText = '3D panoramical cube map';
// we're not using the drawNoise function because our canvas is rectangular
var mCubemap = document.createElement('canvas')
    ,iW = 6*iSize;
mCubemap.width = iW;
mCubemap.height = iSize;
var  iHSize = iSize/2 // half the size of the cube
    ,oCtx = mCubemap.getContext('2d')
    ,oImgData = oCtx.getImageData(0,0,iW,iSize)
    ,aPixels = oImgData.data
    ,aa = 123
    ,bb = 231
    ,cc = 321
;
for (var i=0,l=iSize*iSize;i<l;i++) {
    var  x = i%iSize        // x position in image
        ,y = (i/iSize)<<0    // y position in image
        ,a = -iHSize + x+.5    // x position on the cube plane, the added .5 is to get the center of the pixel
        ,b = -iHSize + y+.5 // y position on the cube plane
        ,c = -iHSize        // z position of the cube plane
        ,fDistanceAB = Math.sqrt(a*a+b*b) // to calculate the vectors length we use Pythagoras twice
        ,fDistanceABC = Math.sqrt(fDistanceAB*fDistanceAB+c*c)
        ,fDrds = .5*fDistanceABC // adjust the distance a bit to get a better radius in the noise field
        ,v = 1
    ;
    a /= fDrds; // normalize the vector
    b /= fDrds; // normalize the vector
    c /= fDrds; // normalize the vector
    //
    // since we now know the spherical position for one plane we can derive the positions for the other five planes simply by switching the x, y and z values (the a, b and c variables)
    var aNoisePositions = [
         [a,b,c]    // back
        ,[-c,b,a]    // right
        ,[-a,b,-c]    // front
        ,[c,b,-a]    // left
        ,[a,c,-b]    // top
        ,[a,-c,b]    // bottom
    ];
    for (var j=0;j<6;j++) {
        v = Simplex.noise(
             aa + aNoisePositions[j][0]
            ,bb + aNoisePositions[j][1]
            ,cc + aNoisePositions[j][2]
        );
        var pos = 4*(y*iW+j*iSize+x); // the final position of the rgba pixel
        aPixels[pos] = aPixels[pos+1] = aPixels[pos+2] = v*255<<0;
        aPixels[pos+3] = 255;
    }
}
oCtx.putImageData(oImgData,0,0);
document.body.addChild('img',{src:mCubemap.toDataURL("image/jpeg")});

Hier sind sechs Seiten in einem Bild sowie ein Screenshot davon, wie es aus der Sicht eines Würfels aussieht. Der Quellcode enthält ein 3D-Beispiel, das in drei Sekunden geschrieben ist .


Kubische Panoramakarte


Nahtloses 2D-Bild


Es mag scheinen, dass ein nahtloses 2D-Bild einfach zu implementieren ist, aber es scheint mir, dass dies das schwierigste der im Artikel beschriebenen ist, denn um es zu verstehen, müssen Sie in vier Dimensionen denken. Am nächsten dran war eine zylindrische Karte (mit horizontaler Wiederholung), also werden wir sie als Grundlage nehmen. In der zylindrischen Karte haben wir die horizontale Position des Bildes für den Kreis verwendet; Das heißt, die horizontale Position des Bildes gibt uns zwei Koordinaten x und y im Rauschfeld xyz. Die vertikale Position des Bildes entspricht z im Rauschfeld.

Wir möchten, dass das Bild nahtlos und vertikal ist. Wenn wir also eine weitere Dimension hinzufügen, können wir damit einen zweiten Kreis erstellen und den linearen Wert des z-Felds ersetzen. Dies ähnelt der Erzeugung von zwei Zylindern in einem vierdimensionalen Feld. Ich habe versucht, dies auf einer Skizze zu visualisieren, es ist ungenau, aber ich habe versucht, das allgemeine Prinzip zu vermitteln und keinen vierdimensionalen Zylinder zu zeichnen.


Eine Skizze von zwei Zylindern in vier Dimensionen

Der Code ist recht einfach: Dies sind nur zwei Kreise in einem vierdimensionalen Rauschraum.

// four dimensional tile
fNoiseScale = .003;
drawNoise(function(i,x,y){
    var  fNX = x/iSize
        ,fNY = y/iSize
        ,fRdx = fNX*2*Math.PI
        ,fRdy = fNY*2*Math.PI
        ,a = fRds*Math.sin(fRdx)
        ,b = fRds*Math.cos(fRdx)
        ,c = fRds*Math.sin(fRdy)
        ,d = fRds*Math.cos(fRdy)
        ,v = Simplex.noise(
             123+a*fNoiseScale
            ,231+b*fNoiseScale
            ,312+c*fNoiseScale
            ,273+d*fNoiseScale
        )
    ;
    return (Math.min(Math.max(2*(v -.5)+.5,0),1)*255)<<0;
}).img().div(2,2);

Und hier ist das Ergebnis:

Bild

All Articles