본문 바로가기

[ C/ C++ 프로그래밍 ]/[ Shader ]

[HLSL] 램버트 확산 조명


ㅇ 램버트 확산 조명
“물체 표면에서 반사하는 빛의 휘도(Luminance)는 빛의 입사 벡터 L과 표면의 법선 벡터 N이 이루는 각도의 코사인에 비례한다”

확산 반사광
-빛의 강도 i, 입사각을 c, 물체의 반사 계수를(물체의 색) k라고 하면, 물체 표면의 색 I은
I = i * k * cos© = I * k * (N*L) (cos© < 0 -> 0)
( N: 법선 벡터, L: 광원이 있는 방향을 가리키는 광원 벡터 )

※ cos©  가 음수이면 값은 0이다

ㅇ 환경광
일정한 밝기로 물체를 전부 칠하는 빛으로, 물체 면의 방향이나 빛의 방향이 전혀 영향을 주지 않는다. 간접광은 다양한 물체에서 다양한 반사를 반복해 다른 물체를 비추므로 그 밝기는 장소에 따라 다르므로 정확하게 계산하는 것이 매우 어렵다. 그래서 간접광을 [복잡하게 날아다니지만 색과 강도는 평군해서 여러 가지 방향에서 간접광이 들어온다고 하자]고 적당히 타협을 이룬 것이 환경광이다.
-빛의 강도 i’, 물체의 반사 계수(물체 색) k’ 라고 하면, 물체 표면의 색 I는
I = I’ * k’
(확산 반사광의 i와 i’, k와 k’는 다른 값을 사용, 일반적으로 i’는 i를 약하게 한 색, k’는 k와 동일한 값을 사용)

ㅇ 램버트 조명 모델
I = i’ * k’ + i * k * (N*L) ( 환경광 + 확산 반사광 )

ㅇ 법선 벡터 계산
-법선 벡터는 월드 행렬의 역행렬의 전치 행렬로 월드 좌표의 법선으로 변환해야 한다.
-법선 벡터를 그대로 이용하고, 계산에 사용되는 광원의 방향을 로컬로 대입하면, 법선 벡터에 대한 좌표 변환이나 정규화가 필요 없기 때문에 더 빠르게 실행된다.

ㅇ Tip
-정점마다 색의 계수들이 변할 경우가 없기 때문에, 미리 계산해 둔다.

출처:[기초다지기] 조명 공식 정리- 램버트 확산 조명 http://cagetu.egloos.com/1746967

      참고 서적







ㅇ HLSL 구현
- Vertex Shader
float4x4 matWorldViewProjection;
float4x4 matWorldInverseTranspose;
float4x4 matWorldInverse;
float3 vLightDir;

// 프로그램으로 부터 입력
float4 I_a;
float4 I_d;
float4 K_a;
float4 K_d;

// 프로그램으로 부터 입력 받지 않고 고정값으로 할경
// float4 I_a = { 0.3f, 0.3f, 0.3f, 0.0f };     
// float4 I_d = { 0.7f, 0.7f, 0.7f, 0.0f };
// float4 K_a=  { 1.0f, 1.0f, 1.0f, 1.0f };
// float4 K_d = { 1.0f, 1.0f, 1.0f, 1.0f };

struct VS_INPUT
{
   float4 Position : POSITION0;
   float3 Normal    : NORMAL;
};

struct VS_OUTPUT
{
   float4 Position : POSITION0;
   float4 Color    : COLOR0;
  
};

VS_OUTPUT vs_main( VS_INPUT Input )
{
   VS_OUTPUT Output;
   Output.Position = mul( Input.Position, matWorldViewProjection );
  
   float3  L =  -vLightDir;
   float3 N = Input.Normal;
  
   Output.Color =   (I_a * K_a) +  (I_d * K_d * max(0, dot(N, L)));

   return( Output );
  
}

- Pixel Shader



struct PS_INPUT
{
   float4 color : COLOR0;
};


float4 ps_main(PS_INPUT input) : COLOR0
{  
   return( input.color );
  
}

ㅇ 렌더몽키로 돌려본 결과