텍스쳐
텍스쳐는 쉽게말해 단조로운 폴리곤에 옷을 입히는것
우리 아니지 내가 여태까지 재미없게 무지개색 삼각형이나 검은색 원통을 띄운게 다지만, 이젠 아닙니다
텍스쳐 좌표계
텍스쳐에는 텍스쳐를 제어하기위해서 텍스쳐만의 전용 좌표계가 존재한다
그게 2차원 u,v 좌표계다(s,t좌표계도 있는데, 그건 OpenGL에서 쓰는것이다)
u, v 좌표계는 C#으로 3D할 때 했었다, 텍스쳐 부분을 참고하세요
모든 텍스쳐 좌표계는 기본적으로 0.0 ~ 1.0 사이의 실수값을 갖게되고, 이 값의 범위를 넘어서는 경우에 대해서 특별한 처리를 해 줄 수있다.
텍스쳐를 사용하기 위한 준비
1. IDirect3DTexture9 인터페이스 선언
2. 텍스쳐 좌표를 갖는 정점 선언
3. 텍스쳐 생성(일반적으로 파일을 로드하여 생성하지만 라이트 맵핑등의 기법에서는 텍스쳐를 직접 생성하기도 한다)
4. 텍스쳐 스테이지 설정
5. 그려질 텍스쳐 지정
6. 메시 그리기
인터페이스를 선언하고, 정점도 다르게 선언해야한다, 텍스쳐를 읽어들이고, 사용하는 방법도 상당히 많은 전처리를 요구한다, 하지만 단순한 폴리곤과 정점으로만은 표현해낼 수 없는것이고 필수적인 요소이기 때문에 꼭 해야한다
/// Direct3D9를 사용하기 위한 헤더
#include <d3dx9.h>
#include <Windows.h>
#include <mmsystem.h> /// TimeGetTime() 함수 사용하기 위한 헤더
/**-----------------------------------------------------------------------------
* 전역변수
*------------------------------------------------------------------------------
*/
#define SHOW_HOW_TO_USE_TCI
이것을 디파인 했느냐 하지 않았느냐에 따라 출력결과가 차이난다.
반사 맵핑이라는 기법인데 일단 봅시다
LPDIRECT3D9 g_pD3D = NULL; /// D3D 디바이스를 생성할 D3D객체변수
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; /// 렌더링에 사용될 D3D디바이스
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; /// 정점을 보관할 정점 버퍼
LPDIRECT3DTEXTURE9 g_pTexture = NULL; /// 텍스쳐 정보
1. 텍스쳐 정보를 넣을 인터페이스 객체를 생성한다
/**-----------------------------------------------------------------------------
* 이 프로그램의 시작점
*------------------------------------------------------------------------------
*/
/// 사용자 정점을 정의할 구조체
/// 텍스쳐 좌표가 추가된걸 알 수 있다
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; /// 정점의 3차원 좌표
D3DCOLOR color; /// 색깔
#ifdef SHOW_HOW_TO_USE_TCI
FLOAT tu,tv; /// 텍스쳐 좌표
#endif
};
2. 텍스쳐 좌표를 갖는 정점을 선언한다.
/// 사용자 정점 구조체에 관한 정보를 나타내는 FVF 값
#ifdef SHOW_HOW_TO_USE_TCI
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
#else
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
#endif
/**-----------------------------------------------------------------------------
* 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);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
/// Z버퍼를 켠다
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
return S_OK;
}
HRESULT InitGeometry()
{
if(FAILED(D3DXCreateTextureFromFile(g_pd3dDevice, "banana.bmp", &g_pTexture)))
3. 텍스쳐 생성
{
/// 현재폴더에 없으면 상위폴더 검색
if(FAILED(D3DXCreateTextureFromFile(g_pd3dDevice, "..\\banana.bmp", &g_pTexture)))
{
MessageBox(NULL, "banana.bmp가 없어요^^", "^^", MB_OK);
return E_FAIL;
}
}
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].color = 0xffffffff;
#ifdef SHOW_HOW_TO_USE_TCI /// SHOW_HOW_TO..가 선언되어있으면 텍스쳐좌표를 생성하지 않는다
pVertices[2*i+0].tu = ((FLOAT)i)/(50-1); /// 텍스쳐의 u좌표 0/49, 1/49, 2/49... 49/49 (즉 0.0~1.0)
pVertices[2*i+0].tv = 1.0f; /// 텍스쳐의 v좌표 1.0
#endif
pVertices[2*i+1].position = D3DXVECTOR3(sinf(theta), 1.0f, cosf(theta));
pVertices[2*i+1].color = 0xff808080;
#ifdef SHOW_HOW_TO_USE_TCI
pVertices[2*i+1].tu = ((FLOAT)i)/(50-1);/// 텍스쳐의 u좌표 0/49, 1/49, 2/49... 49/49 (즉 0.0~1.0)
pVertices[2*i+1].tv = 0.0f; /// 텍스쳐의 v좌표 0.0
#endif
}
g_pVB->Unlock();
return S_OK;
}
/**-----------------------------------------------------------------------------
* 초기화된 객체들을 소거한다.
*------------------------------------------------------------------------------
*/
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 Render()
{
if( NULL == g_pd3dDevice )
return;
/// 후면버퍼와 Z버퍼를 지운다
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
/// 렌더링 시작
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
/// 월드, 뷰, 프로젝션 행렬을 설정한다
SetupMatrices();
/// 생성한 텍스쳐를 0번 텍스쳐 스테이지에 올린다
/// 텍스쳐 스테이지는 여러 장의 텍스쳐와 색깔 정보를 섞어서 출력할 때 사용한다
/// 여기선 텍스쳐의 색깔과 정점의 색깔 정보를 modulate 연산으로 섞어서 출력한다
g_pd3dDevice->SetTexture(0, g_pTexture); /// 0번 텍스쳐 스테이지에 텍스쳐 고정
5. 그려질 텍스쳐 지정
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); /// modulate 연산으로 색을 섞는다
4. 텍스쳐 스테이지 설정
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); /// 첫번째 섞을 색은 텍스쳐 색
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); /// 두번째 섞을 색은 정점의 색
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); /// 알파연산 사용하지 않음
#ifdef SHOW_HOW_TO_USE_TCI
D3D의 텍스쳐 좌표 생성 기능을 사용하는 예를 보여준다
여기서는 카메라 좌표계에서의 정점 정보를 사용하여 텍스쳐 좌표를 생성한다
4x4 크기의 텍스쳐 변환 행렬을 텍스쳐 좌표 인덱스(TCI = Texture Coord Index) 전달 인자를 사용하여 x,y,z TCI 좌표를 u, v텍스쳐 좌표로 변환한다
사용한 것은 단순히 (-1.0 ~ +1.0)값을 (0.0~1.0) 사이의 값으로 변환하는 행렬이다
월드, 뷰, 프로젝션 변환을 거친 정점은 (-1.0 ~ +1.0) 사이의 값을 갖게된다
// tu = 0.5*x+0.5
// tv = -0.5*y+0.5
D3DXMATRIXA16 mat;
mat._11 = 0.25f; mat._12 = 0.00f; mat._13 = 0.00f; mat._14 = 0.00f;
mat._21 = 0.00f; mat._22 = -0.25f; mat._23 = 0.00f; mat._24 = 0.00f;
mat._31 = 0.00f; mat._32 = 0.00f; mat._33 = 1.00f; mat._34 = 0.00f;
mat._41 = 0.50f; mat._42 = 0.50f; mat._43 = 0.00f; mat._44 = 1.00f;
g_pd3dDevice->SetTransform(D3DTS_TEXTURE0, &mat);
/// 텍스쳐 변환행렬
g_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
/// 2차원 텍스쳐 사용
g_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
/// 카메라 좌표계 변환
#endif
/// 정점버퍼의 내용을 그린다
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2*50-2);
6. 메시 그리기
/// 렌더링 종료
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",
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;
}
SHOW_HOW_TO_USE_TCI 디파인 안한 상태, 노반사맵핑
SHOW_HOW_TO_USE_TCI 디파인 한 상태, 반사맵핑
반사맵핑이 낫긴한데 좀 코드가 어렵다
'Graphics > DirectX' 카테고리의 다른 글
Direct 3D - *인덱스 버퍼 (0) | 2014.06.19 |
---|---|
Direct 3D - *메시 (0) | 2014.06.19 |
Direct 3D - *광원 (0) | 2014.06.19 |
Direct X - *행렬 (0) | 2014.06.19 |
Direct X - *정점 (0) | 2014.06.19 |