개발/Shader / / 2023. 3. 21. 02:38

GLSL - Random Noise Pattern, Value Noise 랜덤 노이즈 패턴, 값 노이즈

반응형

https://thebookofshaders.com/11/

 

The Book of Shaders

Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders.

thebookofshaders.com

※ 다음 페이지를 참고하여 공부한 게시물입니다.

https://www.opentutorials.org/module/3659/24891

 

20. Noise1 - GLSL / Shader

20. Noise1 2019-05-07 15:23:45

www.opentutorials.org

 

Noise 

float i = floor(x);  // integer
y = rand(i); //rand() is described in the previous chapter

random 값을 이용해서 위와 같이 만들 수 있는데,

 

float i = floor(x);  // integer
float f = fract(x);  // fraction
y = mix(rand(i), rand(i + 1.0), f);

fract와 mix를 이용하면, 이렇게 그래프와 같은 형태를 만들 수 있다. fract로 0~1 사이의 값을 구하고, mix로 각 위치 사이를 점점 보간한다.

 

float i = floor(x);  // integer
float f = fract(x);  // fraction
y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));

smoothstep을 이용하면, 더 부드러운 모양으로 만들 수 있다.

 

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

float random(float f){
    return fract(sin(f*654.876)*915.876);
}

float noise(float val){
    float i = floor(val);
    float f = fract(val);
    
    float ret;
    ret = random(i);
    
    return ret;
}

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    
    vec3 col = vec3(noise(coord.x*10.));
    
    gl_FragColor = vec4(col, 1.0);
}

위 노이즈 그림은 너무 경계가 명확하다. 경계 부분을 흐릿하게 하는 여러 방법들이 있다.

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

float random(float f){
    return fract(sin(f*654.876)*915.876);
}

float noise(float val){
    float i = floor(val);
    float f = fract(val);
    
    float ret;
    ret = mix(random(i), random(i+1.), f);
    
    return ret;
}

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    
    vec3 col = vec3(noise(coord.x*10.));
    
    gl_FragColor = vec4(col, 1.0);
}

 

위에서 다루었던, fract와 mix를 이용한 방법으로 경계면을 흐릿하게 만들었다.

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

float random(float f){
    return fract(sin(f*654.876)*915.876);
}

float noise(float val){
    float i = floor(val);
    float f = fract(val);
    
    float ret;
    ret = mix(random(i), random(i + 1.), smoothstep(0., 1., f));
    
    return ret;
}

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    
    vec3 col = vec3(noise(coord.x*10.));
    
    gl_FragColor = vec4(col, 1.0);
}

smoothstep을 이용하는 방법으로 한 층 더 부드럽게 표현할 수 있지만, 더 좋은 방법이 있다고 한다.

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

float random(float f){
    return fract(sin(f*654.876)*915.876);
}

float noise(float val){
    float i = floor(val);
    float f = fract(val);
    
    float ret;
    f = f*f*f*(f*(f*6.-15.) + 10.);
    ret = mix(random(i), random(i + 1.), f);
    
    return ret;
}

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    
    vec3 col = vec3(noise(coord.x*10.));
    
    gl_FragColor = vec4(col, 1.0);
}

위에서 f 식이 5차 방정식으로 상당히 큰데, 쉐이더 그래프 수식을 이용하면 더 경계부분을 매끄럽게 하는 좋은 방법이다. 

 

2D Noise

위에서 다루었던 1D Noise와는 달리 2D Noise는 사각형의 네 모서리를 보간한다. 만약 3D Noise를 만들고 싶다면, 8개의 모서리를 보간해야하고, 이렇게 특정 값들을 지정해서 보간기 때문에 이러한 방법을 value noise라고 한다. 

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

float random(vec2 v){
    float f = dot(v, vec2(23.53, 32.124));
    
    return fract(sin(f*12.432)*16234.12412);
}

float noise(vec2 v){
    vec2 i = floor(v);
    vec2 f = fract(v);
    
    vec2 v1 = i;
    vec2 v2 = i+vec2(1., 0.);
    vec2 v3 = i+vec2(0., 1.);
    vec2 v4 = i+vec2(1., 1.);
    
    float r1 = random(v1);
    float r2 = random(v2);
    float r3 = random(v3);
    float r4 = random(v4);
    
    float bot = mix(r1, r2, f.x);
    float top = mix(r3, r4, f.x);
    float ret = mix(bot, top, f.y);
    
    return ret;
}

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    coord *= 10.;
    
    vec3 col = vec3(noise(coord));
    
    gl_FragColor = vec4(col, 1.0);
}

이 노이즈는 f 경계 보간이 선형보간으로 이루어져 있어 경계면이 부드럽지 못하고 날카롭다.

 

각각의 모서리 v1~v4까지의 위치를 각각 랜덤 값 r1~r4로 만들고,

r1, r2를 x좌표 비율만큼 mix해서 bot, r3, r4를 x좌표 비율만큼 mix해서 top,

bot과 top을 해당 좌표의 y좌표 비율만큼 mix하면 noise가 완성된다. 

로직이 난해해서 이해하기가 어렵다.

 

bot과 top은 가로간의 색을 자연스럽게 mix하기 위해서 사용되고, ret은 이 두 개의 색들을 y좌표에 따라 자연스럽게 mix시켜서 line별로 랜덤색들을 연결시켜주는 역할을 한다. 

 

만약 ret을 사용하지 않고, bot 혹은 top으로만 noise를 만들게 되면 위와 같이 line의 경계가 생기고, 각 line들의 색도 위쪽과 아래쪽이 같은 색으로 표현된다.

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

float random(vec2 v){
    float f = dot(v, vec2(23.53, 32.124));
    
    return fract(sin(f*12.432)*16234.12412);
}

float noise(vec2 v){
    vec2 i = floor(v);
    vec2 f = fract(v);
    
    vec2 v1 = i;
    vec2 v2 = i+vec2(1., 0.);
    vec2 v3 = i+vec2(0., 1.);
    vec2 v4 = i+vec2(1., 1.);
    
    float r1 = random(v1);
    float r2 = random(v2);
    float r3 = random(v3);
    float r4 = random(v4);
    
    f = smoothstep(0., 1., f);
    float bot = mix(r1, r2, f.x);
    float top = mix(r3, r4, f.x);
    float ret = mix(bot, top, f.y);
    
    return ret;
}

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    coord *= 10.;
    
    vec3 col = vec3(noise(coord));
    
    gl_FragColor = vec4(col, 1.0);
}

smoothstep을 이용해서 경계부분의 보간을 비선형으로 바꾸었다. 노이즈의 경계가 더 부드러워졌다.

하지만 이러한 value noise의 경우 "blocky" 하다는 말을 한다. 노이즈의 형태가 네모네모한 느낌이다. 이러한 점을 해결한 것이 gradient noise라는 것인데, 이는 다음에 알아보자.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유