momodudu.zip

#2 ShaderToy - 원 그리기 본문

Graphics/ShaderToy

#2 ShaderToy - 원 그리기

ally10 2019. 9. 16. 12:50

 

저번 포스팅에서는 전반적으로 기본 shader로 fragCoord값이 어떻게 이루어지는지에 대해서 보았다.

이번 포스팅에서는 간단한 원을 그려보고자 한다.

 

일단 아래와 같은 코드를 준비했다.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    vec3 d = vec3(length(uv));

    // Output to screen
    fragColor = vec4(d,1.0);
}

 

fragColor의 RGB채널에 d값을 넣어주었다. d는 uv coord의 distance로 정의되는데, 이는 곧 원점으로부터의 거리라고 보면 된다.

즉 좌상단은 (0,0)이므로 원점에 가까우므로 d는 0에 수렴하게 된다. 그래서 black에 가깝고, 좌상단은 (1,1)이므로 distance가 white에 가까워지고 있다.

 

 

원점이 좌 상단으로 되어있으므로 원점을 직관적으로 중앙으로 옮겨보자.

이는 그냥 uv coord를 평행이동시킨다고 생각하면 된다.

 

기존에 좌상단이 (0,0)인경우 화면의 중앙점은 (0.5,0.5)가 될 것이다.

우리는 이 중앙점을 0으로 만들고 싶은것이니까 uv coord에 -0.5를 해주면 된다.

 

    vec2 uv = fragCoord/iResolution.xy;
	uv.xy -= 0.5;

 

 

그리고 이제 그라데이션 효과를 제거해볼것이다.

fragColor의 RGB채널을 distance로 줬으니, 원점과 멀어질수록 그라데이션 효과로 점점 옅어지는데,

이를 0 혹은 1의 값으로 바꿔서 명확하게 구분되는 원을 그려볼것이다.

이를 위해서 step이라는 쉐이더 내장 함수를 사용한다.

 

https://thebookofshaders.com/glossary/?search=step

 

The Book of Shaders

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

thebookofshaders.com

step 내장함수의 정의다.

즉, step(value,x) 에서 모든 x에 대해, x가 value값보다 작으면 0, 아니면 1을 리턴한다. 즉 기준이 되는 값을 두고 맞다 아니다를 리턴한다고 보면 된다.

 

이제 명확한 0,1 구분을 위해 step함수를 써보자.

현재 포스팅의 경우에는 value값에 반지름을 넣는다고 생각하면 된다.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;
	uv.xy -= 0.5;
    
    vec3 d = step(0.3,vec3(length(uv)));


    // Output to screen
    fragColor = vec4(d,1.0);
}

RGB채널에 들어갈 d값에 0.3을 기준으로 작은 length는 0, 큰 length는 1로 정의된다.

즉 원점과의 거리가 0.3 이하인 값은 모두 까맣게 된다고 보면 된다.

 

이는 원의 정의와도 매우 관련이 깊은데, 원이라는 것 정의 자체가 x,y값이 원점(특정 점이 될수도 있지만 이해를 쉽게 하고자 원점으로 정의한다)으로부터의 거리가 r, 즉 반지름인 점들을 그려놓은것이라고 보면 된다.

 

모양은 resolution때문에 다르지만, 그래프로 보면 위와 같다.

즉 빨간색 선이 원점으로부터의 거리가 0.3 이내인 점들이 모여서 원을 구성한다.

그럼 원 안에 있는 값들은 어떨까? 원 안에 있는 distance(x,y)<=0.3 이라고 볼 수 있다.

 

이런 원리로 위의 결과값에서 검은색으로 칠해진 fragment는 모두 원점으로부터의 거리가 0.3이내인 fragment라고 보면 된다.

 

 

한가지 이상한건, distance가 0.3으로 정의된 "정원" 인데, shader toy에서는 "타원"으로 출력이 된다.

이건 좌표를 정규화 해서 그렇다. 우리가 그리는 그림판은 가로 세로가 일치하는 판이 아닌데, 이 판을 [-0.5,0.5]사이로 정규화했으니 가로가 세로보다 더 길게 나오는것은 당연하다.

 

그래서 이 정규화한 좌표를 조금 손을 봐줘야 한다.

    vec2 uv = fragCoord/iResolution.xy;
	uv.xy -= 0.5;
    uv.x *= iResolution.x / iResolution.y;

중심을 평행이동 한 좌표에 스크린 비율을 x에 곱해주었다. 이러면 x,y의 비율이 맞게 되므로 정 원이 완성된다.

 

여기서 주의할점은, 이것도 matrix와 다를바가 없다는 점이다.

즉, -0.5를 해준거는 평행이동, Translation에 해당하고 비율을 곱해준건 scale의 연산과도 같다.

 

model matrix를 공부할적에, 연산 순서가 TRS, 즉 Translation -> rotate -> scale의 순서를 지켜야 한다고 들어봤을 것이다.

여기서도 마찬가지다. 만약 scale을 먼저 하고 translation을 하게 된다면 결과가 조금 다르게 나온다.

 

 

추가적으로 step이 아닌 smoothstep함수도 써보자.

   vec3 d = smoothstep(0.3,0.4,vec3(length(uv)));

 

smoothstep의 경우는 step의 조금 다른 버전이라고 생각하면 된다. 0.3 미만의 값은 1, 0.4보다 큰 값은 0으로 처리하고

[0.3,0.4]의 값은 Hermit curve를 이용해서 부드러운 커브 값으로 리턴해준다.

 

step과 smoothstep의 그래프를 보면 이해가 쉽다.

 

step
smoothstep

해당 범위 값에 있는 경우에는 smooth하게 1로 간다고 보면 된다.ㅎㅎ

 

간단한 원을 그리는 예제도 완료! 다음 포스팅에서는 이 원을 가지고 이것저것 해보려고 한다.

'Graphics > ShaderToy' 카테고리의 다른 글

#3 ShaderToy - 원을 이용한 간단한 예제(Smile)  (0) 2019.09.16
#1 ShaderToy - 기본 예제  (0) 2019.09.16