타 일식 Perlin 노이즈를 어떻게 생성합니까?


127

관련 :

타일 ​​가능한 Perlin 노이즈를 생성하고 싶습니다. 폴 버크 (Paul Bourke)의 PerlinNoise*() 기능 에서 일하고 있습니다 .

// alpha is the "division factor" (how much to damp subsequent octaves with (usually 2))
// beta is the factor that multiplies your "jump" into the noise (usually 2)
// n is the number of "octaves" to add in
double PerlinNoise2D(double x,double y,double alpha,double beta,int n)
{
   int i;
   double val,sum = 0;
   double p[2],scale = 1;

   p[0] = x;
   p[1] = y;
   for (i=0;i<n;i++) {
      val = noise2(p);
      sum += val / scale;
      scale *= alpha;
      p[0] *= beta;
      p[1] *= beta;
   }
   return(sum);
}

다음과 같은 코드를 사용하십시오.

real val = PerlinNoise2D( x,y, 2, 2, 12 ) ; // test

return val*val*skyColor + 2*val*(1-val)*gray + (1-val)*(1-val)*cloudColor ;

하늘처럼

비타 일

타일링 할 수 없습니다.

픽셀 값은 0-> 256 (너비 및 높이)이고 픽셀 (0,0)은 (x, y) = (0,0)을 사용하고 픽셀 (256,256)은 (x, y) = (1,1)을 사용합니다

타일 ​​가능하게 만들려면 어떻게해야합니까?


14
참고로 Perlin의 소음은 없습니다. 프랙탈 노이즈입니다. Perlin 노이즈는 프랙탈 노이즈의 각 옥타브를 생성하는 "noise2"기능 일 가능성이 높습니다.
Nathan Reed

답변:


80

이와 같이 매끄럽게 타일 가능한 fBm 노이즈를 만드는 데는 두 가지 부분이 있습니다. 먼저 Perlin 노이즈 기능 자체를 타일링 가능하게 만들어야합니다. 다음은 최대 256 기간까지 작동하는 간단한 Perlin 노이즈 함수에 대한 Python 코드입니다 (첫 번째 섹션을 수정하여 원하는만큼 확장 할 수 있음).

import random
import math
from PIL import Image

perm = range(256)
random.shuffle(perm)
perm += perm
dirs = [(math.cos(a * 2.0 * math.pi / 256),
         math.sin(a * 2.0 * math.pi / 256))
         for a in range(256)]

def noise(x, y, per):
    def surflet(gridX, gridY):
        distX, distY = abs(x-gridX), abs(y-gridY)
        polyX = 1 - 6*distX**5 + 15*distX**4 - 10*distX**3
        polyY = 1 - 6*distY**5 + 15*distY**4 - 10*distY**3
        hashed = perm[perm[int(gridX)%per] + int(gridY)%per]
        grad = (x-gridX)*dirs[hashed][0] + (y-gridY)*dirs[hashed][1]
        return polyX * polyY * grad
    intX, intY = int(x), int(y)
    return (surflet(intX+0, intY+0) + surflet(intX+1, intY+0) +
            surflet(intX+0, intY+1) + surflet(intX+1, intY+1))

펄린 노이즈는 무작위로 지향 된 그래디언트와 분리 가능한 다항식 폴 오프 함수의 곱인 작은 "서 프릿"의 요약에서 생성됩니다. 이것은 양의 영역 (노란색)과 음의 영역 (파란색)을 제공합니다

핵심

서블릿은 2x2 범위를 가지며 정수 격자 점을 중심으로하므로 공간의 각 지점에서 Perlin 노이즈의 값은 차지하는 셀의 모서리에 서블릿을 합산하여 생성됩니다.

요약

그래디언트 방향을 일정 기간 동안 랩핑하면 노이즈 자체가 동일한 기간으로 매끄럽게 랩됩니다. 이것이 위의 코드가 순열 테이블을 통해 해시하기 전에 격자 좌표 모듈로주기를 취하는 이유입니다.

다른 단계는 옥타브를 합산 할 때 옥타브의 주파수에 따라주기를 조정하는 것입니다. 기본적으로 각 옥타브가 여러 번이 아닌 전체 이미지를 한 번 타일링하기를 원할 것입니다.

def fBm(x, y, per, octs):
    val = 0
    for o in range(octs):
        val += 0.5**o * noise(x*2**o, y*2**o, per*2**o)
    return val

그것을 합하면 다음과 같은 것을 얻습니다.

size, freq, octs, data = 128, 1/32.0, 5, []
for y in range(size):
    for x in range(size):
        data.append(fBm(x*freq, y*freq, int(size*freq), octs))
im = Image.new("L", (size, size))
im.putdata(data, 128, 128)
im.save("noise.png")

Tileable fBm 노이즈

보시다시피, 이것은 실제로 매끄럽게 타일링됩니다.

fBm 노이즈, 타일

약간의 미세 조정 및 색상 매핑을 통해 2x2 크기의 클라우드 이미지가 있습니다.

구름!

도움이 되었기를 바랍니다!


3
나는 파이썬 사람이 아니므 x*2**o로 어떻게 묻 습니까? C로 변환합니까? 그것은이다 : x*pow(2,o)pow(x*2,o)?
idev

7
x*pow(2, o)지수는 곱셈보다 우선 순위가 높습니다.
John Calsbeek

1
누군가 이것을 이것을 C로 변환 할 수 있습니까? 파이썬으로 아무것도 한 적이 없기 때문에이 코드를 이해하는 데 큰 문제가 있습니다. 예를 들어 a가치 란 무엇 입니까? 그리고 함수가 어떻게 C로 변환되는지 확실하지 않습니다 ... 출력에서만 직선을 얻습니다.
idev

1
소음의 영역이 타일의 모양에 묶여 있으면 괜찮을 것입니다. 예를 들어, 이것은 임의의 회전을 허용하지 않습니다. 그러나 그러한 것이 필요하지 않다면 이것이 이상적인 답변입니다.
존 Calsbeek

1
참고 : 128 이외의 크기를 생성하려면 행의 숫자 값을 변경하지 마십시오 im.putdata(data, 128, 128). (파이썬이나 PIL에 익숙하지 않은 사람들에게는 이미지 크기가 아니라 스케일과 오프셋을 의미합니다.)
Antti Kissaniemi

87

다음은 4D Perlin 노이즈를 사용하는 다소 영리한 방법입니다.

기본적으로 픽셀의 X 좌표를 2D 원에 매핑하고 픽셀의 Y 좌표를 두 번째 2D 원에 매핑하고 두 원을 서로 직교하는 4D 공간에 배치합니다. 결과 텍스처는 타일링 가능하고 명백한 왜곡이 없으며 미러 텍스처와 같은 방식으로 반복되지 않습니다.

기사에서 코드를 복사하여 붙여 넣기 :

for x=0,bufferwidth-1,1 do
    for y=0,bufferheight-1,1 do
        local s=x/bufferwidth
        local t=y/bufferheight
        local dx=x2-x1
        local dy=y2-y1

        local nx=x1+cos(s*2*pi)*dx/(2*pi)
        local ny=y1+cos(t*2*pi)*dy/(2*pi)
        local nz=x1+sin(s*2*pi)*dx/(2*pi)
        local nw=y1+sin(t*2*pi)*dy/(2*pi)

        buffer:set(x,y,Noise4D(nx,ny,nz,nw))
    end
end

3
이것이 정답입니다. 치수 추가는 오래된 수학자입니다. Olinde Rodrigues docet (WR 해밀턴 선생님도 마찬가지지만 약간 적음)
FxIII

@FxIII,이 Noise4D () 구현 방법을 알고 있습니까? 나는 이것을 시도하고 싶지만이 Noise4D ()가 어떻게 작동 해야하는지 전혀 모른다.
idev

4
4D 노이즈 기능을 사용할 수 있습니다. 단순한 소음이 내 추천이 될 것입니다. webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
John Calsbeek

2
고마워 존! 잘 작동 했어, 달콤 해! 아무도 말하지는 않았지만 x1, y1, x2, y2는 일종의 스케일링, 더 큰 거리, 자세한 노이즈 인 것 같습니다. 이것이 누군가를 돕는다면.
idev

5
이것은 보보 보보의 답과 토폴로지 적으로 동일합니다. 매핑은 2 토러스를 ℝ⁴에 포함합니다. which³에 포함시킬 때 필연적으로 얻을 수있는 메트릭 왜곡없이 가능합니다.
leftaroundabout

22

알았어 답은 3D 노이즈 로 원환 체 를 걸어 2D 텍스처를 생성하는 것입니다.

원환 체는 2 개의 dirs를 감 쌉니다

암호:

Color Sky( double x, double y, double z )
{
  // Calling PerlinNoise3( x,y,z ),
  // x, y, z _Must be_ between 0 and 1
  // for this to tile correctly
  double c=4, a=1; // torus parameters (controlling size)
  double xt = (c+a*cos(2*PI*y))*cos(2*PI*x);
  double yt = (c+a*cos(2*PI*y))*sin(2*PI*x);
  double zt = a*sin(2*PI*y);
  double val = PerlinNoise3D( xt,yt,zt, 1.5, 2, 12 ) ; // torus

  return val*val*cloudWhite + 2*val*(1-val)*gray + (1-val)*(1-val)*skyBlue ;
}

결과 :

한번:

기와 하늘

그리고 타일 :

타일을 보여주는


6
그것은 작동하지만 원환 체의 곡률로 인해 많은 왜곡이 발생하는 것처럼 보입니다.
Nathan Reed

1
당신은 실제로 위치를 모듈로 할 수 있지만, 나는이 질문에 대한 모든 훌륭한 / 창조적 인 답변을 좋아합니다. 같은 일을하는 많은 다른 방법들.

실제로 0-1 값을 사용하고 싶지 않지만 0-0.9999 ... 값을 사용하고 싶습니다. 그래서 당신은 x / width, y / height 등을 사용할 것입니다. 그렇지 않으면 이음새가 일치하지 않습니다 (반대쪽 가장자리가 정확히 같은 픽셀을 만듭니다). 또한 PerlinNoise3D () 함수는 결과 값에 대한 클램핑이 필요하거나 일부 픽셀 값이 오버플로하는 것처럼 보입니다.
idev

@Nathan, 당신은 왜곡을 해결하는 방법을 알고 있습니까?
idev

2
@idev 왜곡 문제를 해결하는 방법은이 질문의 최상위 답변에 4D 방법을 사용하는 것입니다. ;)
Nathan Reed

16

내가 생각할 수있는 간단한 방법은 노이즈 함수의 출력을 가져 와서 두 배 크기의 이미지로 미러 / 플립하는 것입니다. 설명하기 어렵 기 때문에 여기에 이미지가 있습니다. 여기에 이미지 설명을 입력하십시오

자,이 경우에, 당신이 이것을 볼 때 당신이 한 일이 분명합니다. 이것을 해결할 수있는 두 가지 방법을 생각할 수 있습니다.

  1. 더 큰 이미지를 가져온 다음 그 위에 더 많은 노이즈를 생성 할 수 있지만 중간에 초점을 맞추면 가장자리가 동일하게 유지됩니다. 뇌가 단순히 거울상이 아니라고 생각하게하는 차이를 추가 할 수 있습니다.

  2. (이것이 가능한지 확실하지 않습니다.) 초기 이미지를 다르게 생성하기 위해 노이즈 기능에 대한 입력을 조사해 볼 수 있습니다. 시행 착오를 통해이 작업을 수행해야하지만 타일 / 미러를 할 때 눈을 끄는 기능을 찾아서 생성하지 않도록하십시오.

도움이 되었기를 바랍니다.


3
매우 좋지만 대칭입니다!
bobobobo

1
@bobobobo 그것이 다른 단계들이 완화 될 것이라고 생각했던 것입니다. 따라서이 방법을 사용하여 "기본"을 생성 한 다음 전체에 대한 세부 정보를 추가하여 미러링되지 않은 것처럼 보이게 할 수 있습니다.
Richard Marskell-Drackir

이런 종류의 일을 할 때 이상한 패턴이 나타나기 시작합니다. 이것은 특히 나비처럼 보입니다. 그러나 쉬운 해결책.
notlesh

이것은 너무 내 첫 번째 방법을했지만, 그것은 여기에, 눈에 보이는 문제가 있습니다 dl.dropbox.com/u/6620757/noise_seam.png을 당신이 플립 경계는 즉시의 기울기를 반전하여 노이즈 함수에 연결이 끊긴 원인이 교차으로 기능. 상단에 두 번째 노이즈 기능을 적용하더라도 출력에서 ​​여전히 볼 수 있습니다.
Jherico

좋은 생각이야 삼각파 기능을 사용하여 픽셀 쉐이더에서 쉽게 수행 할 수 있습니다 .tex2d(abs(abs(uv.x)%2.0-1.0), abs(abs(uv.y)%2.0-1.0))
tigrou

10

이 답변의 첫 번째 버전은 실제로 잘못되었습니다.

내가 성공적으로 사용한 방법은 노이즈 도메인을 타일링하는 것입니다. 즉, 기본 noise2()기능을 주기적으로 만드십시오 . 경우에 noise2()주기적이고 beta정수 노이즈 생성하는 단계와 동일한 기간을 가질 것이다 noise2().

어떻게 noise2()주기적으로 만들 수 있습니까? 대부분의 구현에서이 함수는 일종의 격자 노이즈를 사용합니다. 즉, 정수 좌표에서 난수를 가져와 보간합니다. 예를 들면 다음과 같습니다.

function InterpolatedNoise_1D(float x)

  integer_X    = int(x)
  fractional_X = x - integer_X

  v1 = SmoothedNoise1(integer_X)
  v2 = SmoothedNoise1(integer_X + 1)

  return Interpolate(v1 , v2 , fractional_X)

end function

이 함수는 정수 기간으로 주기적으로되도록 간단하게 수정할 수 있습니다. 한 줄만 추가하면됩니다.

integer_X = integer_X % Period

계산하기 전에 v1v2. 이런 식으로 정수 좌표의 값은 모든주기 단위를 반복하며 보간은 결과 함수가 매끄 럽도록합니다.

그러나 이것은 Period가 1보다 클 때만 작동한다는 점에 유의하십시오. 따라서 이음새가없는 텍스처를 만드는 데 실제로 사용하려면 1x1이 아니라 Period x Period 제곱을 샘플링해야합니다.


그러나 어떻게 noise2주기적으로 작성합니까 (1 단위와 같은 짧은 기간으로)? 나는 그것이 궁극적으로 묻는 질문이라고 생각합니다. 표준 Perlin 노이즈는 각 축에서주기가 256 인주기적인주기이지만 더 작은주기의 수정 된 노이즈를 원합니다.
Nathan Reed

@Nathan Reed noise2제안한대로 호출 하면 함수 자체가 주기적인지 여부에 관계없이 주기적으로 결과 얻을 수 있습니다. 때문에 인수가 매 1 개 단위를 랩 어라운드.
Nevermind

1
그러나 그리드 선에 이음새가 생기지 않습니까? 내가 놓친 것이 없다면 noise2 (0, 0.999)가 noise2 (0, 0) 근처에 있다고 보장 할 수는 없습니다.
Nathan Reed

1
@Nathan Reed 좋은 지적입니다. 사실, 방금 이전 코드를 다시 확인했는데 잘못되었습니다. 이제 답변을 편집하겠습니다.
Nevermind

큰! 이것은 실제로 좋은 대답입니다. +1 :)
Nathan Reed

6

또 다른 대안은 libnoise 라이브러리를 사용하여 노이즈를 생성하는 것입니다. 이론적으로 무한한 양의 공간에서 원활하게 노이즈를 생성 할 수 있습니다.

다음을 살펴보십시오 : http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile

위의 XNA 포트도 있습니다 : http://bigblackblock.com/tools/libnoisexna

XNA 포트를 사용하면 다음과 같이 할 수 있습니다.

Perlin perlin = new Perlin();
perlin.Frequency = 0.5f;                //height
perlin.Lacunarity = 2f;                 //frequency increase between octaves
perlin.OctaveCount = 5;                 //Number of passes
perlin.Persistence = 0.45f;             //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;

//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);

//Get a section
_map.GeneratePlanar(left, right, top, down);

GeneratePlanar는 나머지 텍스처와 매끄럽게 연결되는 각 방향으로 섹션을 가져 오기 위해 호출하는 함수입니다.

물론이 방법은 여러 표면에 사용할 수있는 단일 텍스처를 갖는 것보다 비용이 많이 듭니다. 임의의 타일 가능한 텍스처를 만들려는 경우 관심있는 것일 수 있습니다.


6

여기에 효과가있는 답변이 있지만 대부분은 복잡하고 느리고 문제가 있습니다.

정기적으로 노이즈 생성 기능을 사용하기 만하면 됩니다. 그게 다야!

Perlin의 "고급"노이즈 알고리즘을 기반으로 한 우수한 퍼블릭 도메인 구현은 여기 에서 찾을 수 있습니다 . 필요한 기능은 pnoise2입니다. 이 코드는 뾰족한 코멘트를했다 스테판 Gustavson에 의해 작성되었습니다 여기에 바로이 문제에 대해, 그리고 다른 사람이 잘못된 접근 방식을 촬영 한 방법. 구스타프 슨의 말을 듣고 그는 자신이 말하는 것을 알고 있습니다.

여기에 제시된 다양한 구면 투영법에 관해서는, 본질적으로 (천천히) 작동하지만 평평한 구인 2D 텍스처를 생성하여 가장자리가 더 응축되어 바람직하지 않은 효과가 발생할 수 있습니다. 물론, 2D 텍스처를 구체에 투영 하려는 경우이 방법이 필요하지만 이것이 요구되는 것은 아닙니다.


4

타 일드 노이즈를 수행하는 훨씬 간단한 방법은 다음과 같습니다.

셰이더 토이 코드의 펄린 노이즈 타일링

각 노이즈 규모에 대해 모듈 식 랩 어라운드를 사용합니다. 사용하는 주파수 스케일에 관계없이이 영역의 가장자리에 맞습니다. 따라서 훨씬 빠른 일반 2D 노이즈 만 사용해야합니다. 다음은 ShaderToy에서 확인할 수있는 라이브 WebGL 코드입니다. https://www.shadertoy.com/view/4dlGW2

최상위 세 함수는 모든 작업을 수행하며 fBM은 0.0 ~ 1.0 범위의 벡터 x / y로 전달됩니다.

// Tileable noise, for creating useful textures. By David Hoskins, Sept. 2013.
// It can be extrapolated to other types of randomised texture.

#define SHOW_TILING
#define TILES 2.0

//----------------------------------------------------------------------------------------
float Hash(in vec2 p, in float scale)
{
    // This is tiling part, adjusts with the scale...
    p = mod(p, scale);
    return fract(sin(dot(p, vec2(35.6898, 24.3563))) * 353753.373453);
}

//----------------------------------------------------------------------------------------
float Noise(in vec2 x, in float scale )
{
    x *= scale;

    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f*f*(3.0-2.0*f);
    //f = (1.0-cos(f*3.1415927)) * .5;
    float res = mix(mix(Hash(p,                  scale),
        Hash(p + vec2(1.0, 0.0), scale), f.x),
        mix(Hash(p + vec2(0.0, 1.0), scale),
        Hash(p + vec2(1.0, 1.0), scale), f.x), f.y);
    return res;
}

//----------------------------------------------------------------------------------------
float fBm(in vec2 p)
{
    float f = 0.4;
    // Change starting scale to any integer value...
    float scale = 14.0;
    float amp = 0.55;
    for (int i = 0; i < 8; i++)
    {
        f += Noise(p, scale) * amp;
        amp *= -.65;
        // Scale must be multiplied by an integer value...
        scale *= 2.0;
    }
    return f;
}

//----------------------------------------------------------------------------------------
void main(void)
{
    vec2 uv = gl_FragCoord.xy / iResolution.xy;

#ifdef SHOW_TILING
    uv *= TILES;
#endif

    // Do the noise cloud (fractal Brownian motion)
    float bri = fBm(uv);

    bri = min(bri * bri, 1.0); // ...cranked up the contrast for no reason.
    vec3 col = vec3(bri);

#ifdef SHOW_TILING
    vec2 pixel = (TILES / iResolution.xy);
    // Flash borders...
    if (uv.x > pixel.x && uv.y > pixel.y                                        // Not first pixel
    && (fract(uv.x) < pixel.x || fract(uv.y) < pixel.y) // Is it on a border?
    && mod(iGlobalTime-2.0, 4.0) < 2.0)                 // Flash every 2 seconds
    {
        col = vec3(1.0, 1.0, 0.0);
    }
#endif
    gl_FragColor = vec4(col,1.0);
}

1
이미지 링크가 종료되었습니다. 나는 가장 추측하고 그것을 당신이 게시 한 shadertoy 코드의 출력 스크린 샷으로 대체했습니다. 올바르지 않은 경우 원하는 이미지를 Stack Exchange 서버에 직접 다시 업로드하십시오.
Pikalek

3

좀했다 - 나쁘지 않은 결과가 타일 (에지 - 포장)의 가장자리 근처에 보간, 그러나 당신이 달성하기 위해 노력하고 정확한 노이즈 파라미터있어 어떤 영향에 따라 달라집니다. 다소 흐릿한 노이즈에 적합하며 스파이크 / 세밀한 노이즈에는 적합하지 않습니다.


0

비슷한 스레드에 대한 답을 찾기 위해이 스레드를 검사 한 다음이 파이썬 코드 개발자로부터 깨끗하고 컴팩트 한 솔루션을 사용하여 펄린 / 단순 노이즈에서 프랙탈 노이즈를 생성했습니다. 업데이트 된 코드는 이 (닫힌) 문제에서 제공 되며 "제너레이터"의 오른쪽에 대한 그라디언트를 왼쪽의 그라디언트와 동일하게 설정하는 데 재개 할 수 있습니다 (예 :

# Gradients
angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
gradients = np.dstack((np.cos(angles), np.sin(angles)))
# Make the noise tileable
gradients[-1,:] = gradients[0,:]
gradients[:,-1] = gradients[:,0]

우아하고 깨끗한 솔루션처럼 보입니다. 내 코드가 여기에 없기 때문에 전체 코드를 복사하지는 않지만 위의 링크에서 사용할 수 있습니다. 이것이 인공물이나 왜곡이없는 필자가 필요로하는 타일 가능한 프랙탈 2D 이미지를 생성하려는 누군가에게 유용 할 수 있기를 바랍니다.

tileable 프랙탈 지형

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.