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

오늘은 렌더맨에 metal.sl을 가지고 specular에 관해 알아보겠습니다.

우선 소스를 보죠.

metal.sl
----------------------------------------------------------------------------------

surface metal (float Ka=1, Ks=1, roughness=.1)
{
    normal Nf;
    vector V;

    Nf = faceforward(normalize(N), I) ;
    V = normalize(-I) ;

    Oi = Os;
    Ci = Os * Cs * ( Ka*ambient() + Ks*specular(Nf,V,roughness) );
}


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

1. 오늘의 주제인 specular는 금속같이 맨들 맨들한 표면에서 강한 하이라이트를 만들어 주는 역활을 합니다.

이 효과를 만드는 알고리즘을 알아보기 위해서는 수학 2가지를 익혀야 합니다.

전 시간에 보았던 내적의 새로운 성질과 벡터의 연산입니다.

자~ 차례대로 알아보죠.

Pasted Graphic

2. 내적은 전시간에 말씀드린대로 두 벡터의 각을 알고 싶을 때 유용하게 사용됩니다.

거기에 또 특이한 성질이 있는데, 바로 한 벡터에서 다른 벡터로 투영하여 얻어진 벡터의 길이라는 것입니다.

그림에서 보시면 A 벡터와 B 벡터가 있을때, A와 B의 내적을 구하면 그것은 A에서 B로 투영하여 a길이를 얻기 됩니다. 벡터가 구해지는게 아니라 길이가 구해진다는 것에 주목하시기 바랍니다.





3. 그럼, 이번에 벡터의 연산에 관해 알아보죠. 벡터도 일반 스칼라 값처럼 덧셈, 뺄셈을 할 수 있는데, 이것이 바로 기하학적으로 의미가 있습니다.



4. 우선 벡터의 합을 알아보죠.

벡터의 합은 각 벡터의 끝부분을 쭉 이른 다음, 처음점과 마지막 점을 이은 벡터입니다.

E = A + B + C + D
Pasted Graphic 1 Pasted Graphic 2


그림에서 보면 E가 모든 벡터들을 합한 벡터가 되겠네요.



5. 그럼 벡터의 차는 어떨까요 ?

벡터의 차는 벡터의 합을 이용합니다. 보시죠~

C = A - B == C = A + (-B)

Pasted Graphic 3 Pasted Graphic 4
Pasted Graphic 5

보시는 바와 같이 A벡터와 B벡터의 차를 구한다는 것은 A벡터에 -B벡터를 더한 같습니다.



6.
마지막으로 스칼라값과 벡터의 곱은 벡터의 길이만 길어지는 역활을 합니다.

수식으로 보자면 a * V(x,y,z) = (a*x, a*y, a*z) 방향을 유지한 상태로 길이만 길어지게 됩니다.



7.
대충 밑그림은 그렸네요 ^^ 본격적으로 들어가 보죠~!



8. specular
무엇일까요 ?

그것은 금속같이 맨질맨질한 표면에 빛이 반사되어 강하게 하이라이트를 이루는 것을 말합니다.

그렇다는 것은 빛의 위치와 표면의 반사되는 위치가 아주 중요하겠군요.

~ 그래서 우리가 지금까지 힘들게 알아본 2가지 수학을 가지고 반사 벡터를 만들어 보겠습니다.

Pasted Graphic 6


그림을 보시면, 빛으로 부터 표면으로 가는 L 벡터, 표면에서 나오는 N 벡터, 눈에서 표면으로 가는 I 벡터, L 벡터에서 N 벡터를 기준으로 반사된 R 벡터, 이렇게 있습니다.






9. 그림을 좀 더 쉽게 고쳐 보죠.
Pasted Graphic 7

L벡터에서 나온 빛을 N벡터를 기준으로 반사하는 R벡터를 만들기 위해서는, 내적을 이용해야 합니다.

우선 L과 N의 내적을 구합니다. 그러면, 위에서 배운 것처럼 L에서 N에 투영된 길이A를 구할 수 있습니다.

그럼 N벡터의 길이를 2배한 다음, 그 길이를 N 벡터에 곱해 길이를 늘인 N 벡터를 만든 다음, L 벡터를 더하면 R벡터가 나오겠군요. ( B 벡터는 N과 A벡터를 더할때, 첫번째 벡터 마지막 위치에 두번째 벡터 처음을 붙여서 첫번째 벡터의 처음 위치와 두번째 벡터 마지막을 이어 만드는 것을 보여주기 위함입니다. 벡터의 합 그림을 참조하세요. )

아주 간단합니다. 조금만 생각해 보시면 금방 알 수 있습니다.

여기서 좀 생각해 봐야 하는 문제가 있습니다. 바로 normalize라는 함수의 역활이죠.

이 함수는 벡터의 길이를 1로 만들어 주는 함수입니다. 벡터는 길이를 왜 1로 만들어 주는 것일까요 ?

바로 바로~ 이 specular 계산에서 극명하게 드러나게 됩니다. 짜자잔~!!



10. 9번에서 L와 N을 내적해서 길이를 구한다음 그 길이를 2배한 것을 N에 곱해서 N 벡터를 만든다고 했습니다.

1이라는 숫자가 곱셈에서 어떤 의미인지 가슴에 와닿지 않습니까 ? 맞습니다. 어떤 수도 1에 곱해봐야 원래 수지요 ?

벡터에서도 마찮가지입니다. 만약 N벡터가 normalize되지 않고 반사벡터를 구하는 공식에 사용되었다고 생각해 보세요.

바로 답이 나오지 않습니까 ?

Pasted Graphic 8

만약 N벡터의 길이가 1 아니라 1.2였다면, L N 내적해 구한 길이에 2배한 수를 4라고 했을때, 우리가 원하는 것은 길이 4짜리 벡터임에도 불고하고, 4*1.2 = 4.8이라는 오묘한 숫자가 나오게 됩니다.

그러면 우리가 구하려는 벡터보다 0.8이나 벡터를 구하게 되고, 이걸로 반사벡터를 구하면 틀린 결과가 나오게 됩니다.

그렇기 때문에 실수를 줄이기 위해서는 가능하면 모든 벡터를 normalize시키는 것을 잊어서는 안됩니다.

벡터가 normalize되어 있어야 우리가 원하는 길이의 벡터를 맘껏 만들 있겠네요.



11.
~ 그럼 결론을 내보죠~!

specular
근본은 I벡터와 R벡터를 내적하여 나온 값을 가지고 하이라이트를 만들어 주는 것입니다.

I
R 겹쳐질수록 강한 하이라이트가 생기는 것이죠. ( I R 내적이 0보다 커서 90도보다 작은 경우 )

지금까지 알아본 알고리즘이 Phong specular 반사식입니다.

놈의 소스를 참고로 보죠.
----------------------------------------------------------------------------------

color phong( normal N; vector V; float size )
{
        color C = 0;
        vector R = reflect( -normalize(V), normalize(N) ); // -V임에 주의
        illuminance( P, N, PI/2 ) {
                vector Ln = normalize(L);
               C += Cl * pow(max(0.0,R.Ln), size);
        }
        return C;
}


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

12.
그런데, 우리가 공부한 Phong specular 알고리즘과는 ~~ 다른 처럼 보이네요. 입력 값으로 라이트 벡터L 노말 벡터 N 받지 않고, 엉뚱하게 시선 벡터 V 노말 벡터 N 받고 있습니다.

이거 지금까지 힘들게 공부한 우리의 공식이 틀린 걸까요 ? ~ 마이 ~~!!!



13.
걱정하지 마세요. 우리는 틀리지 않았습니다.

Pasted Graphic 9

우선 이 함수의 내용을 자세히 하나 하나 봅시다. 입력 값은 다음과 같습니다.

normal Nf = normalize(N);
vector V = normalize(-I);



첫번째 라인 color C = 0;은 최종 색상을 우선 검은색으로 초기화하고 있습니다.

두번째 라인 vector R = reflect( -normalize(V), normalize(N) );은 V와 N으로 우리가 원하는 반사 벡터 R을 구하고 있습니다.

Pasted Graphic 10

처음 입력 V 표면에서 눈쪽으로 벡터 방향을 바꾸었기때문에 다시 - 붙여서 원래 벡터 방향으로 돌렸습니다.( 처음에 I벡터 방향을 바꾼 이유는 조금 있다가 설명드리겠습니다.)

그런 다음, N 기준으로 V벡터의 반사 벡터 Rv 구했습니다.

다음에는
----------------------------------------------------------------------------------

illuminance( P, N, PI/2 )  {
         vector Ln = normalize(L);
         C += Cl * pow(max(0.0,R.Ln), size);
}


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

L
벡터를 normalize 다음, Rv벡터와 L 벡터를 내적한 값을 size 제곱하여 우리가 원하는 하이라이트를 만들고 있습니다.



14.
이런 방법을 사용했을까요 ?

우리가 사용하는 라이트가 여러 개일 경우, 아무 곳에서나 벡터 L 얻어 온다면, 얻어 오는 기준이 애매모호해지게 됩니다. 그렇게 때문에 라이트 벡터 L illuminance라는 특수한 함수 내에서만 사용할 있습니다.

illuminance함수는 아주 특수한 함수로 함수가 호출되면, Scene내에 모든 라이트를 루프를 돌면서 함수 내부의 내용을 실행해 줍니다. 모든 라이트를 돌면서 라이트 L 지금 처리하고 있는 라이트 벡터 L 넣어주고, C = C + Cl * pow(max(0.0,R.Ln), size); 해줌으로서 C 값에 색상을 누적시키고 있는 것이죠.

만약 우리가 사용한 공식을 사용했다면, illuminance함수 내에서 매번 라이트 벡터 L 반사 벡터 RL 구해줘야만 할겁니다. 하지만 라이트 계산하는 동안 변경되지 않는 시선 벡터 V 경우 illuminance함수 바깥에서 한번만 계산된 다음, 라이트 루프내에서 계속 재활용할 있는 것이죠.

픽사 아저씨들 머리 정말 좋지 않습니까 ? 단번에 라이트 갯수만큼의 반사 벡터 계산을 번으로 줄였습니다.


이젠 무언가 알듯하니, 왠지 머리가 개운해 지네요.



15.
그럼 C = C + Cl * pow(max(0.0,R.Ln), size); 내용을 보죠.

max(0.0,R.Ln) R Ln 내적을 구한 다음, 0 이상의 값이 나오면 값을 사용하고, 마이너스 값이 나오면 0으로 만들어 줍니다. 뜻을 풀어 보자면, 각이 90이상으면 마이너스로, 반사되는 면을 없으므로 0으로 만들어 주는 것입니다.

그런다음, pow함수를 사용하여 나온 내적값을 size 제곱해 주는 것입니다.

size값이 클수록 넓은 면이 반사되고, 작을 수록 반사면이 작게 모아지는 것이죠.



16.
그럼 도대체 이건 어떤 기준으로 만든 알고리즘일까요 ?

Phong specular 반사 모델은 물리적 반사 모델과 ~ 관계없는 모델입니다.
단지 수식으로 반사와 비슷한 모양을 만들어낸 것에 지나지 않는 것이죠. 그렇기 때문에 내적을 하느냐? 내적한 함수를 제곱하느냐? 물어도 대답은 그렇게 하는게 비슷하니까.....입니다.



17.
~ 거의 왔습니다.

이렇게 구한 값을 웨이트 값으로 사용하여 Cl이라는 라이트 칼라값에 곱하고 칼라값을 C라는 색상에 누적해 주는 것이 끝입니다.



18.
그럼 specular(Nf,V,roughness) 뭐에요 ?

엉뚱한 phong함수만 보고 있죠 ?



19.
사실 specular함수는 지금까지 phong함수에 비하면 너무나도 쉽습니다.

phong
함수 자체가 어렵고, 반사 벡터라는 것이 여러 모로 쓸모가 있기 때문에 자세히 다뤄 것입니다.



20.
, 그럼 처음으로 돌아가서 specular함수 내용을 보죠.
----------------------------------------------------------------------------------

color specular( normal N; vector V; float roughness )
{
        color C = 0;
        illuminance( P, N, PI/2 )
                C += Cl * specularbrdf(normalize(L), N, V, roughness);
        return C;
}

color specularbrdf(vector L, N, V; float roughness)
{
    vector H = normalize(L+V);
    return pow(max(0, N.H), 1/roughness);
}


----------------------------------------------------------------------------------
보시면 특별한 것은 illuminance 함수 내부에서 specularbrdf라는 함수를 호출하는 것입니다.

Pasted Graphic 11

놈은 L V 벡터의 중간 벡터 H 구해서, 벡터를 N 내적해서 원하는 결과를 만듭니다.

자자~~ 슬슬 고백할 것이 있습니다.

반사 벡터 알고리즘을 설명할때, 라이트 벡터L 방향을 빛에서 표면으로의 방향이라고 설명했습니다.

그것은 렌더맨에서는 틀린 설명입니다. 렌더맨에선 라이트 벡터 L 표면에서 빛이 있는 방향으로의 벡터입니다.

지금 보고 계시는 그림이 올바른 그림이죠.

아무래도 L 입사해 들어 가는 방향으로 설명드려야 이해가 쉬울듯 하여 조금 바꾼 것입니다. ^^

올바른 그림으로 돌아 왔으니, 한가지 사실을 짚고 넘어가죠.

아까 Phong specular 반사를 구할때, L N 내적하지 않는 이유를 매번 L 반사 벡터를 구하지 않기 위해서라고 말씀드렸습니다. 거기에 추가하여 매번 라이트 벡터의 방향도 바꾸어 주지 않아도 되는 좋은 점이 추가되네요.



21.
~ 다시 우리의 specularbrdf함수로 돌아와서,

드디어 나왔습니다~~~~~~~~~~!!!!!

13
번에서 시선 벡터 I 방향을 바꾸어서 시선 벡터 V 만들었는지 명확해 집니다. 그것은 벡터 L 벡터 V 더해 우리가 얻고자 하는 하프벡터 H 구하기 위해서이지요. 사실상 phong specular 반사에선 필요 없는 부분일 있는데, 우리에 픽사 아저씨들은 우리가 헛갈릴까봐 그냥 I 반전 벡터를 사용하게끔 함수를 만들어 주셨죠.

고마운 일입니다. ^^ 감사합니다. 픽사 아저씨들~~!!



22.
다시 본론으로 돌아와서, 벡터의 합으로 구한 H N 내적한 값을 가지고 하이라이트를 만드는 것이 전부랍니다. 나머지 과정은 전에 설명드린 phong specular 동일하죠.

그럼 알고리즘은 어떻게 나왔을까요 ?

이것도 마찮가지로 비슷한 효과를 내주기 위해 임의로 만든 수식이라고 있겠습니다. 처음에 phong specular 물리학적인 면이 있지만, 사실상 함수가 만들어 내는 결과는 구별할 없는 정도입니다.

그렇다면, 내적에 기타 등등을 하는 phong specular 쓰는 보다는 단지 벡터의 합만을 사용하는 specular함수를 사용하는 이유가 충분하겠네요. 방법을 알아내신 분이 유명하신 Jim Blinn 아저씨랍니다. Blinn하면 생각나는 아저씨죠. 녀석이 실시간 그래픽에서도 사용되는 방법이죠.



23.
~~~~ 결론을 내보죠.

Ks
적당히 specular 웨이트를 조정해서 원하는 하이라이트를 얻는 것이 오늘 공부한 metal.sl 핵심이었습니다.



24.
오늘 나온 내용이 라이트 관련 계산에선 첫번째 어려운 관문이라고 있습니다. 여러분 모두 첫번째 관문을 무사히 통과하셨으면 좋겠습니다.

이로서 ambient, diffuse, specular 모두 알아봤습니다. 나중에 우리가 배운 알고리즘을 바탕으로 우리만의 라이팅 모델을 만들어 것입니다.



그럼 다음 시간에 뵙죠~~!