강좌를 보기 전에...
Cygwin 설치방법
REYES
Shader 강좌 1
Shader 강좌 2
Shader 강좌 3
Shader 강좌 4
Shader 강좌 5
Shader 강좌 6
Shader 강좌 7
Shader 강좌 8
안녕하세요. 김대현입니다.

오늘은 그동안 배운 함수들을 응용해서 간단한 프로시주얼 텍스쳐를 만들어 보겠습니다.





1. 우선 텍스쳐에 간단한 선분을 하나 그려보죠.

선분은 전시간에 보셨던 step 2개를 이용해서 중간에 색상이 다른 부분을 만들었습니다. 이 구간을 작게 만들면 그게 바로 선분인 것이죠. step는 aliasing 문제가 있으므로 예전에 구현했던 smoothstep을 이용해서 만들어 보도록 하죠. ( test.rib, Makefile등의 사용법은 이젠 더이상 설명하지 않겠습니다. 이전 튜토리얼을 참고하세요. )

line.sl
------------------------------------------------------------------------------------

surface line ()
{
    color red = color "rgb" (1,0,0);
    color green = color "rgb" (0,1,0);
    float indent = 0.025;

    float distance = abs(s-0.5);

    float inLine=1-smoothstep(0.005-indent,0.005+indent,distance);

    Ci = mix(red,green,inLine);
}


------------------------------------------------------------------------------------

Pasted Graphic


2. abs
함수는 절대값을 돌려주는 함수라고 말씀드렸습니다.

아래 보시는 그래프처럼 0이되는 곳에서 급격하게 꺽이는 그래프입니다. 함수를 이용하여 현재 s 위치에 관계없이 일정한 곳을 가리킬 있습니다. 예제에서는 0.5이니 중간 위치를 가리키겠군요.


Pasted Graphic 1


3. smoothstep
함수는 세번째 값이 첫번째 값보다 작으면 0, 크면 1 리턴하며, 첫번째, 두번째 사이일 경우에는 부드럽게 보간해 줍니다.

그렇다는 것은 distance값이 0.5, 0.4, 0.3, 0.2, 0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5 바뀌므로 처음에는 0.5 smoothstep 두번째 0.005+indent보다 크므로 1 리턴하다가, 0 가까이 가서 첫번째 0.005-indent보다 작게 되면, 0 ( 이부분이 선분의 내부 )이었다가 다시 값이 두번째 0.005+indent보다 커지므로 1 리턴하게 됩니다.

만약 abs 부분에 s-0.3 했다면 0.3, 0.2, 0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7 되어 30% 영역에 선분이 생기게 되겠네요.

참고로 1 - smoothstep 이유는 첫번째 값이 1 이므로 smoothstep 결과를 반전하여 mix 올바른 색상 순서(red, green 순서) 만들기 위해서 입니다.





4.
~ 그럼 이번엔 예제를 가지고 그물을 만들어 봅시다.

lines.sl
------------------------------------------------------------------------------------

surface lines ()
{
    color red = color "rgb" (1,0,0);
    color green = color "rgb" (0,1,0);
    float indent = 0.025;

    float repeat = 10;
    float ss = mod(s*repeat, 1);
    float tt = mod(t*repeat, 1);

    float distanceS = abs(ss-0.5);
    float distanceT = abs(tt-0.5);

    float inLineS = 1 - smoothstep(0.005*repeat-indent, 0.005*repeat+indent, distanceS);
    float inLineT = 1 - smoothstep(0.005*repeat-indent, 0.005*repeat+indent, distanceT);

    Ci = mix(red, green, inLineS);
    Ci += mix(red, green, inLineT);
}


------------------------------------------------------------------------------------


Pasted Graphic 2

5. mod
함수는 첫번째 값을 두번째 값으로 나누고, 나온 값에 정수부를 제거하는 함수라고 말씀드렸습니다. 그렇다는 것은 s 범위가 0~1인데, 범위에 0~1범위값을 10 넣는다고 있습니다. 아래 그림을 보시면 아시겠지만, s안에 다른 s repeat 만큼 넣는다고 이해하시면 되겠네요.

Pasted Graphic 3

6.
~ 그럼 새로운 ss 얻었으니, 선분 위치를 지정하면 되겠네요.
float distanceS = abs(ss-0.5); 통해 새로운 ss좌표계 내에서 중간에 선분을 그린다고 지정하였습니다. 0.3으로 한다면, 30% 위치에 적용되겠네요.

7.
다음 smoothstep으로 첫번째 예제와 같이 선분의 두께를 지정해 주면 됩니다.

, 특이한 점이 repeat값을 곱해 준다는 것입니다.

그럴까요 ?

우리가 새로만든 ss좌표계를 사용한다는 것은 원본 s좌표계를 작게 압축해서 사용하는 것이 됩니다.

그렇다면, 첫번째 예제 그림을 작게 줄여서 타일로 배열해 이번 예제를 만드는 것과 마찮가지입니다.

감이 오시지요 ?

맞습니다. 작게 만들었기때문에, 선분의 두께도 같이 작어졌습니다. 그렇다면, 선분의 두께를 타일의 갯수만큼 늘려줘서 두께를 보정해 주면 되겠네요. 올커니~~!!


8.
그런다음, t 똑같이 만들어 준다음, s 만든 타일과 t 만든 타일 이미지 레이어를 더해서 최종 이미지를 만들게 됩니다. 그림의 색상을 어떻게 결합할 것인지 결정하는 레이어 합성모드라고 보시면 되겠네요.








9.
그럼, 이번엔 둥그라미를 한번 그려보죠.
circle.sl
------------------------------------------------------------------------------------

surface circle ()
{
    color red = color "rgb" (1,0,0);
    color green = color "rgb" (0,1,0);
    float indent = 0.025;

    float distance = sqrt( ( s-0.5)*(s-0.5) + (t-0.5)*(t-0.5) );

    float inCircle = 1 - smoothstep(0.3-indent, 0.3+indent, distance);

    Ci = mix(red, green, inCircle);
}


------------------------------------------------------------------------------------

Pasted Graphic 4


10.
원의 방정식이 r^2 = x^2 + y^2 입니다.

공식에서 반지름 r 구하기 위해서는 root x^2 + y^2으로 구할 있습니다.

root
함수는 Square Root(제곱근) 줄인 말인 sqrt함수입니다.

~ 그럼 s에서 0.5 빼는 것일까요 ?

s
아시다시피 0~1 변화하는 값입니다. 거기서 0.5 빼면, -0.5~0.5 범위로 변경이 됩니다. 그런데, s 제곱하니 마이너스 성분이 사라지므로 우리가 abs함수하고 비슷한 꼴이 되었네요. 이렇게 함으로서 우리는 원의 중점을 가운데로 보내는 역활을 한다고 보시면 됩니다. 만약 -0.5 하지 않았다면, 좌하단에 s, t 0 되는 곳이 원의 중심 될겁니다.



11.
~ 이젠 smoothstep으로 내부를 칠해야 합니다.

Pasted Graphic 5

여기서 distance s입장에서 보면 현재 s위치에서 반지름까지의 거리를 나타내는 것입니다. , 원의 둘레 좌표까지의 거리이지요.

현재 smoothstep 값이 0.3으로 되어 있으니, 원의 반지름이 0.3 원을 그리게 되는 것입니다.








12.
그럼, 이번에 응용해서 동그라미 패턴을 만들어 보죠.
circles.sl
------------------------------------------------------------------------------------

surface circles (float repeat = 5)
{
    color red = color "rgb" (1,0,0);
    color green = color "rgb" (0,1,0);
    float indent = 0.025;

    float ss = mod(s*repeat, 1);
    float tt = mod(t*repeat, 1);

    float distance = sqrt( ( ss-0.5)*(ss-0.5) + (tt-0.5)*(tt-0.5) );

    float inCircle = 1 - smoothstep(0.3-indent, 0.3+indent, distance);

    Ci = mix(red, green, inCircle);
}


------------------------------------------------------------------------------------

Pasted Graphic 6

13.
별다른 것은 없습니다. 단지 s repeat만큼 만들어 ss 만들고, 거기에 똑같이 원을 그려주는 것입니다.

중요한 포인트는 0~1범위를 가지는 ss repeat만큼 만든다는 것입니다.

그리고, repeat rib 파일에서 지정할 있게끔 인자로 지정하였으며, 지정하지 않을 경우 기본값 5 사용하게 하였습니다.

쉐이더를 만들 경우 이렇게 다양한 값들을 rib 파일에서 조작할 있게 하여 우리가 만든 쉐이더를 유용하게 만들 있습니다.









14.
그럼 sine 곡선을 한번 그려보죠.
sine.sl
------------------------------------------------------------------------------------

surface sine (
    float repeat = 5;
    color first = color "rgb" (1,0,0);
    color second = color "rgb" (0,1,0);
    float indent = 0.025;
    )
{
    float ss = s + sin(t*2*PI)*0.4;

    float distance = abs(ss-0.5);

    float inSine = 1 - smoothstep(0.1-indent, 0.1+indent, distance);

    Ci = mix(first, second, inSine);
}


------------------------------------------------------------------------------------


Pasted Graphic 7

15. sin
함수는 아시다시피 -1 1사이를 왔다리 갔다리하는 함수입니다. 수학 시간에 많이 보셨던 그래프라고 생각합니다.

우리는 t y축이라고 생각하고, s x축이라고 생각하고 웨지브를 만들어 보겠습니다.

s
진행됨에 따라 t값이 진동하게 되는데, 이걸 계산해 주는 부분이,

float ss = s + sin(t*2*PI)*0.4;

으로 sin내부를 보시면, t*2*PI라고 되어 있습니다. 그렇다면 t 0일때, 0이고, t 1일때, 1*2*PI 됩니다.

그렇다는 것은 PI 180도이니 2*180 = 360 입니다.

오호~! t 0~1 범위를 가질때, 완벽한 sin 한주기를 모두 보여줄 있겠네요. 좋습니다.~~!!!

~ sin에서 얻어지는 범위는 -1~1입니다. 우리가 있는 범위는 0~1이므로 0.4 곱해 범위를 40%정도 줄여주어 전체 곡선을 있게 하였습니다.

그런다음 s에서 sin값을 더합니다.

그럴까요 ?

사실 웨이브도 직선을 조금 응용한 것입니다. 직선의 경우 (s-0.5) 같이 일정한 위치를 0으로 지정하여 직선을 표현하고 있습니다. 우리가 작성한 웨이브는 일정한 0 위치를 sin 으로 계산하여, 값이 좌우로 진동함에 따라 옮겨 주는 것입니다. 이것이 바로 더하는 이유죠~~!!

그렇기 때문에, abs(ss-0.5) 중점을 지정하는듯 보이지만, ss 위치 자체가 0 위치를 변경해 주는 것이죠.

역시나 smoothstep에서 0.1 두깨를 주어 곡선을 그리고 있습니다. 직선과 다른 점이 없습니다.







16.
그럼, 마찮가지로 사인도 여러개를 만들어 보죠.

sines.sl
------------------------------------------------------------------------------------

surface sines (
     float repeat = 5;
     color first = color "rgb" (1,0,0);
     color second = color "rgb" (0,1,0);
     float indent = 0.025;
     )
{
     float ss = mod(s*repeat, 1);
     float tt = mod(t*repeat, 1);

     ss += sin(t*2*PI)*0.4;
     tt -= sin(t*2*PI)*0.4;


     float distanceS = abs(ss-0.5);
     float distanceT = abs(tt-0.5);


     float inSineS = 1 - smoothstep(0.1-indent, 0.1+indent, distanceS);
     float inSineT = 1 - smoothstep(0.1-indent, 0.1+indent, distanceT);

     Ci = mix(first, second, inSineS);
     Ci += mix(first, second, inSineT);
}


------------------------------------------------------------------------------------

Pasted Graphic 8


보시는 대로 단지 s 범위를 작게 쪼갠 좌표를 사용하고, 알고리즘 자체는 동일한 것을 있습니다.

그런데, 결과가 이상하지 않습니까 ?

모르시겠다구요 ?

원래 색상은 녹색입니다. 그런데, 노란색으로 나오고, 겹치는 부분만 우리가 지정한 녹색이 나오는 군요.

참으로 이상합니다............생각 해보죠~~ 이유가 뭘까요 ?

바로 바로~ 마지막 소스가 문제입니다. 소스대로라면, 가로로 웨이브치는 그림과 세로로 웨이브치는 그림의 색상을 더하는 방식으로 이미지를 합성하고 있습니다.

노란색이 (255, 255, 0) 이라는 점이 바로 우리가 주의해야 하는 점입니다. 바로 (255, 0, 0) (0, 255, 0) 단순히 합쳐졌기때문에 노란색이 된것입니다. 가운데 겹쳐지는 부분은 원래 색상 (0,255,0) (0,255,0) 합쳐봐야 최대 255 넘을 없으므로 그냥 (0,255,0) 되어 원래 색상이 유지되는 것입니다.

그럼 해결책은 무엇일까요 ?

바로 바로 바로~~~ 이미지 합성을 하지 않으면 되겠군요 ^^;;

~ 해답 나갑니다. 본인의 생각과 맞춰보시죠~!!

Ci = mix(first, second, inSineS);
Ci += mix(first, second, inSineT);
=>

Ci = mix(first, second, inSineS + inSineT);

~~~ 마지막 줄을 위와 같이 한줄로 바꾸어 주면 됩니다. 간단하지 않습니까 ?

결과를 보시죠~


Pasted Graphic 9


두번째 예제 그물망 만들기에도 살짜쿵 힌트를 넣고 그냥 넘어갔는데, 눈치 채신 계신지 모르겠네요 ^^

Ci = mix(red, green, inLineS + inLineT);

바꾸셔야 올바른 예제가 된답니다. 첨부한 소스에는 수정되어 있지 않으니 한번 직접 수정해 보세요.







17.
자자~ 너무 폭주하는거 아닌지 모르겠네요. 하자만~ 가는데까지 봅시다~~!!!

strip.sl
------------------------------------------------------------------------------------

surface strip (
    float repeat = 10;
    color first = color "rgb" (1,0,0);
    color second = color "rgb" (0,1,0);
    float indent = 0.025;
    )
{
    float whichTile = floor(s*repeat);
    Ci = mix(first, second, smoothstep(s-indent, s+indent, mod(whichTile, 2)));
}


------------------------------------------------------------------------------------

Pasted Graphic 10

이번 예제는 체스판을 만들기 위한 전단계로 2가지 색상을 번갈아 가면서 찍어줍니다.

새로운 함수가 나왔습니다. 바로 floor.

이놈이 하는 일은 바로 소수 부분을 날려주는 역활을 합니다. floor(3.14)하면 3 결과로 나옵니다.

그럼 whichTile 어떻게 변화하는지 보죠.

s =
0 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9
s*repeat =
0 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0

floor(s*repeat) =
0
1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9



~ 어떤 규칙성이 ~~~ 보이지 않나요 ?

그럼, 웨이트 값을 결정하는 부분을 보죠.

mod
전에 말씀 드렸듯이 두번째 값으로 첫번째 값을 나누고, 나온 값의 정수부분을 없애줍니다.

mod(whichTile, 2) =

0 5 5 0 0 5 5 0 0 5 5 0 0 5 5 0 0 5

어차피 1이상은 의미가 없으므로 1이상의 값만 나오면 mix 두번째 색상이 선택될 것입니다.

보시다시피 0 1 번갈아 가면서 찍히게 되어 있습니다.







18.
자자 이젠 체스판을 만들어 봅시다.

strips.sl
------------------------------------------------------------------------------------

surface strips (
    float repeat = 10;
    color first = color "rgb" (1,0,0);
    color second = color "rgb" (0,1,0);
    float indent = 0.025;
    )
{
    float whichTileS = floor(s*repeat);
    float whichTileT = floor(t*repeat);

    Ci = mix(first, second, smoothstep(s-indent, s+indent, mod(whichTileS+whichTileT, 2)));
}


------------------------------------------------------------------------------------


Pasted Graphic 11



멋지군요. antialiasing까지 깜찍한 체스판을 만들었습니다. s,t 합성한 부분만 주의해서 보시면 될듯 합니다.





19.
마지막으로 단조로운 색상 지정은 이젠 지겹습니다. 바꿔 봅시다.

cellnoise
함수를 이용해서 렌덤 값을 얻어낼 예정인데, noise함수들에 관해서는 기회되면 자세히 다뤄 예정입니다.

우선은 그냥 가벼운 마음으로 구경만 보시죠~!

tileNoise.sl
------------------------------------------------------------------------------------

surface tileNoise (
    float repeat = 10;
    color first = color "rgb" (1,0,0);
    color second = color "rgb" (0,1,0);
    )
{
    float whichTileS = floor(s*repeat);
    float whichTileT = floor(t*repeat);
    Ci = color cellnoise(whichTileS+whichTileT);
}


------------------------------------------------------------------------------------


Pasted Graphic 12


cellnoise라는 함수를 통해 타일 전체에 랜덤한 색상을 지정해 봤습니다.



circlesNoise.sl
------------------------------------------------------------------------------------

surface circlesNoise (float repeat = 5)
{
    color red = color "rgb" (1,0,0);
    color green = color "rgb" (0,1,0);
    float indent = 0.025;

    float ss = mod(s*repeat, 1);
    float tt = mod(t*repeat, 1);

    float radius = float cellnoise(s*repeat, t*repeat)*0.5;
    color noiseColor = color cellnoise(s*repeat, t*repeat);
    float distance = sqrt( ( ss-0.5)*(ss-0.5) + (tt-0.5)*(tt-0.5) );

    float inCircle = 1 - smoothstep(radius-indent, radius+indent, distance);

    Ci = mix(red, noiseColor, inCircle);
}


------------------------------------------------------------------------------------

Pasted Graphic 13


원의 반지름과 색상을 cellnoise함수로 랜덤하게 그려봤습니다.






종합 예제 선물 세트가 맘에 드셨는지 모르겠네요.

그럼 다음 시간에 뵙죠.



예제소스 : shader_8.zip