https://thebookofshaders.com/11/
The Book of Shaders
Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders.
thebookofshaders.com
1. 펄린 노이즈란?
먼저 펄린노이즈를 배우기전에 랜덤노이즈 부터 생각해보자. 랜덤노이즈는 값이 랜덤하게 나오는데 값의 앞뒤로 전혀 상관없는 애들이 튀어나오는 노이즈이다.
반면 펄린노이즈는 랜덤한 값이 나오지만 값의 앞뒤로 연속성이 있다. 이것이 랜덤노이즈와 가장 큰 차이점이다.
2. 그래서 어디다 씀?
펄린노이즈는 값이 랜덤하지만 값의 변화가 부드럽다. 이에따라 게임 이펙트 같은곳에 많이 사용된다.
구름을 만들거나 플라즈마 이펙트 같은걸 만들거나 할 때 사용한다.
3. 1-D Perlin Noise
1차원 펄린노이즈에 대해 먼저 공부해보자
아래코드는 아래와 같은 노이즈를 만든다
float i = floor(x); // integer
float f = fract(x); // fraction
y = rand(i);

이번에는 데이터의 연속성을 보장하기 위해 mix와 fractional을 통해 보간을 해보자
float i = floor(x); // integer
float f = fract(x); // fraction
y = mix(rand(i), rand(i + 1.0), f);

하지만 부드럽지 않다. 여기에 smoothstep을 이용한다면?
float i = floor(x); // integer
float f = fract(x); // fraction
y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));

위 예제는 가장 클래식한 펄린노이즈를 만드는 예시이다.
꼭 위와같은 방식으로 사용할 필요 없이 각자의 취향에따라 펄린노이즈를 만들 수 도 있다.
예제를 보면펄린노이즈를 만드는 법을 아래와 같이 정리할 수 있다.
- integer 부분을 통해 랜덤값을 얻어온다
- 연속된 값 부분도 랜덤값을 얻어온다.
- fractional과 이용해서 값 사이를 부드럽게 interpolation한다
위 내용을 보면 펄린노이즈는 연속성을 부여하기 위해서 integer와 fractional 두개를 모두 이용하는것을 알 수 있다.
4. 2-D Perlin Noise

2차원에서의 펄린노이즈는 위와 같다. x,y를 기점으로 z값을 노이즈로 생각한다면 마치 평면이 울긋 불긋한 노이즈형태를 나타낼 것이다.
그래서 펄린노이즈는 산과 같은 지형을 만들때 사용된다.
자 여기서 우리는 재밌는 사실을 알 수 있다. random함수가 진짜 random이 아니라는것
deterministic random 또는 pseudo-random이라고 불리고 인풋이 같으면 랜덤함수가 같은 값을 리턴한다.
내가 펄린노이즈로 산을 만들었는데 산을 만드는 랜덤함수가 진짜 랜덤이여서 캐릭터가 움직일 때 마다 산이 바뀐다고 생각하면 끔직할 것이다. 그리고 gpu에 진짜 랜덤함수 만드는것도 비용이라 함
자 그럼 2D 펄린노이즈는 어떻게 계산하냐?
1차원 펄린노이즈를 다시 복습해보자
정수를 통해 랜덤값을 계산한다
연속된 값부분도 정수를 이용해 랜덤값을 계산한다
fractional을 이용하여 부드럽게 interpolation한다
여기서 주목할 부분은 연속된 값이다. 2차원이니깐 총 4개를 계산해야 한다
( x, y )
( x+1, y )
( x, y+1 )
( x+1, y+1 )
그림으로 나타내면 아래와 같다

아래는 book of shader에 나오는 예제임

#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// 2D Random
float random (in vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898,78.233)))
* 43758.5453123);
}
// 2D Noise based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
// Four corners in 2D of a tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
// Smooth Interpolation
// Cubic Hermine Curve. Same as SmoothStep()
vec2 u = f*f*(3.0-2.0*f);
// u = smoothstep(0.,1.,f);
// Mix 4 coorners percentages
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
// Scale the coordinate system to see
// some noise in action
vec2 pos = vec2(st*5.0);
// Use the noise function
float n = noise(pos);
gl_FragColor = vec4(vec3(n), 1.0);
}
자 여기서 보면 마지막에 interpolation 하는 부분의 식이 조금 이해가 안간다.
이는 Bilinear interpolation을 조금 최적화해서 정리한 수식으로 사실상 아래와 동일하다
value = a*(1-x)*(1-y) +
b*x*(1-y) +
c*(1-x)*y +
d*x*y
그리고 사실상 아래의 로직대로 흘러가는것이다.
float L0 = mix(a, b, u.x);
float L1 = mix(c, d, u.x);
return mix(L0, L1, u.y);
x축방향으로 위아래로 보간한뒤, 그 보간값을 가지고 y축을 보간. 그래서 최종적으로 나온 형태가 아래와 같은것이다
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
이렇게 하는건 곱셈을 최소화 해서 최적화를 하기 위한것이라고함. 솔직히 식만보면 직관적으로 이해가 잘 안된다.
아직 내 수준에서는 그냥 외우거나 가져다 쓰면 되는 영역인것 같다.
'Shader' 카테고리의 다른 글
| color warping (0) | 2025.12.01 |
|---|---|
| fBM에 대한 정리 (0) | 2025.11.19 |
| 패턴을 위한 fract와 floor함수 (0) | 2025.11.18 |
| smoothstep으로 선그리기 (0) | 2025.11.17 |
| SDF 응용 - 선 그리기 (0) | 2025.11.09 |
댓글