멀티미디어 프로젝트 인천대학교 컴퓨터공학과 성미영 3차원 기초이론과 D3D의 최소한의 이해 멀티미디어 프로젝트 인천대학교 컴퓨터공학과 성미영
개요 Tut01_CreateDevice: 디바이스 Tut02_Vertices: 정점 Tut03_Matrices: 행렬 Tut04_Lights: 광원 Tut05_Textures: 텍스쳐 Tut06_Meshes: 메시 Tut07_IndexBuffer: 인덱스 버퍼
Win32 프로그램과 D3D 프로그램 RegisterClassEx()로 윈도우 클래스 등록 CreateWindow()로 윈도우 생성 InitD3D()에서 Direct3D 객체 생성 Render()에서 Direct3D 객체 사용 ShowWindow(), UpdateWindow()로 윈도우를 화면에 표시 GetMessage(), TranslateMessage(), DispatchMessage()로 구성된 메시지 루프 수행 메시지 루프에서 빠져나올 때 Direct3D 객체 해제 프로그램 종료
Direct 3D 프로그래밍의 흐름 Direct3D 객체 생성 Direct3D 객체 사용 Direct3D 객체 제거 g_pD3D=Direct3DCreate9() CreateDevice(…, &g_pd3dDevice) Direct3D 객체 사용 g_pd3dDevice->Clear() g_pd3dDevice->BeginScene() Rendering 내용 정의 g_pd3dDevice->EndScene() g_pd3dDevice->Present() Direct3D 객체 제거 g_pd3dDevice->Release(); g_pD3D->Release();
Tut01_CreateDevice: 디바이스 실행화면 함수 상관도
디바이스 화면에 직접 폴리곤을 그리는 역할을 하는 인터페이스 (…) 사용자 소프트웨어와 비디오 카드 사이의 의사소통 라인을 제공하는 객체 Direct3D 객체에서 생성되는 비디오 카드 접근 자료구조체 프리미티브가 렌더링되는 하나의 서피스(surface) = 프레임 버퍼(frame buffer)로의 연결점이다. 프레임 버퍼는 페이지 플립핑(page flipping; 전체 화면 모드에서 후면 버퍼의 내용을 전면 버퍼로 교환) 또는 블리팅(blitting; 윈도우 모드에서 비디오 데이터들을 비디오 메모리로 복사) 응용의 하나의 백 버퍼가 된다.
InitD3D() Direct3D를 초기화 하기 위한 함수이다. Direct3DCreate9()함수를 사용하여 D3D의 객체를 생성하고 인터페이스IDirect3D9를 리턴해준다. IDirect3D9->CreateDevice()함수를 호출 디바이스를 생성한다. 이 CreateDevice()함수는 중요한 함수이므로 좀더 자세히 알아보도록 하자.
CreateDevice() HRESULT CreateDevice( UNIT Adapter, D3DDEVTYPE Device Type, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface ); Adapter : 디바이스를 생성할 비디오 카드 장치의 순서 번호 DeviceType : 출력 디바이스의 종류 hFocusWindow : 디바이스가 출력할 윈도우의 핸들이다. BehaviorFlags : 하드웨어 가속을 사용할 것인지, 소프트웨어적으로 지원할 것인지를 결정하는 플래그 값 pPresentationParameters : 앞에서 선언한 구조체의 포인터다. ppReturndeDeviceInterface : 우리가 얻고자 했던 바로 그 값이다. iDirect3DDevice9 의 인터페이스를 갖고 있는 포인터가 담겨서 돌아온다.
d3dpp(D3DPRESENT_PARAMETERS) 작성 //GetAdapterDisplayMode()함수를 이용하여 DisplayMode정보를 받아옴 D3DDISPLAYMODE d3ddm; pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ); //작성할 구조체를 초기화 D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp) ); //true를 적용하여 윈도우 모드를 선택 d3dpp.Windowed = TRUE; //2차원 표면내용을 1차원표면으로 flip하는 방법을 적용시킴 //가장 효율적인 방법 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; //DisplayMode의 포멧방식을 적용시킨다. d3dpp.BackBufferFormat = d3ddm.Format;
DeviceType & BehaviorFlags HAL Device (Hardware abstraction layer) 하드웨어를 이용한 연출 (하드웨어를 가지고 그림) 렌더링 속도 빠름 플래그 : D3DDEVTYPE_HAL 세트 Reference Device 하드웨어와 소프트웨어를 이용한 연출 (하드웨어로 부족할 때 소프트웨어로 하드웨어를 emulate) 렌더링 속도 중간 플래그 : D3DDEVTYPE_REF Pluggable Software Device 소프트웨어를 이용한 연출 렌도링 속도 느림 플레그 : D3DDEVTYPE_SW 정점처리 (BehaviorFlags) D3DCREATE_HARDWARE_VERTEXPROCESSING 그래픽카드에서 사용할 수 있는 하드웨어 변환과 조명을 사용한다. D3DCREATE_SOFTWARE_VERTEXPROCESSING 소프트웨어 변형과 조명을 사용한다.
Render() Clear(): 먼저 화면을 깨끗이 지운다. BeginScene(): 이제부터 폴리곤을 그린다고 D3D에게 알린다. EndScene(): 폴리곤을 다 그렸다고 D3D에게 알린다. Present(): 화면에 나타내게 한다.
Render() Clear() BeginScene() EndScene() Present() 첫 번째 인자에 0을 설정하고, 두 번째 인자에 NULL을 설정 함으로써 rendering target을 덮는 하나의 사각형을 사용한다. 세 번째 인자는 버퍼(render target, 깊이(Z) 버퍼, 스텐실 버퍼)중 rendering target만을 사용한다. 나머지 세 개의 인자는 render target, 깊이(Z) 버퍼, 그리고 스텐실 버퍼를 위한 지울 값을 반영하기 위해 설정한다. 현재 render target만을 사용하였으므로 파란색으로 rendering target을 지웠으며 나머지 값은 무시된다. BeginScene() 3D배경에 어떤 폴리곤들을 실제로 출력하기 전에 먼저 BeginScene()함수를 사용해서 3D 랜더링 모드로 진입해야 함 EndScene() BeginScene() 함수를 호출하면 반드시 EndScene()함수를 호출해야 한다. 또한 이 작업을 거친 후에 출력된 최종 그래픽은 디바이스에 저장이 된다. Present() 렌더링된 장면을 화면에 display하는 작업 대부분의 경우에 NULL로 설정되어야만 한다 첫 번째 인자는 source 사각형이다. 두 번째 인자는 destination 사각형이다. 이 단계에 샘플 코드는 이 두 인자에 NULL을 설정함으로써 전체 백버퍼(준비버퍼)를 프론트버퍼(출력버퍼)로 present한다. 세 번째 인자는 이 presentation을 위한 destination 윈도우를 설정한다. 이 인자가 NULL로 설정되었기 때문에 D3DPRESENT_PARAMETERS 의 hWndDeviceWindow 맴버가 사용된다. 네 번째 인자는 DirtyRegion 인자이다.
페이지 플리핑 (Page Flipping) 깜빡임 현상(flickering)을 막기 위해 사용하는 방법이다. D3D에서 후면 버퍼의 내용을 전면 버퍼로 교환(버퍼 주소들의 교환)하는 방식이다.
Tut02_Vertices: 정점 실행화면 함수 상관도
InitVB() 정점버퍼를 생성하고 정점 값을 채워 넣는 함수이다. CreateVertexBuffer()함수를 사용하여 버퍼를 생성한다. FVF를 지정하여 보관할 데이터 형식을 지정한다. 생성한 다음 Lock()과 Unlock()으로 포인터를 얻어내 정점 정보를 써넣어야 한다. Lock()을 수행한 정점 버퍼는 반드시 Unlock()를 호출해야 한다.
CreateVertexBuffer() HRESULT CreateVertexBuffer( UINT Length, DWORD Length , DWORD Usage, D3DPOOL FVF, IDirect3DvertexBuffer9** ppVertexBuffer ); Length : 생성할 정점 버퍼의 바이트 단위 크기 Usage : 정점 버퍼의 종류 혹은 처리 방식지정 FVF : 정점 정보 구조체에 따라 선언된 FVF 플래그 값 Pool : 정점 버퍼가 저장될 메모리의 위치와 관리 방식 지정 ppVertexBuffer : 반환될 정점 버퍼의 인터페이스
Lock() HRESULT Lock( UINT OffsetToLock, UINT SizeToLock, BYTE ** ppbData, DWORD Flags ); OffsetToLock : Lock을 할 버퍼의 시작점,SizeToLock 과 함께 양쪽 모두 0이 면 버퍼 전체 SizeToLock : Lock을 할 버퍼의 크기,OffsetToLock 과 함께 양쪽 모두 0이면 버퍼 전체 ppbData : 읽고 쓸 수 있게 된 메모리 영역의 포인터 Flags : Lock을 수행할 때 함께 사용하는 플래그
Flexible Vertex Format, FVF RHW: 동차 좌표계의 W값, 이 값이 있으면 변환이 완료된 정점 RGBA: Red, Green, Blue, Alpha
FVF 플래그 값의 용도 정점의 좌표 : 정점의 3차원자표를 나타낸다. RHW : 동차 좌표계의 W값. 이값이 있으면 변환이 완료된 정점이다. 결합가중치 : 스키닝에 사용된다. 법선벡터 : 정점의 접선 벡터를 나타낸다. 주로 광원 처리시 사용된다. 확산광 : RGBA(r,g,b,a) 매크로 값이며, 정점의 확산광 색깔을 나타낸다. 반사광 : RGBA(r,g,b,a) 매크로 값이며, 정점의 반사광 색깔을 나타낸다. 텍스처 좌표 : 텍스처 좌표값을 나타낸다. D3D는 8개까지 텍스처를 동시에 겹쳐서 사용할 수 있다. 예를 들어 법선 맵핑을 하려면 첫번째 값을 확산 맵 정보로, 두번째 값을 법선 맵 정보로 설정하여 사용할 수 있다.
Render() 앞에서 본 예제와 다르게 Render()함수 중 BeginScene()과 EndScene()사이에 정점 버퍼의 내용이 추가된다. 1. SetStreamSource()로 출력할 정점 버퍼를 디바이스에 바인딩한다. 2. SetFVF()로 정점 포맷을 디바이스에 지정한다. 3. DrawPrimitive()로 정점 버퍼의 폴리곤을 그린다.
Render() DrawPrimitive()함수는 삼각형을 그려주는 함수이다. 첫번째 전달 인자에 의하여 다양한 동작을 한다. D3DPT_LINELIST D3DPT_POINTLIST D3DPT_LINESTRIP D3DPT_TRIANGLEUST
Render() D3DPT_TRIANGLESTRIP D3DPT_TRIANGLEFAN
Tut03_Matrices: 행렬 실행화면 함수 상관도
월드 변환(World Transform) 2개의 물체를 따로따로 출력하기 위해서는 물체가 사용하는 로컬 좌표계가 아닌 전체 월드 좌표계를 도입해야 한다. 로컬 좌표계 -> 월드 좌표계 모든 물체마다 TM(Transform Matrix)이 존재한다. TM을 적용하여 그린 경우
월드 변환 함수 SetTransform(D3DTS_WORLD, &matWorld)
카메라 변환(Camera Transform) 3차원 월드 좌표계를 카메라를 기준으로 한 카메라 좌표계로 변환하는 것을 말한다. 월드 좌표계에서 카메라 좌표계로의 변환
카메라 변환 함수 카메라 변환 행렬 계산 D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec); matView : 변환 행렬이 들어갈 행렬 구조체 vLookatPt : 카메라가 바라보는 위치의 월드 좌표 vUpVec : 카메라의 상방 벡터 행렬 작용 SetTransform(D3DTS_VIEW, &matView);
투영 변환(Projection Transform) 3차원 좌표계를 우리가 실제로 보게 되는 화면의 2차원 좌표계로 변환 하는 것이다. 카메라 좌표계에서 투영 좌표계로의 변환
투영 변환 함수 투영 변환 행렬 계산 D3DXMatrixPerspectiveFovLH(&matProj, fov, Sw/Sh, Zn, Zf); matProj : 변환 행렬이 들어갈 행렬 구조체 FOV : 시야 각 Sw/Sh : 종횡비 Zn : 근접 클리핑 평면(near clipping plane) Zf : 원거리 클리핑 평면(far clipping plane) 행렬작용 SetTransform(D3DTS_PROJECTION, &matProj);
렌더링 파이프라인(Rendering Pipeline) 최초의 로컬 좌표계가 최종 좌표계로 변환되는 과정이다. D3D의 렌더링 파이프라인
Tut04_Lights: 광원 실행화면 함수 상관도
광원 광원 : 암흑과 같은 3차원 공간에 빛이 들어감으로써 정점이 폴리곤으로 보일 수 있다. 재질값을 이루는 구성 요소 주변광(ambient light): 주변광이란 최적 평균 밝기를 말한다. 똑같은 양으로 모든 면에서 나오는 빛. 확산광(diffuse): 표면의 모든 점들에 균일하게 비춰지는 빛 반사광(specular): 특정한 방향으로만 반사하는 빛. 광원의 위치와 카메라에 위치에 따라서 달라진다. 방출광(emissive): 메시 표면에서 자체적으로 방출되는 빛. 이 빛이 다른 메시에 영향을 주지는 못한다. 광원의 종류 주변 광원(ambient light): 똑같은 양으로 모든 곳을 비추는 빛이다. 점 광원(point light): 백열전구와 같은 빛이다. 방향성 광원(directional light): 하나의 방향을 갖는 태양과 같은 빛이다. 점적 광원(spot light): 정해진 위치와 범위만 비추는 특수한 조명이다.
광원의 설정 D3DLIGHT9 구조체에 적당한 값을 넣고 SetLight()함수를 통하여 광원을 설치한 다음 LightEnable()함수를 통하여 광원을 켠다. D3D로 하여금 광원 연산을 하도록 전체 메인스위치(SetRenderState(D3DRS_LIGHTING, TRUE))를 켜야 한다.
Tut05_Textures: 텍스쳐 실행화면 함수 상관도
텍스쳐 텍스쳐 : 3차원 메시에 2차원 이미지를 덧붙여서 좀 더 현실감 있는 장면을 연출해준다. 텍스쳐의 종류 1차원 2차원 3차원
텍스쳐 좌표계 텍스쳐 좌표계 : 텍스쳐를 제어하기 위한 텍스쳐만의 전용 좌표계 1차원 2차원 3차원
정점, nomal, 텍스쳐 좌표 입력방법 정점은 일반 X축과 Y축을 중심으로 뒤쪽이 Z의 증가이며 앞쪽이 Z의 감소이다 normal은 점의 normal벡터로서 삼각형의 수직인 방향을 나타낸다. 앞의 코드는 정면을 바라보고 있으므로 Z축의 음(-)의 방향이 normal벡터이다 텍스쳐 좌표는 사각형을 그렸을 경우 다음과 같다 텍스쳐 좌표를 다음과 같이 하였을 경우 네 개의 그림이 매핑된다. 0, 0 1, 0 0, 1 1, 1 0, 0 2, 0 0, 2 2, 2
텍스쳐 사용 1. IDirect3DTexture9 인터페이스 선언 2. 텍스쳐 좌표를 갖는 정점 선언 3. 텍스쳐 생성 4. 텍스쳐 스테이지 설정 5. 그려질 텍스쳐 지정 6. 메시 그리기
Tut06_Meshes: 메시 실행화면 함수 상관도
메시 X파일은 DirectX에서 지원하는 기본적은 3차원 메시 파일 포맷이다. 실제로 게임을 제작할 때는 프로그래머는 디자이너들이 제작한 3차원 메시파일을 읽어들여 출력해야 한다. X파일 추출기를 이용하여 메시값을 추출해낸다. 추출된 X파일은 IDirect3DXMesh를 통해서 완벽하게 제어가 가능하다. D3DXLoadMeshFromX()함수를 사용한다.
Tut07_IndexBuffer: 인덱스 버퍼 실행화면
인덱스 버퍼 인덱스 버퍼란 정점의 인덱스를 보관하기 위한 전용 버퍼를 말한다. 메모리 소모량이 적고 캐시를 이용해 높은 효율을 낼 수 있다. 캐시를 통한 최적화는 인덱스 버퍼를 사용할 때만 가능한 것으로 통상 출력 속도가 두 배 정도 빨라진다. 인덱스 버퍼는 IDirect3DIndexBuffer9인터페이스를 사용한다. SetFVF()함수로 정점 버퍼의 정보를 D3D에 전달한다. SetStreamSource()함수로 정점 버퍼사용할 준비를 한다. SetIndices()함수를 사용하여 인덱스 버퍼를 사용할 준비를한다. 기존에 사용했던 DrawPrimitive()함수가 아닌 DrwaIndexedPrimitive()함수를 사용하여 정점 버퍼 + 인덱스 버퍼로 폴리곤을 출력한다.
정점과 인덱스의 관계