한양과학기술고등학교

에듀테크 프로젝트 대시보드

펄린 노이즈

펄린 노이즈 (Perlin Noise)

Math.random()은 점끼리 아무 관계가 없어 지지직거린다. 펄린 노이즈는 부드럽게 이어지는 무작위를 만들어, 자연스러운 산·구름·불꽃·물결을 코드로 그려낸다.

어디에 쓰일까? 마인크래프트의 지형, 게임 속 무한 맵, 영화의 CG 구름과 불, 나무껍질·대리석 텍스처, 카메라 흔들림 연출까지 — "손으로 그리지 않았는데 자연스러운 것"의 뒤에는 거의 항상 노이즈 함수가 있다. 1983년 켄 펄린이 영화 트론의 질감을 만들다 고안했고, 이 공로로 아카데미 기술상을 받았다.

1. 그냥 난수 vs 부드러운 난수

왼쪽은 각 x마다 독립적으로 뽑은 난수, 오른쪽은 펄린 노이즈다. 둘 다 "무작위"지만, 오른쪽은 옆 값과 이어져 언덕과 골짜기처럼 보인다. 지형을 만들 때 우리에게 필요한 건 오른쪽이다.

순수 난수 — 이웃과 무관, 매 픽셀 점프

펄린 노이즈 — 이웃과 연속, 완만한 곡선

시드가 바뀌면 지형 전체가 달라진다 (재현 가능한 무작위)

2. 원리 — 격자 + 그래디언트 + 보간

펄린 노이즈의 한 칸은 이렇게 계산된다.

  1. 격자: 공간을 정수 격자로 나눈다. 점 x는 두 격자점 x0, x1 사이에 있다.
  2. 그래디언트: 각 격자점에 시드로 정해진 기울기(방향) 하나를 매단다. 점에서 격자점까지의 거리 벡터와 이 기울기를 내적해 각 격자점의 "영향값"을 구한다.
  3. 페이드(fade): 위치 t를 6t⁵-15t⁴+10t 곡선으로 완만하게 만든다. 이 덕분에 격자 경계에서 꺾임 없이 매끄럽게 이어진다.
  4. 보간: 두 영향값을 페이드된 t로 섞는다(lerp). 이게 최종 노이즈 값이다.
선형 보간으로 바꾸면 격자마다 각이 진다 — 스무스텝이 "펄린"의 핵심

보라 점 = 격자점의 그래디언트 방향, 하늘색 곡선 = 최종 1D 노이즈

3. 2D 노이즈 필드

x, y 두 방향으로 확장하면 흑백 이미지가 된다. 밝을수록 값이 크다. 스케일을 키우면 격자가 촘촘해져 무늬가 잘게 부서지고, 줄이면 크고 완만한 덩어리가 된다.

스케일 (격자 크기) 10

4. 옥타브를 쌓으면 진짜 지형이 된다 (fBm)

노이즈 한 겹은 밋밋하다. 주파수를 2배로 키우고 진폭을 절반으로 줄인 노이즈를 여러 겹 더하면 — 큰 산맥 위에 언덕, 그 위에 바위 굴곡 — 자연에 가까운 프랙탈 브라운 운동(fBm)이 나온다.

깊은 물 얕은 물 모래 풀 바위 눈

옥타브 수 5
지속성 0.50
라쿠나리티 2.0
해수면 0.00

5. 시간을 한 축으로 — 흐르는 구름

3D 노이즈에서 z축을 "시간"으로 천천히 움직이면, 무늬가 살아 움직인다. 아래는 옥타브를 쌓은 2D 노이즈를 매 프레임 조금씩 이동시킨 것이다.

속도

코드로 보는 2D 펄린 노이즈

켄 펄린의 "개선된 노이즈(2002)" 축약본이다. 512칸짜리 순열 테이블 perm이 시드 역할을 한다.

function fade(t)      { return t*t*t*(t*(t*6 - 15) + 10); }   // 6t^5-15t^4+10t^3
function lerp(a,b,t)  { return a + t*(b - a); }
function grad(h, x, y) {                       // 해시값 h로 8방향 중 하나 선택
  const g = [[1,1],[-1,1],[1,-1],[-1,-1],[1,0],[-1,0],[0,1],[0,-1]][h & 7];
  return g[0]*x + g[1]*y;
}

function perlin2(x, y, perm) {
  const xi = Math.floor(x) & 255, yi = Math.floor(y) & 255;
  const xf = x - Math.floor(x),  yf = y - Math.floor(y);
  const u = fade(xf), v = fade(yf);

  const aa = perm[perm[xi]     + yi    ];
  const ab = perm[perm[xi]     + yi + 1];
  const ba = perm[perm[xi + 1] + yi    ];
  const bb = perm[perm[xi + 1] + yi + 1];

  const x1 = lerp(grad(aa, xf,   yf  ), grad(ba, xf-1, yf  ), u);
  const x2 = lerp(grad(ab, xf,   yf-1), grad(bb, xf-1, yf-1), u);
  return lerp(x1, x2, v);                      // 대략 -1 ~ 1
}

// 옥타브 쌓기 (fBm)
function fbm(x, y, perm, octaves, persistence, lacunarity) {
  let amp = 1, freq = 1, sum = 0, norm = 0;
  for (let o = 0; o < octaves; o++) {
    sum  += amp * perlin2(x*freq, y*freq, perm);
    norm += amp;
    amp  *= persistence;
    freq *= lacunarity;
  }
  return sum / norm;
}

한눈에 정리

개념뜻
그래디언트 노이즈격자점마다 방향(기울기)을 두고 보간 — 펄린/심플렉스 노이즈
fade / smoothstep경계에서 매끄럽게 잇기 위한 완만화 곡선
옥타브 (fBm)주파수↑·진폭↓ 노이즈를 여러 겹 더해 디테일 추가
지속성겹마다 진폭이 줄어드는 비율 (거칠기 조절)
시드 / 순열 테이블같은 시드 → 같은 지형. 재현 가능한 무작위
심플렉스 노이즈펄린의 후속작. 고차원에서 더 빠르고 방향 편향이 적다
한 문장 요약: 펄린 노이즈는 "옆 칸과 사이좋게 이어지는 주사위"다. 이 부드러운 무작위를 배율만 바꿔 여러 겹 쌓으면, 세상에 없던 대륙 하나가 코드 몇 줄에서 태어난다.