타일링 노이즈 – 절차적 GPU 패턴

Tiling Noise – Procedural GPU Patterns

catlikecoding.com ▲ 59 댓글 2 ibobev

요약

값 노이즈의 격자 좌표를 타일 경계에서 되감으면 이음매 없이 반복되는 타일링 노이즈를 만들 수 있습니다.

캣라이크 코딩이 Godot 4.7로 연재하는 셰이더 강좌 '절차적 GPU 패턴'의 아홉 번째 글입니다. 앞 편의 값 노이즈(격자점마다 난수를 두고 그 사이를 보간하는 노이즈)를 토러스(도넛 모양 모델)에 입히면 UV 0과 1이 만나는 곳에 이음매가 보입니다. 이 글은 그 이음매를 없애는 방법을 다룹니다.

왜 중요한가

  • 이웃 격자점을 섞는 노이즈는 UV의 소수 부분만 쓰는 방식으로는 부족하고, 경계에서 보간까지 이어 줘야 합니다.
  • 토러스처럼 텍스처 좌표가 한 바퀴 돌아 맞닿는 모델에도 셰이더 노이즈를 이음매 없이 입힐 수 있게 됩니다.

핵심 내용

  • 새 lattice01() 함수는 격자 좌표를 주파수만큼 옮겨, 주파수 4에서 마지막 칸이 (3, 4) 대신 (3, 0)을 섞게 합니다.
  • 타일링이 유지되려면 주파수가 정수여야 하고, 주파수 4.5로 시험하면 이음매가 다시 생깁니다.
  • 지원하는 UV 범위는 −1~2까지라서 3×3 타일은 이어지지만 5×5로 넓히면 깨집니다.
  • 격자점마다 시간 오프셋을 엇갈려 두기 때문에 애니메이션은 모든 옥타브 주파수가 짝수일 때만 이어집니다.
  • 미끄러지는 애니메이션은 이동량의 소수 부분만 더해 UV가 허용 범위를 넘지 않게 했습니다.

HN 반응

  • 한 이용자는 GPU 없이 쓸 수 있는 자바스크립트 펄린 노이즈 코드를 공유하며, 0~1 구간을 n번 반복하는 타일링 함수도 함께 소개했습니다.
  • 캣라이크 코딩이 오랫동안 좋은 학습 자료였다며 작성자 재스퍼 플릭에게 감사를 전하는 댓글도 있었습니다.

댓글

2개 표시 · 전체 2개
  1. Lerc HN

    GPU를 쓰지 않는 버전이 필요하시다면, 제가 가끔 쓰는 간결한 JavaScript 코드가 있습니다.

        const min = Math.min;
        const max = Math.max;
        const median = (a, b, c) => max(min(a, b), min(max(a, b), c));
    
    const intHash = x => {
          x *= 0xed5ad4bb;
          x ^= x >> 11;
          x = Math.imul(x,0xac4c1b51);
          x ^= x >> 15;
          x = Math.imul(x,0x31848bab);
          x ^= x >> 14;
          return x;
       }
    
    const makeRandom = seed => {
          let index=0;
          let random = (x=index) => {
            let pattern = intHash( (x + seed) & 0x7fffffff)
            let intValue = intHash( (x + pattern)  & 0x7fffffff);
            index=++x;
            return intValue / 0x7fffffff;
          }
          return random;
        }
    
    let ss=(v,a=0,b=1,w=v*v*v*(v*(v*6-15)+10))=>(1.0-w)*a+(w*b); 
        let positiveMod=(v,size,q=v%size)=>v<0?size+q:q;
        let clamp = a=> a<0?0:a>1?1:a;
        let perlinRandom = makeRandom(seed);
        let perlin=(x,y,wrapX=256,wrapY=wrapX)=> {    
          let dg=(ix,iy,gi=perlinRandom(positiveMod(iy,wrapY)*wrapX+positiveMod(ix,wrapX)*2)*Math.PI*2)  =>
              ((x-ix)*Math.sin(gi)) + ((y-iy)*Math.cos(gi));
          let u=0|x;
          let v=0|y;
          let sx=x-u; 
          let sy=y-v;
          let u1=(u+1);
          let v1=(v+1);
          return ss(sy,ss(sx,dg(u,v),dg(u1,v)),ss(sx,dg(u,v1),dg(u1,v1)));
        }
    
    let wrapPerlin=(x,y,wrapX=2,wrapY=wrapX)=>perlin(x*wrapX,y*wrapY,wrapX,wrapY);
    

    코드 크기를 줄이려고 짠 것이라 좀 난해합니다. ss()는 smoothstep입니다. dg()는 아무 Perlin 노이즈 설명이나 보면 금방 알아볼 수 있습니다. 0..1 구간을 n번 반복시키고 싶을 때는 wrapPerlin을 쓰면 됩니다.

  2. coinfused HN

    Catlike Coding은 오래전부터 정말 좋은 자료였죠. Jasper Flick님, 감사합니다!

Hacker News에서 보기 ↗