lambert

반응형

렘버트 또는 람베르트의 기본 공식은 dot(L,N).
http://mgun.tistory.com/1306
위 링크에서 엄청나게 마니 언급되었던 기본 조명모델이다.
이걸 렌더몽키에서 표현하는것도 무척 쉽다.

공식대로 계산을 하기 위해 Light Vector가 우선 필요하다.





사진으로 보니 더욱 명확하다.
램버트에 대한 내용이나 코사인 각도가 무었을 의미하는지 등은 언급한 링크에서 읽어보면 될 것 같고..
내가 말하고자 하는 것은 벡터의 방향이다.

이 부분에 대한 링크 : http://kblog.popekim.com/2011/01/blog-post_4153.html

첫번째 사진이나 두번째 사진이나 결국은 같은 거라고 생각한다.
내적의 특성상 두번째 사진의 개념은 부호를 한번 뒤집어서 벡터의 방향을 바꿔야 하긴 하지만
개인적인 생각으로는 두번째 사진이 "내가 보기에는" 이해하기가 쉽다.
옆 노멀벡터는 점이 속한 평면에서 수직인 벡터인데 결국 점을 기준으로 방향을 잡기 때문에
광원벡터 역시 광원에서 빛을 쏘는 방향이라고 일관성 있게 이해하고 싶다.


벡터에 대한 정리는 이 링크를 참고하세요. http://mgun.tistory.com/1143

무튼 라이트 위치를 추가해 주자


대략 오른쪽 위에, 모니터의 반대방향쪽에 위치시켰다.


오옷~! 빛의 위치는 바로 저기~!.. 2d 이미지라서 z위치는 식별하기 어렵지만..대략 저기임....

자~ 이제 노말은 어쩔거임? 노말은 이미 시멘틱이 존재한다.
그래서 걍 코딩으로 추가해 주면 된다.
struct VS_INPUT
{
   float4 mPosition : POSITION0;
   float3 mNormal : NORMAL;
};

이제 빛의 위치로 부터 빛의 방향벡터도 가져와야 겠지.
float3 lightDir = Output.mPosition.xyz - gWorldLightPosition;
lightDir = normalize(lightDir);
대부분 알고있겠지만 Position - LightPosition과 LightPosition - Position의 차이는 명백하다.
사실 무었을 하든 상관없다. 이 감산연산의 결과만 제대로 이해하고 있다면.
첫번째 식인 정점의 위치 P에서 광원의 위치 LP를 빼면 LP 에서 P로 향하는 벡터가 만들어 진다.
반대로 LP - P라면 정점 P에서 광원 LP로 향하는 벡터가 만들어 진다.
벡터는 크기와 방향을 가지기 때문에 이 방향을 제대로 아는 것이 상당히 중요하다.

float3 lightDir = Output.mPosition.xyz - gWorldLightPosition;
lightDir = normalize(lightDir);
이 식대로 계산하면 LP에서 P로 향하는 Light Vector을 만들 수 있다.
L(빛의방향 벡터)와 N을 내적해야 하는데 그러기 위해서는 두 벡터의 밑동이 서로 만나야 한다.
하지만 현재 L은 방향이 반대다. 즉 밑동이 광원의 위치이고 머리가 정점의 위치다.

Output.mDiffuse = dot(-lightDir, worldNormal);
그래서 이렇게 내적시 -을 곱해주어 방향을 바꾼다.

그리고 가끔 빼먹는 저 normalize !!.
욘석은 은근히 많은 사람들이 "그냥 올바른 값을 만들기 위한 초기화"정도로 이해하고 있는사람이 많지만
사실 내적의 공식을 들여다보면 반드시 필요한 "정리"다.
내적공식인 dot(A,B) = Cos각도 * A의 길이 * B의 길이로 정리할 수 있는데 두 벡터인 A와 B의 길이를 1로 만들어 주면
dot(A,B) = Cos 각도가 되서 단지 A벡터와 B의 벡터의 내적만으로 간단히 cos의 각도를 구할수 있다는 것이 핵심!
저 normalize가 바로 벡터의 길이를 1로 만들어 주는 정규화를 해 주는 녀석이다.
벡터의 길이가 1인 벡터를 단위벡터라고 말하는데 단위벡터를 직접 구하는건 그다지 어렵지 않다.
단위벡터 = 벡터 / 벡터의 길이.
즉 (1,2,3)의 벡터가 있다면 이 벡터의 길이는 sqrt(1*1 + 2*2 + 3*3) 가 되고
단위벡터는 (1,2,3) / sqrt(14) 가 된다.
사실 여기까진 몰라도 된다. 다만 내가 내가 무의식적으로 적어주는, 또는 빼 먹는 저 normalize가
결코 가볍지 않은 일을 한다는 것을 말하고 싶었다.

추가적으로...
 

half lambert
Output.mDiffuse = pow(dot(-lightDir, worldNormal) * 0.5 + 0.5, 4);


wrapped diffuse

float magicNmuber = 0.5; 
Output.mDiffuse = (dot(-lightDir, worldNormal) + magicNmuber) / (magicNmuber+1);

energy conserving wrapped diffuse
float magicNmuber = 0.5;  
float NdotN = dot(-lightDir,worldNormal);
Diffuse = saturate( (NdotN+magicNmuber) / ((magicNmuber+1)*(magicNmuber+1))); 


렌더몽키 파일 : 


 

'RenderMonkey' 카테고리의 다른 글

Shadow Mapping  (0) 2014.01.11
fresnel  (0) 2013.10.22
normal mapping (법선 매핑)  (2) 2013.02.07
기본적인 반사 벡터  (0) 2013.02.02
흐음...렌더몽키라..  (0) 2013.01.29
TAGS.

Comments