본문 바로가기
Shader CG

2.6 나이테 Noise

by SimonLee 2023. 8. 19.

Editor : http://editor.thebookofshaders.com/

 

** The Book of Shaders Editor **

 

editor.thebookofshaders.com

 

나이테 모양을 렌더링 해봅시다.

나이테 그림을 보면 여러개의 라인으로 이루어져 있으며, 라인의 폭이 다르고 굴곡이 적용이 되어있습니다.

다음 단계로 나누어서 생각해 봅시다.

 

1) 라인을 그려봅니다.

위에서 아래 방향인 직선 여러개를 그려봅니다.

// Author : Simon.Lee
// http://patriciogonzalezvivo.com

#ifdef GL_ES
precision mediump float;
#endif

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

float lines(in vec2 pos, float scale){
    pos *= scale;
    return smoothstep(0.0, 1.0, abs( ( sin (pos.x*3.1415) ) ) );
}

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    st.y *= u_resolution.y/u_resolution.x;
    vec2 pos = st;

    float pattern = lines(pos.xy, 1.); // Draw lines
    vec3 color = vec3(pattern);
    gl_FragColor = vec4(color, 1.0);
}

lines 함수를 봅시다.  첫번째 인자는 st 좌표이고, 두번째는 scale 값입니다.

먼저 라인함수의 scale 값은 포지션에 곱함으로써 좌표를 확장할 수 있도록 합니다.

 

abs ( sin (pos.x * 3.14) )

=> pos.x 값의 범위가 0 ~ 1 이기 때문에, pos.x *  3.14 범위는 0 ~ 파이 가 됩니다.

=> sin ( 0 ~ 파이 ) 값은 0 ~ 1 범위가 됩니다.

Sin graph

=> pos.x 값이 0 에서 1 변화에 따라,  해당식의 범위는 0 -> 1 -> 0 으로 변화됩니다.

=> smoothstep (0., 1.,  < 식 > );

=> smoothstep 함수를 통해서 0 ~ 1 사이를 부드럽게 보간하여 아래 0 ~ 1 ~ 0 사이를 회색톤으로

만들어 줍니다.

사인 그래프가 y = x 그래프에 비해 초반에 급격하게 증가를 하고 점점 완만하게 증가를 하기 때문에,

검은색 부분이 상대적으로 짧은 것을 볼수가 있습니다.

 

이번에는

float pattern = lines(pos.xy, 1.);  코드에서 scale 변수의 값 1을 5로 변경해 봅시다.

5개의 세로 직선 모양이 생성이 되었네요.

frac을 사용하지 않아도 반복패턴이 나온 이유는

scale 값을 곱함으로써 pos 값이 같이 커지게 되는데,  

abs ( sin ) .. 함수의 인풋값과 상관없이

리턴 값은 input 값이 크던 적던 항상 0 ~ 1을 리턴하기 때문입니다.

이전에 했었던 타일링 챕터를 보시면 더 잘 이해하실 것 입니다.

 

위 그림의 선으로 나누어진 5개의 흰색 구간의 position.x 값은

0~1, 1~2, 2~3, 3~4, 4~5 의 구간이 됩니다.

이 구간 역시 sin 함수로 인해 동일하게 0 ~ 1의 값으로 변하게 됩니다.

 

참고로 rgb값이 동일하면 회색계열의 색상이 나옵니다.

(0, 0, 0) -> black  (1, 1, 1) -> White 나머지는 회색

 

2) 라인을 굴곡지게 만들어 봅니다.

선이 여러 방향으로 구부러진 것을 생각해보면, 회전을 적용해야 합니다.

회전 각도도 랜덤으로 되어야 하니,

랜덤 부분을 지난번에 사용했던 noise, random 함수로 사용합니다.

 

float random (in vec2 st) 
{
    return fract(sin(dot(st.xy,
                         vec2(12.9898,78.233)))
                * 43758.5453123);
}

float noise(vec2 st) 
{
    vec2 i = floor(st);
    vec2 f = fract(st);
    vec2 u = f*f*(3.0-2.0*f);
    return mix( mix( random( i + vec2(0.0,0.0) ),
                     random( i + vec2(1.0,0.0) ), u.x),
                mix( random( i + vec2(0.0,1.0) ),
                     random( i + vec2(1.0,1.0) ), u.x), u.y);
}

mat2 rotate2d(float angle)
{
    return mat2(cos(angle),-sin(angle),
                sin(angle),cos(angle));
}

 

pos = rotate2d ( noise ( pos ) ) * pos

rotate2d에 들어가는 파라메터는 각도 값이며 라디안 값을 사용합니다.

각도를 랜덤으로 생성하여 회전 매트릭스를 구하면, pos값과 곱해서 각 픽셀이 회전하도록 합니다.

noise 함수의 리턴값은 0 ~ 1 이기 때문에, 최대 1라디안의 회전이 이루어집니다. (최대 57도 )

 

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    st.y *= u_resolution.y/u_resolution.x;
    vec2 pos = st;
	
    pos = rotate2d(noise(pos)) * pos;
    float pattern = lines(pos.xy, 6.); // Draw lines
    vec3 color = vec3(pattern);
    gl_FragColor = vec4(color, 1.0);
}

휘어지는 형태의 라인이 만들어 졌습니다.

 

하지만 상단에 있는 나이테 모양과는 달리 구불구불한 느낌이 덜 한데, 좀 더 확장해서 관찰해 봅니다.

Sin 함수 계산을 통해 패턴이 적용되어 있기 때문에

 

pos.xy 값에 각각 확장해 주고 싶은 만큼 곱해줍니다.

해당 코드에서는 x 축은 3만큼 확장, y 축은 5만큼 확장 했습니다.

 

void main() 
{
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    st.y *= u_resolution.y/u_resolution.x;
    vec2 pos = st;
    pos  *= vec2(3., 5.);
    pos = rotate2d(noise(pos)) * pos;
    float pattern = lines(pos.xy, 5.); // Draw lines
    
    vec3 color = vec3(pattern);
    gl_FragColor = vec4(color, 1.0);
}

 

음 제대로 라인의 회전이 이루어 진것 같습니다.

조금 다르게 만들어볼까요 ?

 

나이테 모양의 직선을 좌에서 우로 바꾸어 보고, 선의 두께를 좀 얇게 바꾸어주세요 

생각해보시고 직접 해보세요

 

================================================================

 

좌에서 우로 바꾸는 것은 xy 축만 바꾸어 주면 될것 같습니다.

float pattern = lines(pos.yx, 1.); // Draw lines

 

조금 선이 두껍다고 생각이 들면 smoothstep 값 보간범위를 줄입니다. 

smoothstep(0., 0.3, abs( ( sin (pos.x * 3.1415) ) ) );

전체 코드 입니다.

// Author : SimonLee
// Site https://graphicsimon.tistory.com/24

#ifdef GL_ES
precision mediump float;
#endif

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

float random (in vec2 st) {
    return fract(sin(dot(st.xy,
                         vec2(12.9898,78.233)))
                * 43758.5453123);
}

// Value noise by Inigo Quilez - iq/2013
// https://www.shadertoy.com/view/lsf3WH
float noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    vec2 u = f*f*(3.0-2.0*f);
    return mix( mix( random( i + vec2(0.0,0.0) ),
                     random( i + vec2(1.0,0.0) ), u.x),
                mix( random( i + vec2(0.0,1.0) ),
                     random( i + vec2(1.0,1.0) ), u.x), u.y);
}

mat2 rotate2d(float angle){
    return mat2(cos(angle),-sin(angle),
                sin(angle),cos(angle));
}

float lines(in vec2 pos, float scale){
    pos *= scale;
    return smoothstep(0.0, 0.5, abs( ( sin (pos.x*3.1415) ) ) );
}


void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    st.y *= u_resolution.y/u_resolution.x;
	vec2 pos = st;
    
    //pos = st.yx * vec2(10.,3.);
    pos = st.xy * vec2(8., 2.);
    
    // Add noise
    pos = rotate2d( noise(pos) ) * pos;

    // Draw lines
    float pattern = 0.;
    pattern = lines(pos.xy, 3.);
	//pattern = Line(0.5, pos.y, 0.05);
    
    vec3 color = vec3(pattern);
    
    gl_FragColor = vec4(color, 1.0);
}

 

 

 

 

해당 코드는 아래 사이트에서 참조했습니다.

https://thebookofshaders.com/

 

'Shader CG' 카테고리의 다른 글

3D Scenes With Ray Marching  (0) 2024.06.27
2.7 Noise - 영혼이 빠져나감  (0) 2023.08.28
2.5 Gradient Noise  (0) 2023.08.17
2.4 Noise 함수 사용해보기  (0) 2023.08.12
2.3 Noise 미로 만들기  (0) 2023.08.11