DirectX 9 ch5

Download Report

Transcript DirectX 9 ch5

Ch5. 조명
목표
 Direct3D가 지원하는 광원의 종류에 대해 배우고 이들
광원에서 발생하는 빛의 종류에 대해 확인한다.
 빛과 빛이 만나는 표면 간의 상호작용을 정의하는 방
법을 이해한다.
 삼각형이 바라보는 방향을 수학적으로 계산하여 빛이
삼각형과 만나는 각도를 알아내는 방법을 확인한다.
5.1 조명의 요소
 Ambient light (환경광) - ambient light은 다른 표면에 반사되어 전반
적인 장면을 밝게 하는 빛을 모델링한다. 예를 들어, 물체의 일부분은
광원의 직접적인 시야에서 가려져 있더라도 약간의 빛을 받는다. 이런
부분은 다른 표면에서 반사된 빛을 받는 것이다. ambient light은 이와
같이 반사된 빛을 비교적 저렴하고 간단하게 구현하는 방법이다.
 Diffuse light (난반사광) - dl은 특정한 방향으로 진행하며, 표면에 닿
으면 모든 방향으로 동일하게 반사된다. 모든 방향으로 동일하게 반사
되어 위치와는 관계없이 관찰자의 눈에 빛이 도달하므로 관찰자의 위
치는 고려할 필요가 없다. 즉, diffuse light의 방정식에는 빛의 방향과
표면의 형태만을 고려하면 된다.__ diffuse light은 광원에서 발산되는
가장 일반적인 빛이다.
 Specular light (정반사광) - specular light은 특정한 방향으로 진행하
며, 표면에 닿으면 한 방향으로 강하게 반사되어 특정한 각도에서만 관
찰할 수 있다. 한 방향으로만 빛을 반사하기 때문에 specular light의 방
정식에는 빛의 방향과 표면의 형태, 그리고 관찰자의 시점을 모두 고려
해야 한다. specular light은 반짝이는 표면에 빛이 반사되는 효과를 모
델링하는 데 이용된다.
5.1 조명의 요소
 specular light은 다른 타입의 빛에 비해 좀 더 많은 계산을
필요로 하기 때문에 Direct3D는 이를 끌 수 있는 옵션을 제
공한다. 실제로 비활성화가 디폴트 값이며, 활성화하기 위
해서는 IDirect3DDevice9::SetRenderState의
D3DRS_SPECULARENABLE 상태를 true로 설정해야 한다.
g_device->SetRenderState( D3DRS_SPECULARENABLE, true );
 각각의 빛의 형은 빛의 컬러를 표현하는 D3DCOLOR 나
D3DCOLORVALUE로 나타낼 수 있다.
D3DXCOLOR ambientRed( 1.0f, 0.0f, 0.0f, 1.0f );
D3DXCOLOR diffuseBlue( 0.0f, 0.0f, 1.0f, 1.0f );
D3DXCOLOR specularWhite( 1.0f, 1.0f, 1.0f, 1.0f );
D3DCOLOR의 alpha 값은 빛의 컬러를 표현할 때는 사용
되지 않는다.
5.2 재질(Material)
 현실에서 물체의 컬러는 물체가 반사하는 빛의 컬러에 의해 결정된다.
예를 들어, 빨간 공은 빛의 컬러 중에서 빨간색만 반사하고 빨간색 이
외의 다른 색은 모두 흡수하며, 반사된 빨간색이 우리 눈에 도달하여
공을 빨간색으로 인식하는 것이다.
 Direct3D는 물체의 material을 정의할 수 있도록 하여 이와 같은 현상
을 모델링하는데, material은 물체의 표면에서 반사할 빛의 퍼센테이지
를 지정할 수 있도록 해준다. material을 나타내는 데에는
D3DMATERIAL9구조체가 사용된다.
typedef struct _D3DMATERIAL9 {
D3DCOLORVALUE Diffuse; // 표면이 반사하는 diffuse lighting의 양을 지정한다.
D3DCOLORVALUE Ambient; // 표면이 반사하는 ambient lighting의 양을 지정한다.
D3DCOLORVALUE Specular; // 표면이 반사하는 specular lighting의 양을 지정한다.
D3DCOLORVALUE Emissive; // 전반적인 표면의 컬러를 더하는 데 사용되며,
// 물체 자체가 빛을 발하는 것 처럼 좀 더 밝은 물체 효과를 만들어낸다.
float Power;
// specular lighting의 날카로운 정도(sharpness)를 지정하며,
// 값이 높아질수록 highlight가 강조된다.
} D3DMATERIAL9;
5.2 재질(Material)
 예를 들어, 빨간 공을 원한다면 빨간 빛만을 반사하고
다른 모든 빛을 흡수하도록 재질을 설정하면 된다.
D3DMATERIAL9 redMaterial;
ZeroMemory( &red, sizeof( redMeterial ) );
redMeterial.Diffuse = D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f );
redMeterial.Ambient = D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f );
redMeterial.Specular = D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f );
redMeterial.Emissive = D3DXCOLOR( 0.0f, 0.0f, 0.0f, 1.0f );
redMeterial.Power = 5.0f;
5.2 재질(Material)
 D3DMATERIAL9 구조체를 채우는 일을 다음과 같이 미리 정의해 놓고 사용할 수 있다.
D3DMATERIAL d3d::CreateMaterial(D3DXCOLOR ambient, D3DXCOLOR diffuse, D3DXCOLOR specular, D3DX
COLOR emissive, float power)
{
D3DMATERIAL9 material;
material.Ambient = ambient;
material.Diffuse = diffuse;
material.Specular = specular;
material.Emissive = emissive;
material.Power = power;
return material
}
namespace d3d
{
...
D3DMATERIAL9 CreateMaterial(D3DXCOLOR, D3DXCOLOR, D3DXCOLOR, D3DXCOLOR, float);
const D3DMATERIAL9 MATERIAL_WHITE = CreateMaterial( WHITE, WHITE, WHITE, BLACK, 8.0f );
const D3DMATERIAL9 MATERIAL_RED = CreateMaterial( RED, RED, RED, BLACK, 8.0f );
const D3DMATERIAL9 MATERIAL_GREEN = CreateMaterial( GREEN, GREEN, GREEN, BLACK, 8.0f );
const D3DMATERIAL9 MATERIAL_BLUE = CreateMaterial( BLUE, BLUE, BLUE, BLACK, 8.0f );
const D3DMATERIAL9 MATERIAL_YELLOW = CreateMaterial( YELLOW, YELLOW, YELLOW, BLACK, 8.0f );
...
}
 vertex 구조체는 material 속성을 가지지 않으며, 대신 현재의 material을 지정하는
IDirect3DDevice9::SetMaterial을 이용한다.
5.3 버텍스 법선(Vertex Normal)
 face normal(면 법선)은 다각형이 바라보는 방향을 표시하는 벡터이다. vertex
normal은 기본적으로 동일한 아이디어에 기반을 두고 있지만, 각각의 폴리곤
에 따른 법선이 아니라 폴리곤을 구성하는 벡터에 대한 법선을 정의한다.
 Direct3D는 표면에 닿는 빛의 각도를 계산하기 위해 vertex normal을 필요로
한다. 빛의 계산은 vertex 단위로 수행되므로 각 vertex의 표면 각도(법선)가 필
요한 것이다. vertex normal과 face normal이 동일한 것은 아니라는 데 주의하
자. 구체/원은 이를 보여주는 좋은 예이다.
 vertex normal을 표기하기 위해서는 vertex 구조체를 다시 정의해야 한다.
struct Vertex
{
float x, y, z;
float nx, ny, nz;
static const DWORD FVF;
}
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
 4장에서 사용되었던 컬러와 관련된 멤버가 제거된 이유는 vertex컬러를 계산
하는데 조명을 이용할 것이기 때문이다.
5.3 버텍스 법선(Vertex Normal)
 입방체나 구체와 같은 단순한 물체의 경우에는 눈으로도
vertex normal을 볼 수 있지만 좀 더 복잡한 mesh의 경우에
는 다른 방법이 필요하다. 점 p0, p1, p2로 이루어진 삼각형이
있고, 각 점에 대한 법선 n0, n1, n2를 계산해야 한다고 가정
할 때, 가장 간단한 방법은 삼각형의 face normal을 찾고 이
것을 vertex normal에 이용하는 것이다.
 1. 먼저, 삼각형에 있는 두 개의 벡터를 계산한다.
p1 - p0 = u
p2 - p0 = v
 2. 다음으로, face normal을 계산한다.
n=uxv
 3.각각의 vertex normal은 face normal과 같으므로,
n0 = n1 = n2 = n
5.3 버텍스 법선(Vertex Normal)
 다음 함수는 위의 내용을 코드로 옮긴 것이다. 단, 이
함수는 vertex의 winding order가 시계 방향이라고 가
정한다(만약, 그렇지 않다면 법선은 반대 방향에 위치
한다).
void ComputeNormal(D3DXVECTOR3* pOut, D3DXVECTOR3* p0, D3DXVECTOR3* p1, D
3DXVECTOR3* p2)
{
D3DXVECTOR3 u = *p1 - *p0;
D3DXVECTOR3 v = *p2 - *p0;
D3DXVec3Cross( pOut, &u, &v );
D3DXVec3Normal( pOut, pOut );
}
5.3 버텍스 법선(Vertex Normal)
 삼각형으로 부드러운 표면을 표현할 때, face normal을 vertex normal
로 이용하면 그다지 부드러운 결과를 얻을 수 없다. vertex normal을
얻기 위한 좀 더 나은 방법으로는 법선의 평균을 이용하는 방법이 있다.
점 v의 vertex normal vn을 찾기 위해서는 v를 공유하는 모든 face
normal을 구한 다음, 이들의 평균을 구하여 vn을 구할 수 있다.
 변환 단계에서 vertex normal이 왜곡되는 현상이 발생할 수 있으므로,
IDirect3DDevice9::SetRenderState의
D3DRS_NORMALIZENORMALS를 true로 하여 변환 이후에
Direct3D가 모든 법선을 다시 정리하도록 하는 것이 안전하다.
5.4 광원
 Point light (점 광원): point light는 월드 스페이스 내
에 위치를 가지며 모든 방향으로 빛을 발산한다.
 Directional light (방향성 광원): directional light는
위치는 가지지 없지만 지정된 방향으로 평행하게 빛을
빛을 발산한다.
 Spot light (스포트 광원): spot light는 손전등 빛과 비
슷하다. 광원은 위치를 가지며 특정한 방향으로 원뿔
형태의 빛을 발산한다. 원뿔은 θ와 φ의 두 가지 각도에
의해 결정되는데, θ는 안쪽, φ는 바깥쪽의 원뿔을 나타
낸다.
5.4 광원
 Direct3D에서 광원은 D3DLIGHT9 구조체를 통해 나
타낼 수 있다.
typedef struct _D3DLIGHT9 {
D3DLIGHTTYPE Type; // 만들고자 하는 광원의 타입
(D3DLIGHT_POINT, D3DLIGHT_SPOT, D3DLIGHT_DIRECTIONAL)
D3DCOLORVALUE Diffuse; // 광원이 발산하는 diffuse light의 컬러
D3DCOLORVALUE Specular; // 광원이 발산하는 specular light의 컬러
D3DCOLORVALUE Ambient; // 광원이 발산하는 ambient light의 컬러
D3DVECTOR Position; // 광원의 위치 (D3DLIGHT_DIRECTIONAL의 경우 의미가 없음)
D3DVECTOR Direction; // 빛의 방향 (D3DLIGHT_POINT에서는 사용하지 않음)
float Range;
// 빛이 완전히 소멸할 때까지 나갈 수 있는 최대 거리
// (D3DLIGHT_DIRECTIONAL의 경우 의미가 없으며, root(FLT_MAX)보다 클 수 없다.
float Falloff;
// D3DLIGHT_SPOT에서만 사용되며, 안쪽/바깥쪽 원뿔 간의
//빛의 세기 차이를 정의(보통 1.0f)
float Attenuation0; // 감쇠(attenuation)는 거리에 따라 빛의 세기가 약해지는 정도를 정의
float Attenuation1; // Attenuation0은 상수 감소, Attenuation1은 선형 감소, Attenuation2는 이차 감소
float Attenuation2; // Attenuation = 1/(A0 + A1*D + A2*D^2) (단, D는 광원으로부터의 거리)
float Theta;
// D3DLIGHT_SPOT에서만 이용되며, 안쪽 원뿔의 각도를 라디안으로 지정
float Phi;
// D3DLIGHT_SPOT에서만 이용되며, 바깥쪽 원뿔의 각도를 라디안으로 지정
} D3DLIGHT9;
5.4 광원
 초기화하는 과정을 다음과 같이 함수로 작성해 두면 편하게(?) 사용할 수
있다.
D3DLIGHT9 CreateDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
D3DLIGHT9 light;
ZeroMemory( &light, sizeof( light ) );
light.Type = D3DLIGHT_DIRECTIONAL;
light.Ambient = *color * 0.4f;
light.Diffuse = *color;
light.Specular = *color * 0.6f;
light.Direction = *direction;
return light;
}
// +x축의 방향으로 평행하고 흰색 빛을 발산하는 방향성 광원
D3DVECTOR3 dir( 1.0f, 0.0f, 0.0f );
D3DXCOLOR color = WHITE;
D3DLIGHT9 light = CreateDirectionalLight( &dir, &color );
 D3DLIGHT9를 초기화한 다음에는 IDirect3DDevice9::SetLight를 호출하
여 Direct3D가 내부적으로 관리하는 리스트에 광원을 등록시켜야 하며, 광
원을 켜거나 끌 때에는 IDirect3DDevice9::LightEnable을 이용한다.