GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

펄린 노이즈 지형 9종 — 라이브러리 없이 계산만으로

펄린 노이즈는 이웃한 값이 매끄럽게 이어지는 난수라, 산·구름·바다·별밤 같은 자연의 형태를 숫자 계산만으로 그려 내는 밑재료입니다. 이 아홉 종을 노이즈 라이브러리 하나 없이 캔버스 2D 로 직접 구현했고, 실측에서 가장 넓게 움직인 08 강줄기 침식이 누적면적 51.86퍼센트,

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

순서는 화려함의 순서가 아니라 노이즈가 지형으로 쌓이는 순서입니다. 먼저 노이즈 한 줄을 높이로 읽는 단면이 생기고(01·04), 이차원으로 펼쳐 지도가 되고(02·08), 하늘의 현상이 흐르는 장면이 되고(03·05·09), 마지막에 날카로운 변형과 파낸 공간으로 지형에 굴곡이 생깁니다(06·07). 씨앗 하나로 무늬를 다시 뽑는 CSS 배경 패턴과 픽셀을 직접 계산하는 캔버스 특수효과가 이 편의 이웃입니다.

01지형 단면 — fbm 능선 실루엣

아웃도어·등산 랜딩 히어로 배경에 쓰는, 옥타브 4겹의 fbm 으로 그린 능선 실루엣 한 줄입니다.

지형 단면 — fbm 능선 실루엣
새 창에서 열기

01 · 지형 단면 — fbm 능선 실루엣 — 측정 누적면적 31.644% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

fbmlineTorequestAnimationFrame

능선은 왜 겹겹이 쌓는가

var OCT = 4;   // 옥타브 4겹

// fbm — 주파수 2배·진폭 절반으로 4겹 쌓는다. 옥타브마다 자기 위상을 따로 흘려보낸다
// (i+1) 배로 밀기 때문에 고주파 옥타브일수록 더 빨리 밀리고 — 능선이 부위별로 다시 깎인다
function fbm1(x, sec, flow) {
  var sum = 0, amp = 0.5, norm = 0, f = 1;
  for (var i = 0; i < OCT; i++) {
    var phase = sec * flow * (i + 1);
    sum += vnoise1(x * f + phase) * amp;
    norm += amp;
    amp *= 0.5;
    f *= 2;
  }
  return sum / norm;
}

노이즈 한 겹만으로는 굵은 언덕 하나가 전부라 능선이 되지 않는데, 주파수를 두 배로 올리고 진폭을 절반으로 낮춘 겹을 네 번 쌓으면 굵은 산맥 위에 잔 굴곡이 얹히고 각 옥타브의 위상이 시간에 (i+1) 배 속도로 흘러 능선이 부위별로 다시 깎이며, 원경·근경 두 실루엣은 같은 fbm1 에 스케일과 흐름만 다르게 준 겹입니다(실측: 누적면적 31.64퍼센트, 23/23 프레임 움직임).

02등고선 지도 — 고도띠 + 등고선 캔버스

여행·부동산·기상 대시보드 지도 카드에 어울리는, 고도띠 5단과 얇은 등고선을 겹친 지도입니다.

등고선 지도 — 고도띠 + 등고선 캔버스
새 창에서 열기

02 · 등고선 지도 — 고도띠 + 등고선 캔버스 — 측정 누적면적 42.324% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

createImageDataputImageDatafbm

등고선은 어떻게 그냥 픽셀에서 나오나

var MW = 160, MH = 100;                 // 높이맵 버퍼 해상도 — 화면이 커져도 이 값은 그대로다
var buf = sctx.createImageData(MW, MH); // 한 번만 만들어 두고 프레임마다 같은 배열을 덮어쓴다
var px = buf.data;
var heights = new Float32Array(MW * MH); // 문턱선 검사에 다시 쓰려고 높이를 한 프레임만큼 남겨 둔다

var SCALE = 0.052;          // 격자 간격 — 작을수록 산과 골짜기가 굵어진다(실측 튜닝)
var FLOW = 0.5;             // 시간축이 흐르는 속도 — 산맥이 부푸는 빠르기
var OCT = 5;                // fbm 옥타브 수 — 많을수록 등고선이 촘촘해진다
var LINE_LEVELS = 11;       // 등고선 문턱 칸 수(색 밴드 5단과 별개 — 더 촘촘해야 지도처럼 보인다)

2차원 fbm 높이맵을 160×100 픽셀 버퍼에 픽셀마다 계산해 고도띠 5단 색을 칠하고, 같은 높이맵을 11단 문턱으로 다시 나눠 이웃 8칸과 등급이 다른 경계 픽셀만 진하게 덮으면 marching squares 없이도 등고선이 나옵니다. 시간은 노이즈의 셋째 축으로 흘러 산맥이 서서히 부풀고 줄어들며 등고선도 같이 다시 그어지고, 버퍼를 16,000 픽셀로 고정하고 화면 크기는 확대 보간에 맡겼으니 창을 키워도 계산량은 그대로입니다(실측: 누적면적 42.32퍼센트).

03구름 필드 — 흐르는 fbm 구름 층

날씨 앱·여행 사이트 배경 장식에 쓰는, 천천히 흘러가는 구름 층입니다.

구름 필드 — 흐르는 fbm 구름 층
새 창에서 열기

03 · 구름 필드 — 흐르는 fbm 구름 층 — 측정 누적면적 35.34% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

createImageDataputImageDatarequestAnimationFrame

값 노이즈와 펄린 노이즈는 무엇이 다른가

function lp(a, b, t) { return a + (b - a) * t; }
function gdot(h, x, y, z) { var i = (h & 255) * 3; return GRAD[i] * x + GRAD[i + 1] * y + GRAD[i + 2] * z; }

function noise3(x, y, z) {
  var xf0 = Math.floor(x), yf0 = Math.floor(y), zf0 = Math.floor(z);
  var xf = x - xf0, yf = y - yf0, zf = z - zf0;
  var X = xf0 & 255, Y = yf0 & 255, Z = zf0 & 255;
  var u = fade(xf), v = fade(yf), w = fade(zf);
  var A = PERM[X] + Y, AA = PERM[A] + Z, AB = PERM[A + 1] + Z;
  var B = PERM[X + 1] + Y, BA = PERM[B] + Z, BB = PERM[B + 1] + Z;
  return lp(
    lp(lp(gdot(PERM[AA], xf, yf, zf),             gdot(PERM[BA], xf - 1, yf, zf), u),
       lp(gdot(PERM[AB], xf, yf - 1, zf),         gdot(PERM[BB], xf - 1, yf - 1, zf), u), v),
    lp(lp(gdot(PERM[AA + 1], xf, yf, zf - 1),     gdot(PERM[BA + 1], xf - 1, yf, zf - 1), u),
       lp(gdot(PERM[AB + 1], xf, yf - 1, zf - 1), gdot(PERM[BB + 1], xf - 1, yf - 1, zf - 1), u), v),
    w);
}

값 노이즈는 격자 꼭짓점마다 난수를 하나 두고 그 사이를 보간하지만, 펄린 노이즈는 격자점마다 방향 벡터(그라디언트)를 두고 샘플점까지의 거리 벡터와 내적합니다. 격자 중심에서 값이 정확히 0 이 되는 이 성질 덕에 결에 흐름이 생겨 구름처럼 느리게 흐느적거리는 표현에 값 노이즈보다 어울리고, 구름 양 다이얼을 돌리면 하늘이 맑음과 흐림 사이를 오가며 구름 가장자리가 젖듯 번집니다(실측: 누적면적 35.34퍼센트).

04바다 파고 — 노이즈 파고 단면

해양·서핑·리조트 랜딩 배경에 쓰는, 규칙 없이 오르내리는 바다 단면입니다.

바다 파고 — 노이즈 파고 단면
새 창에서 열기

04 · 바다 파고 — 노이즈 파고 단면 — 측정 누적면적 29.956% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

fbmquadraticCurveTorequestAnimationFrame

파도는 왜 사인파가 아니라 노이즈인가

// 겹 하나의 파고 y 좌표 — fbm 값(0~1 로 대략 몰림)을 대비를 편 뒤 base 위아래로 흔든다
function swellY(x, sec, cfg) {
  var n = fbm1(x * cfg.scale + cfg.offset, sec, cfg.flow, cfg.oct);
  n = n * 2 - 0.5;                    // 값 노이즈는 가운데로 몰린다 — 대비를 한 번 펴 준다
  return cfg.base - n * cfg.amp;
}

// 점들을 노이즈 샘플링 창을 팬(pan)해 가며 뽑고 quadraticCurveTo(중점을 앵커로) 로 이어
// 부드러운 파도 단면을 그린 뒤 캔버스 바닥까지 채운다. 근경 위에는 옅은 포말 하이라이트를 덧긋는다.
function drawSwell(sec, cfg, topColor, bottomColor, foam) {
  var step = 6;
  var pts = [];
  for (var x = 0; x <= W; x += step) pts.push([x, swellY(x, sec, cfg)]);

사인파로 파도를 그리면 마루와 골이 같은 간격으로 돌아와 수영장 물결이 되는데, 가로 위치마다 fbm 값을 파고로 읽으면 다음 마루가 어디서 얼마나 솟을지 예측이 되지 않으니 바다가 되고, 점 사이 중점을 앵커로 quadraticCurveTo 로 이으면 노이즈의 계단이 부드러운 물매로 풀립니다. 수평선·중경·근경 세 겹에 스케일과 흐름 속도를 다르게 줘서 멀수록 느리고 잔잔하게, 가까울수록 빠르고 거칠게 출렁입니다(실측: 누적면적 29.96퍼센트, 근경 최대 면적 12.54퍼센트).

05오로라 커튼 — 세로 노이즈 광막

야경·이벤트·겨울 캠페인 히어로에 쓰는, 장막이 흔들리며 겹치는 오로라입니다.

오로라 커튼 — 세로 노이즈 광막
새 창에서 열기

05 · 오로라 커튼 — 세로 노이즈 광막 — 측정 누적면적 42.083% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

createImageDataglobalCompositeOperationfbm

겹칠수록 밝아지는 lighter 합성

function paintBand(b, z, cLow, cHigh) {
  var P = BP[b];
  var sig = P.sig, inv2 = 1 / (2 * sig * sig);
  var cut = 3.5 * sig;
  cpx.fill(0);
  var sway = new Float32Array(NH);      // 중심선의 좌우 치우침 — 행마다 한 번 샘플링한다
  for (var yy = 0; yy < NH; yy++) {
    var sv = fbm(P.ph * 0.13, yy * 0.02, z * P.spd, 3);
    sway[yy] = (sv - 0.5) * 2 * P.amp;
  }
function paint(sec) {
  var z = sec * FLOW;                 // 셋째 축 = 시간
  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = 'high';
  paintBack(z);
  ctx.globalCompositeOperation = 'source-over';
  ctx.drawImage(bg, 0, 0, NW, NH, 0, 0, W, H);
  // ③ 포개기 — lighter 는 src+dst 를 더한다. 장막이 겹치는 곳이 절로 밝아진다
  ctx.globalCompositeOperation = 'lighter';
  for (var b = 0; b < NB; b++) {
    paintBand(b, z, b % 2 ? CB1 : CA1, b % 2 ? CB2 : CA2);
    ctx.drawImage(curt, 0, 0, NW, NH, 0, 0, W, H);
  }
  ctx.globalCompositeOperation = 'source-over';
}

장막 여섯 장은 각각 세로 프로파일(위가 짙고 아래로 흐려짐)에 가우스 단면을 곱하고, 중심선을 fbm 으로 좌우로 흔들며 세로 주름이 시간축을 타고 흘러내리게 합니다. 겹치는 곳이 절로 밝아지는 것은 빛의 덧셈이라 오로라의 물리와 같은 방향이고, 프레임당 최대 면적은 4.05퍼센트로 좁지만 누적면적은 42.08퍼센트 — 장막 전체가 늘 다시 짜여지고 있다는 뜻입니다.

06사구 능선 — 리지드 노이즈 모래언덕

여행·사막 투어·미니멀 브랜드 배경에 쓰는, 겹겹이 물러나는 사구입니다.

사구 능선 — 리지드 노이즈 모래언덕
새 창에서 열기

06 · 사구 능선 — 리지드 노이즈 모래언덕 — 측정 누적면적 30.374% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

fbmlineToglobalAlpha

절댓값을 뒤집으면 능선이 선다

// 리지드 fbm — vnoise1(0..1) 을 -1..1 로 펴고 절댓값 반전(1-|n|) 해 능선을 뾰족하게 세운다.
// 옥타브마다 자기 위상을 따로 흘려보내 능선이 시간에 따라 부위별로 다시 깎인다(01 과 같은 패턴).
function ridgedFbm1(x, sec, flow) {
  var sum = 0, amp = 0.55, norm = 0, f = 1;
  for (var i = 0; i < OCT; i++) {
    var phase = sec * flow * (i + 1);
    var n = vnoise1(x * f + phase);
    var s = n * 2 - 1;              // -1..1
    var r = 1 - Math.abs(s);        // 리지드 — 경계(n=0.5)에서 1, 골에서 0
    sum += r * amp;
    norm += amp;
    amp *= 0.5;
    f *= 2;
  }
  return sum / norm; // 0..1
}

fbm 을 그대로 쓰면 봉우리가 완만하게 뭉개져 구릉이 되는데, 값을 -1 에서 1 로 펴고 절댓값을 뒤집는(1-|n|) 리지드 변형은 노이즈가 부호를 바꾸는 경계에서 1 이 되니, 옥타브를 쌓을수록 그 경계들을 따라 날카로운 능선이 섭니다. 앞뒤 두 겹을 서로 다른 globalAlpha 로 칠해 뒤쪽 사구가 아지랑이처럼 흐릿하게 물러나게 한 것도 계산으로 만든 원근입니다(실측: 누적면적 30.37퍼센트, 강도 52.6).

07동굴 단면 — 노이즈로 파낸 공동

탐험·게임·다크 판타지 랜딩에 쓰는, 종유석과 석순이 달린 동굴 단면입니다.

동굴 단면 — 노이즈로 파낸 공동
새 창에서 열기

07 · 동굴 단면 — 노이즈로 파낸 공동 — 측정 누적면적 31.048% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

fbmquadraticCurveToclip

천장과 바닥 사이만 남기는 clip

function paint(sec) {
  ctx.clearRect(0, 0, W, H);
  // 1) 바위 몸통 — 화면 전체를 어두운 암석 톤으로 깐다
  var rock = ctx.createLinearGradient(0, 0, 0, H);
  rock.addColorStop(0, C_ROCK_TOP);
  rock.addColorStop(1, C_ROCK_BOTTOM);
  ctx.fillStyle = rock;
  ctx.fillRect(0, 0, W, H);

  // 2) 공동 — 천장·바닥 두 fbm 곡선 사이만 clip 으로 남긴다
  ctx.save();
  ctx.beginPath();
  ctx.moveTo(0, ceilY(0, sec));
  for (var x = 2; x <= W; x += 2) ctx.lineTo(x, ceilY(x, sec));
  for (var x2 = W; x2 >= 0; x2 -= 2) ctx.lineTo(x2, floorY(x2, sec));
  ctx.closePath();
  ctx.clip();
  // (clip 이 걸린 상태에서 공동 그라디언트·아래 채광·종유석을 그린 뒤 restore)

천장 곡선은 fbm, 바닥 곡선도 다른 오프셋의 fbm 이라 두 곡선 사이가 노이즈로 파낸 공동이 되고, clip 이 걸린 안쪽에만 공동 색과 아래에서 올라오는 채광·종유석을 그려 바위가 다시 덮으며, 종유석과 석순은 x 칸마다 노이즈를 하나 더 읽어 문턱을 넘는 자리에만 그 값으로 길이를 정해 자라니 큰 것 옆에 작은 것이 무리 지은 진짜 동굴 같은 결이 드러나고 시드를 바꾸면 다른 동굴이 뚫립니다(실측: 강도 평균 120.2 — 밤하늘 09의 179.8 다음으로 진하게 변화).

08강줄기 침식 — 최급경사 흐름 선

지리·환경·데이터 스토리텔링 삽화에 쓰는, 계곡을 따라 굽이치는 강줄기입니다.

강줄기 침식 — 최급경사 흐름 선
새 창에서 열기

08 · 강줄기 침식 — 최급경사 흐름 선 — 측정 누적면적 51.864% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

fbmlineTorequestAnimationFrame

강줄기는 노이즈가 아니라 계산이 그린다

function trace(ix) {
  var path = [ix];
  var x = ix % GW, y = (ix / GW) | 0;
  for (var s = 0; s < MAX_STEP; s++) {
    var best = -1, bestH = heights[y * GW + x];
    for (var dy = -1; dy <= 1; dy++) {
      var ny = y + dy; if (ny < 0 || ny >= GH) continue;
      for (var dx = -1; dx <= 1; dx++) {
        if (dx === 0 && dy === 0) continue;
        var nx = x + dx; if (nx < 0 || nx >= GW) continue;
        var h = heights[ny * GW + nx];
        if (h < bestH) { bestH = h; best = ny * GW + nx; }
      }
    }
    if (best < 0) break;                 // 분지 도달 — 강이 호수로 닫는다
    x = best % GW; y = (best / GW) | 0;
    path.push(best);
    if (y === GH - 1 || y === 0) break;  // 지도 밖(바다)으로 나갔다
  }
  return path;
}

46×28 격자마다 fbm 높이를 구해 지형을 칠하고, 수원 일곱 곳에서 시작해 이웃 8칸 중 값이 가장 낮은 칸으로 한 칸씩 내려가는 최급경사 추적을 반복합니다. 물길이 계곡을 따라 굽이치는 것은 노이즈의 모양이 아니라 '어디가 더 낮은가'라는 계산의 결과고, 물길은 매 프레임 캐시 없이 처음부터 다시 추적되며 노이즈의 시간축이 흘러 산이 다시 태어나면 물길도 같이 다시 패입니다(실측: 누적면적 51.86퍼센트로 이 편에서 가장 넓게 움직임).

09별 하늘 — 노이즈 밀도 별자리

밤 테마·포트폴리오·이벤트 카운트다운 배경에 쓰는, 별의 밀도가 노이즈 결을 따르는 별밤입니다.

별 하늘 — 노이즈 밀도 별자리
새 창에서 열기

09 · 별 하늘 — 노이즈 밀도 별자리 — 측정 누적면적 34.494% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

createImageDataputImageDatafbm

별이 태어나는 자리도 반짝임도 노이즈다

var D_SCALE = 0.09, D_FLOW = 0.32, D_THRESH = 0.60;    // 밀도 노이즈 — 별이 태어나는 문턱
var N_SCALE = 0.018, N_FLOW = 0.5;                     // 성운 노이즈 — 저주파(굵은 결), 밴드 경계가 넘실댄다
var T_SCALE = 0.35, T_FLOW = 1.7;                      // 반짝임 노이즈 — 별마다 다른 위상으로 흔들림
      // 밀도 — 문턱을 넘는 칸에만 별을 얹는다(별의 자리 자체가 fbm 결을 따른다)
      var d = fbm(x * D_SCALE + 500.7, dy + 233.9, zd, 3);
      if (d > D_THRESH) {
        // 반짝임 — 별마다 자기 좌표를 오프셋으로 쓰는 다른 위상 노이즈로 밝기를 흔든다
        var tw = fbm(x * T_SCALE + 811.2, y * T_SCALE + 47.6, sec * T_FLOW, 2);
        var edge = (d - D_THRESH) / (1 - D_THRESH);          // 문턱 위로 얼마나 솟았나(0..1)
        var glow = Math.min(1, edge * 1.6 + tw * 0.6);
        r = STAR_DIM[0] + (STAR[0] - STAR_DIM[0]) * glow;
        g = STAR_DIM[1] + (STAR[1] - STAR_DIM[1]) * glow;
        b = STAR_DIM[2] + (STAR[2] - STAR_DIM[2]) * glow;
        stars++;
      }

균등 난수로 별을 뿌리면 우주가 아니라 소금을 흩뿌린 종이가 되는데, 밀도 노이즈가 문턱을 넘는 칸에만 별을 놓으면 별이 무리와 빈 공간을 이루어 은하수의 결이 생기고 각 별의 밝기는 자기 좌표를 오프셋으로 쓰는 또 다른 노이즈로 흔들려 반짝이며, 배경 성운도 같은 방식의 저주파 fbm 을 22단 밴드로 나눠 칠하니 화면 전체가 계산으로 넘실댑니다(실측: 강도 평균 179.8 — 아홉 종 중 가장 진하게 변화. 프레임당 면적은 2.06퍼센트로 좁지만 누적 34.49퍼센트).

펄린 노이즈 알고리즘은 어떻게 지형이 되나?

한 편을 통틀어 원리는 하나입니다. 노이즈는 격자 위의 난수를 이웃과 매끄럽게 이어 주는 함수일 뿐인데, 그 값을 높이로 읽으면 능선·사구·바다가 되고, 문턱으로 자르면 등고선·구름·별의 밀도가 되고, 가장 낮은 이웃을 따라가면 강줄기가 됩니다. 펄린 노이즈 구현 자체는 시드가 섞은 순서표 하나와 격자점마다의 내적 계산 몇 줄이 전부라 외부 라이브러리가 필요 없고, 같은 시드를 넣으면 같은 테이블이 재생성되어 같은 지형이 돌아옵니다 — 모든 판에 붙은 시드 입력이 이 재생성의 증명입니다. 프레임마다 달라지는 것은 노이즈의 셋째 축(시간)뿐이라 계산은 결정적이면서 화면은 살아 있습니다.

어디서 깨지는가 — 능선이 좌우로 똑같이 반복되던 첫 판

첫 판의 01 능선은 그럴듯한 산을 그리다가 화면을 벗어난 자리에서 처음과 똑같은 산을 다시 그리고 있었습니다. 격자 순서표를 256 칸으로 두고 좌표를 & 255 로 잘라 쓰다 보니, 노이즈 샘플링 위치가 256 격자를 한 바퀴 돌면 값이 처음으로 되돌아오는 주기성이 그대로 드러난 것입니다. 옥타브마다 스케일을 다르게 주니 주기가 어긋나 눈속임은 됐지만, 원경과 근경의 오프셋이 우연히 주기의 배수 근처에 놓이면 두 실루엣이 같은 간격으로 도는 게 눈에 잡혔습니다. 근본 고침은 옥타브·겹마다 독립된 오프셋(13.7, 241.3 같은 무리수 비슷한 값)을 주는 것이었습니다. 등고선 지도에서도 채색 문턱(5단)과 선 문턱(11단)이 어긋나지 않으면 등고선이 고도띠 경계 위에 그대로 붙어 지도가 아니라 외곽선 도안이 된 적이 있었습니다 — 문턱 밀도를 색보다 촘촘하게 두고 8-이웃 검사로 경계만 고르는 지금의 구조는 그 수리의 결과입니다. 아홉 판의 완성된 소스와 React 판은 글 아래 암호 zip 안에 들어 있고, 푸는 암호는 p2v2mgjz 입니다.

변형 세 가지와 접근성

이름 바꾼 값 느낌 한 줄
옥타브 수 02 등고선 5옥타브 → 8옥타브 결이 성기던 지형이 세밀하게 갈라진다
시드 09 별 하늘 시드 7 → 91 같은 규칙, 완전히 다른 별자리 배치
색 $terrain-mint → $terrain-peach 차가운 지형 톤에서 따뜻한 사막 톤으로

접근성에서 아홉 판은 같은 길을 따릅니다. prefers-reduced-motion: reduce 를 켠 사용자에게는 rAF 루프를 시작하지 않고 시드가 정한 정적 한 장만 그립니다 — 01 은 능선이 완성된 실루엣, 02 는 등고선이 그어진 지도, 08 은 물길이 다 팬 강줄기입니다. 이 정적 프레임에서도 시드 입력은 살아 있어 숫자를 바꾸면 즉시 다른 지형 한 장으로 다시 그려집니다. 노이즈 애니메이션이 꺼져도 지형이라는 결과는 남는 것이 이 구조의 요점입니다.

FAQ

펄린 노이즈 구현을 라이브러리 없이 직접 해도 성능이 될까요?

됩니다. 픽셀마다 계산하는 02·03·09 도 버퍼를 160×100(16,000 픽셀)으로 고정하고 화면 크기는 확대 보간에 맡기니 계산량이 창 크기와 무관합니다. 아홉 판 전부 23칸 프레임이 움직였고(실측), rAF 가 넘겨주는 시간값으로 프레임을 재니 느린 기기에서는 느리게 재생될 뿐 계산이 쌓이지는 않습니다.

심플렉스 노이즈는 이 코드를 어떻게 바꾸면 되나요?

심플렉스 노이즈는 격자를 정사각형 대신 삼각형으로 쪼개 보간할 모서리를 줄인 변형이라, 이 편의 값 노이즈에서 격자 접근과 보간 부분만 삼각형 좌표계로 바꾸면 됩니다. 계산량이 적고 방향별 결이 균일해 3차원 이상에서 유리하지만, 2차원 지형·하늘 표현에는 이 편의 펄린·값 노이즈로도 충분합니다.

소스 코드를 그대로 복사해도 되나요?

됩니다. 글 아래 암호 zip 에 아홉 판의 바닐라 판과 React 판이 통째로 들어 있어 붙여 넣으면 바로 돌아갑니다. 색은 각 판 scss 맨 위의 파스텔 토큰 변수로 갈아 끼우면 되고, 시드 입력에 숫자를 넣으면 같은 시드는 같은 지형을 다시 뽑어 줍니다.

더 읽을거리

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.