개발/Shader / / 2023. 3. 13. 15:28

GLSL - Patterns combining an offset on x and y axis 동그라미가 x축, y축으로 움직이는 패턴

반응형

https://thebookofshaders.com/09/

 

The Book of Shaders

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

thebookofshaders.com

 

// Author @patriciogv - 2015 - patricio.io

#ifdef GL_ES
precision mediump float;
#endif

const float PI = 3.1415926535897932384626433832795;

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

vec2 movingTiles(vec2 _st, float _zoom, float _speed){
    _st *= _zoom;
    float time = u_time*_speed;
    if( fract(time)>0.5 ){
        if (fract( _st.y * 0.5) > 0.5){
            _st.x += fract(time)*2.0;
        } else {
            _st.x -= fract(time)*2.0;
        }
    } else {
        if (fract( _st.x * 0.5) > 0.5){
            _st.y += fract(time)*2.0;
        } else {
            _st.y -= fract(time)*2.0;
        }
    }
    return fract(_st);
}

float circle(vec2 _st, float _radius){
    vec2 pos = vec2(0.5)-_st;
    return smoothstep(1.0-_radius,1.0-_radius+_radius*0.2,1.-dot(pos,pos)*3.14);
}

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

    st = movingTiles(st,10.,0.5);

    vec3 color = vec3( 1.0-circle(st, 0.3 ) );

    gl_FragColor = vec4(color,1.0);
}

사이트에 있던 내용 중 위 신기한 패턴을 한 번 공부해보자.

 

먼저 moving tiles가 어떤 원리로 동작하는지를 알아보자. movingTiles()는 coord 좌표값과, zoom, speed로 매개변수가 구성되어있다. 

zoom은 coord에 해당 크기만큼 곱하여 화면에 띄우는 좌표크기를 키운다.

 

코드에서 fract(time)을 처음 보았을 때 헷갈렸는데, fract(time)의 경우 주기라고 보면 편하다. 시간이 진행됨에 따라서 그 결과값이 0~1까지 반복하게 되는데, 여기서는 0.5보다 큰 경우와 작은 경우를 나누어 타이밍을 절반씩 나누어 준다.

 

fract(st.y)와 fract(st.x)도 있는데, 각각 축의 값이 커질수록 결과가 0~1을 반복하는 위와 같은 그래프의 형태로 이루어진다.

fract(st.y), fract(st.x)는 각각의 1x1 칸 단위로 움직임을 주기 위해서 사용된다.

 

if (fract( _st.y * 0.5) > 0.5){
    _st.x += fract(time)*2.0;
} else {
    _st.x -= fract(time)*2.0;
}

코드에서는 이렇게 _st.y에 0.5를 곱하고, 안에 fract(time)에는 2.0을 곱한다. 이게 무엇을 뜻하는 걸까?

 

fract(time)에 2.0을 곱하는 것은 조절해보면 2의 배수인 것이 각각의 칸이 자연스럽게 움직인다. 이 이유는 이후에 알아보도록 하고, _st.y에 0.5를 곱하는 이유를 보자.

 

y좌표에 0.5를 곱하면 좌표 범위는 2배로 증가한다. x좌표에도 곱하였으므로 패턴의 한 단위의 크기가 각각 2배로 증가하는 것이다.

 

이때 fract(_st.y * 0.5)의 값을 0.5 기준으로 if문으로 나누었기 때문에 패턴 한 단위에서 y좌표 중 상단 절반은 -x방향의 움직임을 보이고, 하단 절반은 +x방향의 움직임을 보인다. st.x의 경우 x,y를 바꾸어서 반대로 생각하면 된다.

 

fract(time)에 2의 배수를 곱하여야 칸의 움직임이 자연스러운 것에 대해 알아보자.

st.y와 st.x에 0.5를 곱하여 좌표의 실질적인 길이를 2배씩 증가시켰으므로 fract(time)에서 움직이는 양도 2배로 증가해야 패턴 한 단위의 길이가 맞추어져서 자연스러워진다.

 

// Author @patriciogv - 2015 - patricio.io

#ifdef GL_ES
precision mediump float;
#endif

const float PI = 3.1415926535897932384626433832795;

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

vec2 movingTiles(vec2 _st, float _zoom, float _speed){
    _st *= _zoom;
    float time = u_time*_speed;
    
    if( mod(time, 2.)>1. ){
        if (fract( _st.y) > 0.5){
            _st.x += fract(time)*1.0;
        } else {
            _st.x -= fract(time)*1.0;
        }
    } else {
        if (fract( _st.x) > 0.5){
            _st.y += fract(time)*1.0;
        } else {
            _st.y -= fract(time)*1.0;
        }
    }
    
    return fract(_st);
}

float circle(vec2 _st, float _radius){
    vec2 pos = vec2(0.5)-_st;
    return smoothstep(1.0-_radius,1.0-_radius+_radius*0.2,1.-dot(pos,pos)*3.14);
}

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

    st = movingTiles(st,5.,0.5);

    // vec3 color = vec3( st, 2.252 );
    vec3 color = vec3( 1.0-circle(st, 0.3 ) );

    gl_FragColor = vec4(color,1.0);
}

만약 그렇지 않고 mod(time, 2.0)으로 시간 간격을 2로 정한 후 움직이는 시간을 각각 1초씩 할당하도록 하면, st.y st.x의 길이를 증가시키지 않고, fract(time)도 1배로 유지시킨 형태로 만들 수 있다.

 

float circle(vec2 _st, float _radius){
    vec2 pos = vec2(0.5)-_st;
    return smoothstep(1.0-_radius,1.0-_radius+_radius*0.2,1.-dot(pos,pos)*3.14);
}

위 코드는 또 다른 방법의 circle drawing 함수이다.

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