실시간 3차원 그래픽에서, 가장 중요한 기술은 '보이지 않는 것들을 그리지 않기'라는 것
절두체 컬링은 바로 이런 기술 중 하나다


절두체의 정의
절두체 컬링이란 전체 3차원 월드에는 대단히 많은 폴리곤과 오브젝트가 있지만, 이들 중에서 실제로 카메라의 시야 범위에 포함되는 것들만 렌더링하고, 나머지 것들은 렌더링 하지 않는 기법을 말한다. 3D엔진 개발에 있어 가장 중요한 속도 증가 기법 중 하나다.



절두체를 이루는 6개의 평면
근평면 : 카메라와 수직하며 제일 가까운 곳의 시야 범위를 나타내는 평면
원평면 : 카메라와 수직하며 제일 먼 곳의 시야 범위를 나타내는 평면
좌평면 : 카메라의 좌측 시야 범위를 나타내는 평면
우평면 : 카메라의 우측 시야 범위를 나타내는 평면
상평면 : 카메라의 상단 시야 범위를 나타내는 평면
하평면 : 카메라의 하단 시야 범위를 나타내는 평면

렌더링 파이프라인에 의하면, 이렇게 6개의 평면에 포함되는 영역만을 렌더링하게 되므로 나머지 영역에 포함된 폴리곤들은 D3D의 DrawPrimitive()함수에게 부담만 줄 뿐이다,
이때, 6개의 평면 내부와 외부를 판단하기 위해 평면방정식이라는 수학적 도구를 사용한다.


평면방정식의 정의
평면방정식이란 무한 평면을 정의하는 수학적 도구로, 간단한 수식으로 평면을 표현한 것이다.

ax + by + cz + d = 0

여기서 a, b, c는 평면의 방향을 나타내는 법선 벡터고, d는 평면과 원점간의 거리를 나타내는 값이다.
즉, 이 수식을 만족하는 (x, y, z)좌표값은 모두 이 평면 위에 있는 점들이고, 이러한 점들은 무수히 많이 있다.

일반적으로 3D 그래픽에서 평면 방정식은 평면을 구성하는 세개의 점을 뽑아서 이들 세개의 점으로부터 식을 산출해낸다.

 세개의 점으로부터 평면의 법선 벡터값을 구하는 방법을 보여주는 그림이다.

세개의 점 v0, v1, v2로부터 v' 와 v''를 만들고, 이들 2개의 벡터를 외적연산하면 직교하는 벡터 N=(a,b,c)가 나오는데, 이렇게 얻어진 벡터 N=(a,b,c)가 평면 방정식의 법선 벡터다

d값은 v0, v1, v2중의 어느 하나의 값을 평면방정식에 대입해서 얻어내면 된다.

예를 들면, ax1 + by1 + cz1 + d = 0이므로 d = -(ax1 + by1 + cz1)으로 구할 수 있다 ㅇㅋ

이런 함수 역시 DX에서는 준비되어있다.


D3DXPLANE *D3DXPlaneFromPoints(D3DXPLANE *pOut, CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2, CONST D3DXVECTOR3 *pV3);


pV1, pV2, pV3에 정점의 값을 대입해서 호출하면 pOut에 평면 방정식의 a, b, c, d값을 구해서 돌려준다.



평면 방정식의 성질

우리는 평면 방정식의 정의에서 평면에 방향이 있음을 알 수 있었는데, 평면에 방향이 있다는 것은 어떠한 한 점이 평면의 앞에 있는지, 뒤에 있는지를 판단할 수 있다는 얘기가 된다.



 

평면 방정식에 한 점의 좌표를 대입해서 나온 결과값은 다음과 같은 의미를 갖는다


평면방정식의 연산 결과

위 그림의 (1)방정식 : 점이 평면 위에 있다.

위 그림의 (2)방정식 : 점이 평면 앞에 있다.

위 그림의 (3)방정식 : 점이 평면 뒤에 있다.


이해좀 되시지요? ㅇㅋ^^


결과값의 부호는 평면을 기준으로 점의 위치를 나타내며, 결과값이 나타내는 숫자는 점과 평면간의 거리를 나타낸다.


요런 성질을 이용해서 절두체 내부에 점이 포함되는지, 얼마나 떨어져있는지 알 수 있다.



절두체 컬링 원리

절두체 내부에 점이 포함되는지를 판단하기 위해 6개의 평면 방정식에 점의 좌표를 대입해서 모든 평면방정식의 결과값이 양수면 이 점은 절두체 내부에 포함되어 있는 것이다.



경계구 컬링 기법

'평면 방정식의 성질'에서의 절두체 컬링 기법은 점에대한 테스트만을 하기 때문에 여러개의 점으로 이루어진 메시의 경우 모든점에 대해서 테스트를 하는것은 엄청난 부담이 될 것이다. 공부하면서 이 점이 궁금했습니다.

캐릭터 하나가 1000개의 폴리곤으로 이루어져 있다면, 어림잡아 정점은 3000개 정도로 이루어져 있다고 볼 수 있는데, 이런 캐릭터가 100여개 등장한다면 몇번의 계산이 필요할까?


필요한 연산 = 3000 * 100 * 6(평면방정식)


......쩌네

매 프레임마다 이런 연산을 한다면 절두체 컬링은 당연 안하는게 낫지낫지낫지요?

하지만, 다행이 좀 더 간결한 방법을 써볼겁니다. 바로 그것이 경계구 컬링기법

메시 전체를 감싸는 경계구를 정의하고 이 구가 절두체에 포함되는지를 판단하는 것이다.

경계구의 경우, 중심점과 반지름이라는 2개의 숫자로 이루어지기 때문에 상당히 빠른 판정을 내릴 수 있다. 물론 이외에도 AABB나 OBB등의 기법이 있지만, 간단하니까 경계구 기법을 알아보자


평면 방정식 ax + bx + cz + d = 0에 경계구의 중심점 Vcenter = (Xc, Yc, Zc)의 값을 대입해서 나온 결과값 AXc + BYc + CZc + D가 경계구의 반지름 r보다 작다면 평면과 중심점의 거리가 반지름보다 작은것이므로 경계구에 평면이 포함되는 것이다.



위 그림은 경계구의 정의


요고는 경계구가 절두체에 포함되는 경우

 

 요고는 경계구가 절두체에 일부만 포함되는 경우

이럴 때 경계구 기법을 사용하면, 메시가 절두체에 포함된 것으로 판정을 할 수 있다.



ZFrustum 클래스는 절두체를 구성하는 8개의 정점과 6개의 평면 방정식을 저장하기 위한 멤버변수를 갖고 있다.

Make()함수는 Mview * Mproj 행렬을 받아 월드 좌표계에서의 절두체 정점 좌표를 구한다. 어떻게 구하는지는 실제 함수 소스(ZFrustum.cpp)를 분석해보자

Isin()함수는 절두체에 점이 포함되어 있는지를 판정하며, IsInSphere()함수는 경계구의 반지름을 사용하여 절두체 포함 여부를 판정한다.

반드시 필요한건 아니지만, Draw()함수를 이용해 절두체를 시각적으로 그릴 수 있게 해놨다. 하지만 실제 개발시에는 절두체따위 그리지 않는다. 생각해보세요 왜 그리겠습니꺄?



#define MAINBODY


#include <d3d9.h>

#include <d3dx9.h>

#include "ZFLog.h"

#include "ZCamera.h"

#include "ZFrustum.h"


#define WINDOW_W 500

#define WINDOW_H 500

#define WINDOW_TITLE "높이맵-카메라-절두체컬링"

#define BMP_HEIGHTMAP "map128.bmp"


ZFrustum* g_pFrustum = NULL; // Frustum 클래스

ZCamera* g_pCamera = NULL; // Camera 클래스

HWND g_hwnd = NULL;


LPDIRECT3D9 g_pD3D = NULL;

LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;

LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;

LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;


LPDIRECT3DTEXTURE9 g_pTexHeight = NULL; // 텍스쳐 높이맵

LPDIRECT3DTEXTURE9 g_pTexDiffuse = NULL; // 텍스쳐 색깔맵

D3DXMATRIXA16 g_matAni;

D3DXMATRIXA16 g_matWorld;

D3DXMATRIXA16 g_matView;

D3DXMATRIXA16 g_matProj;


DWORD g_cxHeight = 0;

DWORD g_czHeight = 0;

DWORD g_dwMouseX = 0;

DWORD g_dwMouseY = 0;


int g_nTriangles = 0;

D3DXVECTOR3* g_pvHeightMap = NULL;

BOOL g_bHideFrustum = TRUE; // 절두체를 안그릴것인가?

BOOL g_bLockFrustum = FALSE; // 절두체를 고정 할 것인가?

BOOL g_bWireFrame = FALSE; // 와이어 프레임으로 그릴 것인가?


struct CUSTOMVERTEX

{

D3DXVECTOR3 p;

D3DXVECTOR3 n;

D3DXVECTOR3 t;

};


#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)


struct MYINDEX

{

WORD _0, _1, _2; // 일반적으로 인덱스는 16비트의 크기를 갖는다

};


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;


    /// 디바이스를 다음과 같은 설정으로 생성한다.

    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_CCW);

/// Z버퍼를 켠다

g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);


return S_OK;

}


void InitMatrix()

{

D3DXMatrixIdentity( &g_matWorld );

    g_pd3dDevice->SetTransform( D3DTS_WORLD, &g_matWorld );


    /// 뷰행렬을 설정

    D3DXVECTOR3 vEyePt( 0.0f, 100.0f, -(float)g_czHeight );

    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );

    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );

    D3DXMatrixLookAtLH( &g_matView, &vEyePt, &vLookatPt, &vUpVec );

    g_pd3dDevice->SetTransform( D3DTS_VIEW, &g_matView );


    /// 프로젝션 행렬 설정    

    D3DXMatrixPerspectiveFovLH( &g_matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f );

    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &g_matProj );


/// 절두체 컬링용 프로젝션 행렬

D3DXMatrixPerspectiveFovLH(&g_matProj, D3DX_PI/4, 1.0f, 1.0f, 200.0f);


g_pCamera->SetView( &vEyePt, &vLookatPt, &vUpVec );

}


HRESULT InitTexture()

{

// 높이맵 텍스쳐

// D3DFMT_X8R8G8B8와 D3DPOOL_MANAGED를 주기 위해 이 함수 사용

if(FAILED(D3DXCreateTextureFromFileEx(g_pd3dDevice, BMP_HEIGHTMAP, D3DX_DEFAULT,

D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT,

D3DX_DEFAULT, 0, NULL, NULL, &g_pTexHeight)))

return E_FAIL;


// 색깔 맵

if(FAILED(D3DXCreateTextureFromFile(g_pd3dDevice, "tile2.tga", &g_pTexDiffuse)))

return E_FAIL;


return S_OK;

}


HRESULT InitVB() // 높이맵 정보도 여기서 초기화 시킨다

{

D3DSURFACE_DESC ddsd;

D3DLOCKED_RECT d3drc;


g_pTexHeight->GetLevelDesc(0, &ddsd); // 텍스쳐의 정보

g_cxHeight = ddsd.Width; // 텍스쳐의 가로크기

g_czHeight = ddsd.Height; // 텍스쳐의 세로크기

g_pLog->Log("Texture Size : [%d, %d]", g_cxHeight, g_czHeight);

g_pvHeightMap = new D3DXVECTOR3[g_cxHeight * g_czHeight]; // 높이 맵 배열 생성


/// 정점 버퍼 생성

if(FAILED(g_pd3dDevice->CreateVertexBuffer(ddsd.Width*ddsd.Height*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,

D3DPOOL_DEFAULT, &g_pVB, NULL)))

{

return E_FAIL;

}


// 텍스쳐 메모리 락

g_pTexHeight->LockRect(0, &d3drc, NULL, D3DLOCK_READONLY);

VOID* pVertices;

// 정점버퍼 락

if(FAILED(g_pVB->Lock(0, g_cxHeight*g_czHeight*sizeof(CUSTOMVERTEX), (void**)&pVertices, 0)))

return E_FAIL;


CUSTOMVERTEX v;

CUSTOMVERTEX* pV = (CUSTOMVERTEX*)pVertices;


for(DWORD z = 0; z < g_czHeight; z++)

{

for(DWORD x = 0; x < g_cxHeight; x++)

{

v.p.x = (float)x-g_cxHeight/2.0f; // 정점의 x좌표 (메시를 원점에 정렬)

v.p.z = -((float)z-g_czHeight/2.0f); // 정점의 z좌표 (메시를 원점에 정렬), z축이 모니터 안쪽이므로 -를 곱한다.

v.p.y = ((float)(*((LPDWORD)d3drc.pBits+x+z*

(d3drc.Pitch/4))&0x000000ff))/10.0f; // DWORD이므로 pitch/4


v.n.x = v.p.x;

v.n.y = v.p.y;

v.n.z = v.p.z;


D3DXVec3Normalize(&v.n, &v.n);

v.t.x = (float)x / (g_cxHeight-1);

v.t.y = (float)z / (g_czHeight-1);

*pV++ = v; // 정점버퍼에 정점 저장

//g_pLog->Log("[%f, %f, %f]", v.x, v.y, v.z);


g_pvHeightMap[z * g_cxHeight + x] = v.p; // 높이맵에 정점 저장

}

}

g_pVB->Unlock();

g_pTexHeight->UnlockRect(0);


return S_OK;

}


HRESULT InitIB()

{

if(FAILED(g_pd3dDevice->CreateIndexBuffer((g_cxHeight-1)*(g_czHeight-1)*2*sizeof(MYINDEX),

0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIB, NULL)))

{

return E_FAIL;

}


g_nTriangles = 0;


return S_OK;

}


void SetupLights()

{

// 재질(material) 설정

// 재질은 디바이스에 단 하나만 설정될 수 있다.

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 = 1.0f;

mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;

g_pd3dDevice->SetMaterial(&mtrl);


// 광원설정

D3DXVECTOR3 vecDir; // 방향성 광원이 향할 빛의 방향

D3DLIGHT9 light; // 광원 구조체

ZeroMemory(&light, sizeof(D3DLIGHT9)); // 구조체를 0으로 지운다

light.Type = D3DLIGHT_DIRECTIONAL; // 광원의 종류

light.Diffuse.r = 1.0f;

light.Diffuse.g = 1.0f;

light.Diffuse.b = 0.0f;

vecDir = D3DXVECTOR3(1, 1, 1); // 광원고정

vecDir = D3DXVECTOR3(cosf(GetTickCount()/350.0f), 1.0f, sinf(GetTickCount()/350.0f)); // 광원회전

D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &vecDir); // 광원의 방향을 단위벡터로 만든다

light.Range = 1000.0f; // 광원이 다다를 수 있는 최대거리


g_pd3dDevice->SetLight(0, &light); // 디바이스에 0번 광원을 설치한다

g_pd3dDevice->LightEnable(0, TRUE); // 0번 광원일 켠다

g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE); // 광원설정을 켠다


g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00909090); // 환경광원의 값을 설정한다

}


void LogStatus(void)

{

g_pLog->Log("WireFrame : %d", g_bWireFrame);

g_pLog->Log("HideFrustum : %d", g_bHideFrustum);

g_pLog->Log("LockFrustum : %d", g_bLockFrustum);

}


void LogFPS(void)

{

static DWORD nTick = 0;

static DWORD nFPS = 0;


if(GetTickCount() - nTick > 1000)

{

nTick = GetTickCount();

g_pLog->Log("FPS:%d", nFPS);


/// 카메라의 위치값 출력

D3DXVECTOR3* pv;

pv = g_pCamera->GetEye();

g_pLog->Log("EYE:[%f,%f,%f]",pv->x, pv->y, pv->z );

nFPS = 0;

LogStatus(); // 1초에 한번씩 상태정보 출력

return;

}

nFPS++;

}


HRESULT ProcessFrustumCull()

{

DWORD i[4]; // 임시로 저장할 인덱스 정보

BOOL b[4]; // 임시로 저장할 절두체 컬링 결과값

MYINDEX idx;

    MYINDEX* pI;

    if( FAILED( g_pIB->Lock( 0, (g_cxHeight-1)*(g_czHeight-1)*2 * sizeof(MYINDEX), (void**)&pI, 0 ) ) )

        return E_FAIL;


g_nTriangles = 0;


for( DWORD z = 0 ; z < g_czHeight-1 ; z++ )

{

for( DWORD x = 0 ; x < g_cxHeight-1 ; x++ )

{

i[0] = (z*g_cxHeight+x);

i[1] = (z*g_cxHeight+x+1);

i[2] = ((z+1)*g_cxHeight+x);

i[3] = ((z+1)*g_cxHeight+x+1); // 좌우상단 좌우하단

b[0] = g_pFrustum->IsIn(&g_pvHeightMap[i[0]]); // 좌측상단 정점이 절두체 안에 있는가?

b[1] = g_pFrustum->IsIn(&g_pvHeightMap[i[1]]); // 우측상단 정점이 절두체 안에 있는가?

b[2] = g_pFrustum->IsIn(&g_pvHeightMap[i[2]]); // 좌측하단 정점이 절두체 안에 있는가?


if(b[0] | b[1] | b[2]) // 셋중 하나라도 절두체 안에 있으면 렌더링한다

{

idx._0 = i[0];

idx._1 = i[1];

idx._2 = i[2];

*pI++ = idx;

g_nTriangles++;

}


b[2] = g_pFrustum->IsIn(&g_pvHeightMap[i[2]]); // 좌측하단 정점이 절두체 안에 있는가?

b[1] = g_pFrustum->IsIn(&g_pvHeightMap[i[1]]); // 우측상단 정점이 절두체 안에 있는가?

b[3] = g_pFrustum->IsIn(&g_pvHeightMap[i[3]]); // 우측하단 정점이 절두체 안에 있는가?


if(b[2] | b[1] | b[3]) // 셋중 하나라도 절두체 안에 있으면 렌더링한다

{

idx._0 = i[2];

idx._1 = i[1];

idx._2 = i[3];

*pI++ = idx;

g_nTriangles++;

}

}

}

    g_pIB->Unlock();


return S_OK;

}


void ProcessMouse( void )

{

POINT pt;

float fDelta = 0.001f; // 마우스의 민감도, 이 값이 커질수록 많이 움직인다.


GetCursorPos( &pt );

int dx = pt.x - g_dwMouseX; // 마우스의 변화값

int dy = pt.y - g_dwMouseY; // 마우스의 변화값


g_pCamera->RotateLocalX( dy * fDelta ); // 마우스의 Y축 회전값은 3D world의  X축 회전값

g_pCamera->RotateLocalY( dx * fDelta ); // 마우스의 X축 회전값은 3D world의  Y축 회전값

D3DXMATRIXA16* pmatView = g_pCamera->GetViewMatrix(); // 카메라 행렬을 얻는다.

g_pd3dDevice->SetTransform( D3DTS_VIEW, pmatView ); // 카메라 행렬 셋팅



// 마우스를 윈도우의 중앙으로 초기화

SetCursor( NULL ); // 마우스를 나타나지 않게 한다.

RECT rc;

GetClientRect( g_hwnd, &rc );

pt.x = (rc.right - rc.left) / 2;

pt.y = (rc.bottom - rc.top) / 2;

ClientToScreen( g_hwnd, &pt );

SetCursorPos( pt.x, pt.y );

g_dwMouseX = pt.x;

g_dwMouseY = pt.y;

}


void ProcessKey( void )

{

if( GetAsyncKeyState( 'A' ) ) g_pCamera->MoveLocalZ( 0.5f ); // 카메라 전진!

if( GetAsyncKeyState( 'Z' ) ) g_pCamera->MoveLocalZ( -0.5f ); // 카메라 후진!

if( GetAsyncKeyState( VK_ESCAPE ) ) PostMessage( g_hwnd, WM_DESTROY, 0, 0L );

}


void ProcessInputs( void )

{

ProcessMouse();

ProcessKey();

}


VOID Animate()

{

D3DXMatrixIdentity(&g_matAni);


SetupLights();

ProcessInputs();


D3DXMATRIXA16 m;

D3DXMATRIXA16 *pView;

pView = g_pCamera->GetViewMatrix(); // 카메라 클래스로부터 행렬 정보를 얻는다.

m = *pView * g_matProj; // 월드좌표를 얻기 위해서 View*Proj 행렬을 계산한다.

if(!g_bLockFrustum) g_pFrustum->Make(&m); // View*Proj 행렬로 절두체를 만든다.

ProcessFrustumCull(); // 절두체를 기반으로 보여질 삼각형의 인덱스를 만든다.

LogFPS();

}


VOID CleanUp()

{

if(g_pVB != NULL)

g_pVB->Release();


if(g_pIB != NULL)

g_pIB->Release();


if(g_pd3dDevice != NULL)

g_pd3dDevice->Release();


if(g_pD3D != NULL)

g_pD3D->Release();


if( g_pTexHeight != NULL )        

        g_pTexHeight->Release();


    if( g_pTexDiffuse!= NULL )        

        g_pTexDiffuse->Release();


delete[] g_pvHeightMap;

}


VOID DrawMesh(D3DXMATRIXA16* pMat)

{

g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, g_bWireFrame ? D3DFILL_WIREFRAME : D3DFILL_SOLID);

g_pd3dDevice->SetTransform(D3DTS_WORLD, pMat);

g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));

g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);

g_pd3dDevice->SetIndices(g_pIB);

g_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, g_cxHeight*g_czHeight, 0, g_nTriangles);

}


HRESULT InitGeometry()

{

if(FAILED(InitTexture())) return E_FAIL;

if(FAILED(InitVB())) return E_FAIL;

if(FAILED(InitIB())) return E_FAIL;


InitMatrix();


// 최초의 마우스 위치 보관

POINT pt;

GetCursorPos( &pt );

g_dwMouseX = pt.x;

g_dwMouseY = pt.y;

return S_OK;

}


VOID Render()

{

    /// 후면버퍼와 Z버퍼를 지운다

    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,255,255), 1.0f, 0 );

    

Animate();

    /// 렌더링 시작

    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )

    {

// 0번 텍스쳐 스테이지에 텍스쳐 고정(색깔 맵)

g_pd3dDevice->SetTexture(0, g_pTexDiffuse);


// 0번 텍스쳐 스테이지의 확대 필터

g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);


// 0번 텍스쳐 : 0번 텍스쳐 인덱스 사용

g_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);


g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);

g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);

g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);

DrawMesh(&g_matAni);


if(!g_bHideFrustum) g_pFrustum->Draw(g_pd3dDevice);

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_KEYDOWN:

switch(wParam)

{

case VK_ESCAPE :

PostMessage(hWnd, WM_DESTROY, 0, 0L);

break;

case '1' :

g_bWireFrame = !g_bWireFrame;

break;

case '2' :

g_bLockFrustum = !g_bLockFrustum;

g_bHideFrustum = !g_bLockFrustum;

break;

}

break;

    }


    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,

                      "BasicFrame", NULL };

    RegisterClassEx( &wc );


    /// 윈도우 생성

    HWND hWnd = CreateWindow( "BasicFrame", WINDOW_TITLE, 

                              WS_OVERLAPPEDWINDOW, 100, 100, WINDOW_W, WINDOW_H,

                              GetDesktopWindow(), NULL, wc.hInstance, NULL );


g_hwnd = hWnd;

g_pLog = new ZFLog(ZF_LOG_TARGET_WINDOW);

g_pCamera = new ZCamera; // 클래스 인스턴스화

g_pFrustum = new ZFrustum;


    /// 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();

}

}

    }


delete g_pLog;

delete g_pCamera;

delete g_pFrustum;


    /// 등록된 클래스 소거

    UnregisterClass( "D3D Tutorial", wc.hInstance );

    return 0;

}





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

Direct X - 쿼드트리 컬링  (0) 2014.08.16
Direct X - 쿼드트리  (0) 2014.08.14
Direct X - 카메라  (2) 2014.08.04
Direct X - 지형 처리 기법  (0) 2014.07.31
Direct X - 스키닝  (0) 2014.07.30
Posted by 긍정왕오킹