개발/Shader / / 2023. 3. 6. 22:37

GLSL - cross shape rotate, scale 십자가 회전, 크기 조정

반응형

https://thebookofshaders.com/08/

 

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/24584

 

16. Rotate & Scale - GLSL / Shader

16. Rotate & Scale 2019-04-01 02:58:34 회전변환

www.opentutorials.org

Rotate

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359

uniform vec2 u_resolution;
uniform float u_time;

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

float box(in vec2 _st, in vec2 _size){
    _size = vec2(0.5) - _size*0.5;
    vec2 uv = smoothstep(_size,
                        _size+vec2(0.001),
                        _st);
    uv *= smoothstep(_size,
                    _size+vec2(0.001),
                    vec2(1.0)-_st);
    return uv.x*uv.y;
}

float cross(in vec2 _st, float _size){
    return  box(_st, vec2(_size,_size/4.)) +
            box(_st, vec2(_size/4.,_size));
}

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    // move space from the center to the vec2(0.0)
    st -= vec2(0.5);
    // rotate the space
    st = rotate2d( sin(u_time)*PI ) * st;
    // move it back to the original place
    st += vec2(0.5);

    // Show the coordinates of the space on the background
    // color = vec3(st.x,st.y,0.0);

    // Add the shape on the foreground
    color += vec3(cross(st,0.4));

    gl_FragColor = vec4(color,1.0);
}

십자가 모양이 회전하는 쉐이더 코드를 작성해보자.

 

 

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359

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

float rect(vec2 loc, vec2 size, vec2 coord){
    vec2 sw = loc - size/2.;
    vec2 ne = loc + size/2.;
    
    float pad = 0.001;
    vec2 ret = smoothstep(sw - pad, sw, coord);
    ret -= smoothstep(ne, ne + pad, coord);
    
    return ret.x * ret.y;
}

float cross(vec2 loc, vec2 size, vec2 coord){
    float r1 = rect(loc, size, coord);
    float r2 = rect(loc, size.yx, coord);
    
    return max(r1, r2);
}

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    
    vec3 col = vec3(cross(vec2(.5), vec2(0.550, 0.070), coord));
    gl_FragColor = vec4(col, 1.);
}

먼저 십자가를 그리기 위해 사각형 함수 rect()를 cross안에서 max()로 합쳐서 표현한다. 

이전과 다른 점은 rect() 안에서 smoothstep에 pad를 이용해서 흐릿하게 보이는 구간을 설정하였다. 

sw(south west) 에서는 왼쪽 아래 부분이므로 pad를 빼고, ne(north east)는 오른쪽 상단 부분으로 pad만큼을 더함으로써 smooth한 padding 간격을 만들어낸다.

여기서 회전하는 법은 어떻게 넣을까?

 

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359

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

float rect(vec2 loc, vec2 size, vec2 coord){
    vec2 sw = loc - size/2.;
    vec2 ne = loc + size/2.;
    
    float pad = 0.001;
    vec2 ret = smoothstep(sw - pad, sw, coord);
    ret -= smoothstep(ne, ne + pad, coord);
    
    return ret.x * ret.y;
}

float cross(vec2 loc, vec2 size, vec2 coord){
    float r1 = rect(loc, size, coord);
    float r2 = rect(loc, size.yx, coord);
    
    return max(r1, r2);
}

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

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord.x *= u_resolution.x/u_resolution.y;
    
    coord = coord * rotate2d(0.000);
    
    vec3 col = vec3(cross(vec2(.5), vec2(0.550, 0.070), coord));
    gl_FragColor = vec4(col, 1.);
}

중간에 rotate2d() 라는 함수를 추가하고, 이를 coord좌표에 곱해주었다. rotate2d는 input 각도만큼 회전행렬 연산을 처리해주는 것이다.

 

그랬더니 십자가가 이렇게 움직인다. 헉...

사실 coord에 rotate 회전행렬 연산을 곱했다는 것은 원점을 중심으로 회전시킨 것이다. 현재 원점은 (0,0) 위치 왼쪽 하단에 위치하기 때문에 이렇게 회전하는 것이다.

 따라서 원점을 십자가의 중심으로 옮겨주고, 원점이 화면에 중앙에 나오도록 변경하자.

 

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359

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

float rect(vec2 loc, vec2 size, vec2 coord){
    vec2 sw = loc - size/2.;
    vec2 ne = loc + size/2.;
    
    float pad = 0.001;
    vec2 ret = smoothstep(sw - pad, sw, coord);
    ret -= smoothstep(ne, ne + pad, coord);
    
    return ret.x * ret.y;
}

float cross(vec2 loc, vec2 size, vec2 coord){
    float r1 = rect(loc, size, coord);
    float r2 = rect(loc, size.yx, coord);
    
    return max(r1, r2);
}

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

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord = coord*2. -1.;
    coord.x *= u_resolution.x/u_resolution.y;
    
    coord = coord * rotate2d(0.);
    
    vec3 col = vec3(cross(vec2(0.), vec2(0.550, 0.070), coord));
    gl_FragColor = vec4(col, 1.);
}

 

십자가를 원점으로 이동시켜 회전할 수 있도록 수정하였다.

 

coord = coord*2. -1.;

좌표를 두 배 늘렸다가 절반만큼 뺌으로써 원점을 화면의 중앙으로 옮기고

 

    vec3 col = vec3(cross(vec2(0.), vec2(0.550, 0.070), coord));

이렇게 원점의 위치에 십자가를 생성시킨다.

 

coord = coord * rotate2d(sin(u_time));

 시간에 따라 왔다 갔다 회전시키고 싶은 경우 rotate2d 값에 시간에 따른 sin()값을 넣으면 된다.

 

Scale

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord = coord*2. - 1.;
    coord.x *= u_resolution.x/u_resolution.y;
    
    coord = coord * 1.; // <--
    coord = coord * rotate2d(sin(u_time));
    
    vec3 col = vec3(cross(vec2(0.), vec2(0.550, 0.070), coord));
    gl_FragColor = vec4(col, 1.);
}

coord에 특정 배수만큼 값을 곱하면 값이 커질수록 십자가가 작아져보인다. 이는 곱하는 배수가 커질수록 좌표가 화면에 보여줄 수 있는 범위가 넓어지므로 십자가가 작아 보이는 것이다. 

 

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359

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

float rect(vec2 loc, vec2 size, vec2 coord){
    vec2 sw = loc - size/2.;
    vec2 ne = loc + size/2.;
    
    float pad = 0.001;
    vec2 ret = smoothstep(sw - pad, sw, coord);
    ret -= smoothstep(ne, ne + pad, coord);
    
    return ret.x * ret.y;
}

float cross(vec2 loc, vec2 size, vec2 coord){
    float r1 = rect(loc, size, coord);
    float r2 = rect(loc, size.yx, coord);
    
    return max(r1, r2);
}

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

void main(){
    vec2 coord = gl_FragCoord.xy/u_resolution;
    coord = coord*2. - 1.;
    coord.x *= u_resolution.x/u_resolution.y;
    
    coord = coord * sin(u_time);
    coord = coord * rotate2d(sin(u_time));
    
    vec3 col = vec3(cross(vec2(0.), vec2(0.550, 0.070), coord));
    gl_FragColor = vec4(col, 1.);
}

이를 이용해서 coord 좌표에 sin으로 시간에 따라 커졌다 작아졌다 scale을 조정할 수 있다.

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