ETC/CS

Lambert's cosine Law

지과쌤 2020. 6. 8.
반응형

빛은 물체표면에 입사한 후, 물질 내에서 여러번 복잡하게

산란 후 표면을 빠져 나온다

 

여기서 [복잡하게]라는 것이 다행스럽게도 물체 표면에서 

나오는 가장 눈에 띄는 성분은 모든방향에

대하여 같은 강도로 산란되는 빛이다.

 

즉, 물체로부터 보이는 색은 물체를 비추는 빛의 강도에 비례해서

밝게보인다라는 것, 이것이 램버트의 코사인 법칙이며

 

물체 표면에서 반사하는 빛의 휘도(Luminance)는

빛의 입사벡터 L과 표면의 법선벡터 N이 이루는 각도의

코사인에 비례한다는 정리이다.

 

출처 : http://journal.mycom.co.jp/photo/column/graphics/057/images/001l.jpg

 

광원(光源)

L : 광원벡터

N : 법선벡터

I : 반사광의 세기

Ii : 입사광의 세기

kd : 확산반사율

 

일때 램버트 코사인의 법칙을 수식으로 나타내면 다음과 같다

 

 

단, cosθ=가 음수인 경우(법선과 광원이 반대로 향하고 있는경우)에는 값을 0으로 한다.

뒷면에 빛을 비추지 않게 하기 위해서이다.

 

Ii와 kd는 색성분으로 벡터이며,  곱셈은 색 성분간의 곱셈을 의미한다.

 

램버트의 코사인 법칙에 따르는 빛을 확산 반사광이라 하며

확산 반사광은 음영 표현 기법을 하는데 적합하며,

특히 종이처럼 표면이 거친 물체를 표현하는데 매우 유용하다

 

단, 확산반사광에 의한 라이팅은 법선 방향이 광원방향과 반대라면

완전히 까맣게 되므로 검은 배경안에 물체를 두면

빛의 반대면은 전혀 보이지 않는다는 단점이 있다

 

이러한 단점을 처리하기 위한 광원이 환경광인데,

환경광은 일정한 밝기로 물체를 전부 칠하는 빛으로써,

물체면의 방향이나 빛의 방향이 전혀 영향을 주지 않는다

따라서 환경광을 추가하면 물체의 윤곽을 알 수 있어 빛을 비추기

어려운 부분도 상세히 보여줄 수 있다.

 

                     

      

       

 

 

 

수식으로 쓰면

 

빛의 강도를 Ia, 물체의 반사계수(물체 색)를 Ka라 하면,

물체 표면의 색 I 는 다음과 같다

 

 

여기서 계수 Ia나 Ka는 빛의 출처가 다르므로, 

확산반사광의 Ii나 kd와는 다른 값을 사용한다.

 

그러나 일반적으로 Ia는 Ii를 약하게 한색, Ka 는 Kd와 동일한 값으로 

사용되는 경우가 많다

 

환경광은 보는 바와 같이 계산이 매우 간단해서 환경광만 있는 라이팅도

자주 사용한다(물체의 밝기를 조정하는데 유용하게 사용)

 

이 두가지를 조합한 것이 램버트 조명 모델이라 하며

 

 

최종 수식은 다음과 같다

 

 

 

이걸 HLSL에서 사용해보자

 

픽셸 셰이더에서도 가능하지만(외형이 더욱 정확한 렌더링 결과를 얻을수있음)

 

버텍스 셰이더가 계산을 빨리 할 수 있으므로

 

버텍스셰이더에서 간단하게 작성해보자

 

버텍스의 색 성분이 필요하므로

 

 

float4X4 mWVP;
float4X4 mWIT;

float3 vLightDir;                     //광원 방향

//광원 밝기
float4 i_a = { 0.3f , 0.3f, 0.3f, 0.3f};          //ambient
float4 i_d = { 0.7f , 0.7f, 0.7f, 0.7f};          //diffuse

//반사율
float4 k_a = { 1.f , 1.f, 1.f, 1.f };          //ambient
float4 k_d = { 1.f , 1.f, 1.f, 1.f };          //diffuse

strunct VS_INPUT
{
     float4 Pos : POSITION;      //로컬좌표
     float3 Normal : NORMAL;   //법선벡터
};
struct VS_OUTPUT
{
     float4 Pos : POSITION;
     float Color : COLOR0;
};

VS_OUTPUT Main(in VS_INPUT input)
{
     VS_OUTPUT Out = (VS_OUTPUT)0;     //초기화

     Out.Pos = mul(Pos,mWVP);               //좌표변환

     float3 L = -vLightDir;
     float3 N = normalize(mul(input.Normal,(float3X3)mWIT);  //월드좌표계에서의 법선

     Out.Color = i_a X k_a                   //환경광
                     i_i X k_d X max(0,dot(N,L));  //확산광
     return Out;
}

 

 

vLightDir을 빛이 입사해 들어올 방향이고

우리가 필요한건 광원벡터 L이므로 값의 부호를 바꾸어주는것을

잊지말자

 

또한 광원벡터 L은 지면을 기준으로 월드 좌표계로 정의 되어있기

때문에, 법선벡터 N도 월드좌표계로 바꾸어 주어야 한다

 

여기서 사용된 mWIT는 로컬좌표계를 월드좌표계로 변환하는 행렬의

역전치행렬이다.

 

법선벡터의 월드좌표계변환

 

 N =  * (월드행렬의 역행렬의 전치행렬)*N

 

또한 확대나 축소가 이루어질 경우

 

벡터의 크기가 변환되므로 정규화 해주는것도 알아두도록

 

이것은 셰이더에서 보여주기위한 방식으로

 

보통은 직접 대입

 

float3 N = Normal;

 

으로 설정한 후

 

애플리케이션에서 월드행렬의 역행렬을 계산해서 광원위치를 변환한 후

설정 한다 ( 법선벡터의 좌표변환이나 정규화가필요없으므로 더 빠름)

 

main.cpp

 

 

D3DXHANDLE m_hvLightDir;

m_pEffect->GetParameterByName(NULL,"vLightDir");

light_pos = D3DXVECTOR4(-0.577f, -0.577f, -0.577f,0); //광원방향
D3DXMatrixInverse(&m, NULL, &mWorld);
D3DXVec4Transform(&vm&light_pos,&m);
D3DXVec3Normalize((D3DXVECTOR3*)&v,(D3DXVECTOR3*)&v); // 정규화
m_pEffect->SetVector(m_hvLightDir,&v);

 

반응형

댓글

💲 추천 글