Graphics/DirectX

Direct 3D - *광원

MOLOKINI 2014. 6. 19. 15:05
광원을 알아보기 전에

 


우선 재질의 종류를 알아보자
재질이라는건 메시의 표면 상태를 말하는것이다
빛이 물체의 표면에서 반사되서 변화된 뒤 사람의 눈에 들어오기까지의 과정을 수학적으로 모델링 하기위해 사용되는 값인데... 일단 그냥 이런게 있다는것만 알아두자
광원 구조체(D3DLIGHT9)에 속해있다.
주변광(ambient) : 최저 평균 밝기를 말한다. 똑같은 양으로 모든 면에서 나오는 빛
확산광(diffuse) : 표면의 모든 점들에 균일하게 비춰지는 빛
반사광(specular) : 특정한 방향으로만 반사하는 빛. 광원의 위치와 카메라의 위치에 따라서 달라진다
방출광(emissive) : 메시 표면에서 자체적으로 방출되는 빛, 이 빛이 다른 메시에 영향을 주지는 못한다.



광원의 종류
D3D에서는 4가지 광원을 지원한다
주변광원(ambient light) : 3차원 공간 내에서 메시의 배치나 위치와는 전혀 상관없이 똑같은 양으로 모든곳을 비추는 빛의 강도를 말한다, 방향과 위치가 없으며, 색깔과 강도만 존재한다. - pd3dDevice->SetRenderState(D3DRS_AMBIENT,0x00202020);
점 광원(point light) : 직관적으로 가장 쉽게 생각할 수 있는 빛(백열전구의 빛) - light.Type = D3DLIGHT_POINT;
방향성 광원(directional light) : 모든 광원의 방향이 하나의 방향을 갖는것으로 완벽하지는 않지만 태양을 그 예로 들 수 있다. - light.Type = D3DLIGHT_DIRECTIONAL;
점적 광원(spot light) : 정해진 위치와 범위에만 비추는 특수 조명(영화나 쇼조명) - light.Type = D3DLIGHT_SPOT;

/// Direct3D9를 사용하기 위한 헤더
#include <d3dx9.h>
#include <Windows.h>
#include <mmsystem.h> /// TimeGetTime() 함수 사용하기 위한 헤더

/**-----------------------------------------------------------------------------
 * 전역변수
 *------------------------------------------------------------------------------
 */
LPDIRECT3D9             g_pD3D       = NULL; /// D3D 디바이스를 생성할 D3D객체변수
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; /// 렌더링에 사용될 D3D디바이스
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; /// 정점을 보관할 정점 버퍼

/**-----------------------------------------------------------------------------
 * 이 프로그램의 시작점
 *------------------------------------------------------------------------------
 */

/// 사용자 정점을 정의할 구조체
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; /// 정점의 3차원 좌표
D3DXVECTOR3 normal; /// 정점의 법선 벡터
};
normal이라는 법선벡터가 선언되었는데, 빛은 법선을 기준으로 반사되기 때문에 반드시 광원을 사용할 때는 법선벡터가 있어야지 제대로 된 처리를 할 수 있다.

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)

/**-----------------------------------------------------------------------------
 * Direct3D 초기화
 *------------------------------------------------------------------------------
 */

HRESULT InitD3D( HWND hWnd )
{
    /// 디바이스를 생성하기위한 D3D객체 생성
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;                /// 디바이스 생성을 위한 구조체
    ZeroMemory( &d3dpp, sizeof(d3dpp) );        /// 반드시 ZeroMemory()함수로 미리 구조체를 깨끗이 지워야 한다.
    d3dpp.Windowed = TRUE;                      /// 창모드로 생성
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;   /// 가장 효율적인 SWAP효과
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;    /// 현재 바탕화면 모드에 맞춰서 후면버퍼를 생성
d3dpp.AutoDepthStencilFormat = D3DFMT_D16; /// 복잡한 오브젝트를 그릴것이라 Z버퍼가 필요하다
d3dpp.EnableAutoDepthStencil = TRUE;

    /// 디바이스를 다음과 같은 설정으로 생성한다.
    /// 1. 디폴트 비디오카드를 사용(대부분은 비디오카드가 1개 이다.)
    /// 2. HAL디바이스를 생성한다.(HW가속장치를 사용하겠다는 의미)
    /// 3. 정점처리는 모든 카드에서 지원하는 SW처리로 생성한다.(HW로 생성할경우 더욱 높은 성능을 낸다.)
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }

    /// 컬링 기능을 끈다. 삼각형의 앞면, 뒷면을 모두 렌더링한다.
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

/// Z버퍼를 켠다
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

    return S_OK;
}

HRESULT InitGeometry()
{
if(FAILED(g_pd3dDevice->CreateVertexBuffer(50*2*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL)))
{
return E_FAIL;
}

CUSTOMVERTEX* pVertices;
if(FAILED(g_pVB->Lock(0, 0, (void**)&pVertices, 0)))
return E_FAIL;
for(DWORD i=0; i<50; i++){
FLOAT theta = (2*D3DX_PI*i)/(50-1);
pVertices[2*i+0].position = D3DXVECTOR3(sinf(theta),-1.0f,cosf(theta)); /// 실린더의 아래쪽 원통좌표
pVertices[2*i+0].normal = D3DXVECTOR3(sinf(theta), 0.0f, cosf(theta)); /// 실린더의 아래쪽 원통법선벡터
pVertices[2*i+1].position = D3DXVECTOR3(sinf(theta), 1.0f, cosf(theta));/// 실린더의 위쪽 원통 좌표
pVertices[2*i+1].normal = D3DXVECTOR3(sinf(theta), 0.0f, cosf(theta)); /// 실린더의 위쪽 원통 법선벡터
}
g_pVB->Unlock();

return S_OK;
}
원통을 만드는 원리를 짚고넘어가자
삼각함수를 이용해 아래쪽 원의 정점과 위쪽 원의 정점을 만든다. 나중에 DrawPrimitive()호출 시에 D3DPT_TRIANGLESTRIP으로 연결해주면 되는 것이다.

/**-----------------------------------------------------------------------------
 * 초기화된 객체들을 소거한다.
 *------------------------------------------------------------------------------
 */
VOID Cleanup()
{
    if( g_pd3dDevice != NULL) 
        g_pd3dDevice->Release();

    if( g_pD3D != NULL)
        g_pD3D->Release();

if( g_pVB != NULL)
g_pVB->Release();
}

VOID SetupMatrices()
{
/// 월드 행렬
D3DXMATRIXA16 matWorld;
D3DXMatrixIdentity(&matWorld);
D3DXMatrixRotationX(&matWorld, timeGetTime()/500.0f); /// x축 중심 회전행렬 생성
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld); /// 생성한 회전행렬을 월드행렬로 디바이스에 설정

/// 뷰행렬을 정의하기 위해서는 세가지 값이 필요하다
D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f); /// 1. 눈의 위치 (0, 3.0, -5)
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f); /// 2. 눈이 바라보는 위치 (0,0,0)
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);   /// 3. 천정 방향의 상방벡터 (0,1,0)

D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
/// 1,2,3의 값으로 뷰 행렬 생성

g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
/// 생성한 뷰 행렬을 디바이스에 설정

/// 프로젝션 행렬을 정의하기 위해서는 시야각(FOV=Field Of View)과 
/// 종횡비(aspect ratio), 클리핑 평면의 값이 필요하다.
D3DXMATRIXA16 matProj;
/// matProj : 값이 설정 될 행렬
/// D3DX_PI/4 : FOV(D3DX_PI/4 = 45도)
/// 1.0f : 종횡비
/// 1,0f : 근접 클리핑 평면(near clipping plane)
/// 100.0f : 원거리 클리핑 평면(far clipping plane)
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); /// 생성한 프로젝션 행렬을 디바이스에 설정

}

VOID SetupLights()
{
/// 재질설정
/// 재질은 디바이스에 단 하나만 설정될 수 있다
D3DMATERIAL9 mtrl;
ZeroMemory(&mtrl, sizeof(D3DMATERIAL9));
mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
mtrl.Diffuse.b = mtrl.Ambient.b = 0.0f;
mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
g_pd3dDevice->SetMaterial(&mtrl);

/// 광원설정
D3DXVECTOR3 vecDir;
/// 방향성 광원(directional light)이 향할 빛의 방향
D3DLIGHT9 light; /// 광원구조체
ZeroMemory(&light, sizeof(D3DLIGHT9)); /// 구조체를 0으로 지운다.
light.Type = D3DLIGHT_DIRECTIONAL; /// 광원의 종류(점광원, 방향광원, 점적광원)
light.Diffuse.r = 1.0f; /// 광원의 색과 밝기
light.Diffuse.g = 1.0f;
light.Diffuse.b = 1.0f;
vecDir = D3DXVECTOR3(cosf(timeGetTime()/350.0f), 1.0f, sinf(timeGetTime()/350.0f)); /// 광원의 방향
D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &vecDir); /// 광원의 방향을 벡터로 만든다
light.Range = 1000.0f; /// 광원이 도달할 수 있는 최대 거리
g_pd3dDevice->SetLight(0,&light);
g_pd3dDevice->LightEnable(0,TRUE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00202020); /// 환경광원의 값 설정

}
D3DLIGHT9 구조체에 적당한 값을 넣고 SetLight()함수를 통해 광원을 설치한다.
그리고 광원을 켜(LightEnable())야 한다.
D3D가 광원연산을 할 수 있도록 전체 메인 스위치도 켜(SetRenderState(D3DRS_LIGHTING, TRUE);)야 한다.

그리고 광원과 함께 재질을 생성하고 있는데, 재질은 DrawPrimitive()호출 시 단 하나밖에 지정하지 못한다. 메시가 여러개의 재질로 이루어져 있다면 메시를 재질별로 분리해야한다.

/**-----------------------------------------------------------------------------
 * 화면을 그린다.
 *------------------------------------------------------------------------------
 */
VOID Render()
{
    if( NULL == g_pd3dDevice )
        return;

    /// 후면버퍼와 Z버퍼를 지운다
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
후면버퍼와 Z버퍼를 사용할 것이기 때문에 둘다 지워야한다.
    
    /// 렌더링 시작
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
/// 광원과 재질 설정
SetupLights();

        /// 월드, 뷰, 프로젝션 행렬을 설정한다
SetupMatrices();
/// 정점 버퍼의 내용을 그린다
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2*50-2);
연결해야하는 객체의 수가 2*50-2개이기 때문에

        /// 렌더링 종료
        g_pd3dDevice->EndScene();
    }

    /// 후면버퍼를 보이는 화면으로!
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

/**-----------------------------------------------------------------------------
 * 윈도우 프로시져
 *------------------------------------------------------------------------------
 */
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage( 0 );
            return 0;

        case WM_PAINT:
            Render();
            ValidateRect( hWnd, NULL );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    /// 윈도우 클래스 등록
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      "D3D Tutorial", NULL };
    RegisterClassEx( &wc );

    /// 윈도우 생성
    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 02: vertices", 
                              WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );

    /// Direct3D 초기화
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    { 
/// 정점버퍼 초기화
if(SUCCEEDED(InitGeometry())){

/// 윈도우 출력
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );

/// 메시지 루프
MSG msg; 
ZeroMemory(&msg, sizeof(msg));

while(msg.message!=WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)){
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
Render();
}
}
    }

    /// 등록된 클래스 소거
    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}

 

원래 빙글빙글돌면서 빛이 비춰지는건데 정지이미지라서 요로코롬 보입니다

'Graphics > DirectX' 카테고리의 다른 글

Direct 3D - *메시  (0) 2014.06.19
Direct 3D - *텍스쳐  (0) 2014.06.19
Direct X - *행렬  (0) 2014.06.19
Direct X - *정점  (0) 2014.06.19
Direct X - *디바이스 생성  (0) 2014.06.19