DirectX에서 3D공간을 표시하기위해서 행렬들을 이용 , 변환해서 화면으로 출력한 것처럼

화면에서 클릭했을때 어떤것을 클릭했는지 확인하기 위해서는 화면의 x,y좌표를 변환해야 한다.

이번엔 화면의 x, y좌표를 이용해서

1) 클릭한 곳과 구가 충돌하는지

2) 클릭한 곳이 충돌한 부분에서 어느 좌표인지

이 두가지를 확인해 보자.


먼저 "구"라는 물체를 구조체로 정의해보자.

vCenter : 구의 중점 (위치)

fRadius : 구의 반지름 (크기)

isPicked : 선택됬는지 안됬는지 검사

밑의 ST_SPHERE()는 생성했을때 바로 저 값으로 초기화해준다.

클래스 생성자에서 이니셜 라이즈하는거랑 같다고 생각하면 된다.

-> ST_SPHERE를 생성하고 아무값도 안넣어주면 fRadius에 0.0, vCenter에 0,0,0, isPicked에 false값이 들어가있음


다음은 광선(마우스로 클릭한 좌표에서 -> 클릭한 물체로 향하는)에 대한 정의이다.

m_vOriginal : 광선의 출발 지점 (마우스로 클릭한 좌표)

m_vDirection : 광선의 방향 (화면 안쪽으로 향하게 됨)

m_eRaySpace : 종류를 나타냄, 궂이 없어도 된다.


다음은 마우스 왼쪽클릭시 실행되는 코드이다. 어떤 흐름인지 보자

cRay r : 현재 클릭한 마우스의 x좌표 (LOWORD(lParam), y좌표 (HIWORD(lParam))을 이용해 계산에 사용할 광선을 구해온다.

for반복문 : 모든 구들을 확인한다.

r.IsPicked : 구와 현재 광선이 충돌하는지 확인하고 충돌하면 true 충돌하지 않으면 false를 반환한다.

1) : 광선을 구하고

2) : 모든 구에

3) : 광선이 충돌하는지 검사하고 값을 바꿈

이와같은 순서로 흘러가고 있다.


그러면 어떻게 구해지는지 살펴보자.

RayAtWorldSpace를 구하기 위해서 ViewSpace함수가 필요하기 때문에 처음인 ViewSpace부터 시작한다.


RayAtViewSpace함수

1) d3dDevice에서 뷰포트를 얻어온뒤 저장한다.

2) d3dDevice에서 투영행렬(Projection)을 얻어온뒤 저장한다.

3) 계산에 필요한 방향벡터 (m_vDirection)를 만든다.

4) X,Y좌표에 뷰포트 , 투영행렬을 이용해 계산한다.

4-1)    X = ((+2.0f * 마우스X좌표) / 뷰포트 넓이 - 1.0f) / 투영행렬중 1행 1열 (만약 배열, 벡터로 행렬이 되어있다면 MatProjection[0][0]에 해당한다)

         ->이는 투영행렬의 1행 1열이 X값이 변하는데 영향을 주기 때문이다.

4-2)    Y = ((-2.0f * 마우스Y좌표) / 뷰포트 높이 + 1.0f) / 투영행렬중 2행 2열 (배열 , 벡터라면 [1][1]에 해당)

         ->투영행렬의 2행 2열이 Y값 변화에 영향을 줌

4-3)    Z = 1.0f

         투영 윈도우 값이다. DirectX는 평면 z = 1 과 일치하도록 정의하고 있다.

RayAtWorldSpace함수

1) r에 뷰포트, 투영행렬을 이용해 변환한 방향벡터를 얻어온다.

2) 행렬을 담기위한 변수 2개를 생성한다 (matView, matInvView)

3) d3dDevice를 이용해 matView에 현재 View행렬을 담는다.

4) matInvView행렬에 matView의 역행렬을 담는다.

5) 뷰의 역행렬을 이용해서 광선의 출발점을 변환한다.

6) 뷰의 역행렬을 이용해서 방향벡터를 변환한다.

7) 방향벡터를 정규화(행렬안의 모든값이 1보다 작도록)한다.


여기서 왜 View의 역행렬을 구하는건가 ?

렌더링 파이프라인을 한번 살펴보자

여기에서 실제로 좌표에 영향을 주는 부분은

1 : 로컬 스페이스 -> 2 : 월드 스페이스 -> 3 : 뷰 스페이스 -> 4 : 투영 -> 5 : 뷰포트 이다.

뷰포트 , 투영과 관련된 부분은 이전함수에서 처리했지만 뷰 스페이스와 관련된 부분은 아직 처리되지 않았다.

우리는 현재 뷰스페이스가 적용되기 전의 좌표가 필요하다.

그래서 뷰스페이스의 역행렬을 이용해서 좌표를 변환하는 것이다.


지금까지의 과정을 모두 거치면 실제 3D좌표 내에서 클릭한 점 (벡터)가리키는 방향 (벡터)를 둘다 구한 것이다.

이제 구와 충돌을 어떻게 처리하는지 확인해보자.

r.IsPicked : 광선 R을 이용해서 구와 충돌하는지 확인하겠다는 뜻이다.

1) cRay r = (*this) r이라는 변수를 만들고 거기에 자신의 값을 집어넣는다.

여기에서는 자기와 같은 광선 r을 한게 더 만든거다.

2) matInvWorld : 월드의 역행렬

2-1)    역행렬의 4행 1열에 구의 -x좌표를 넣는다

2-2)    역행렬의 4행 2열에 구의 -y좌표를 넣는다

2-3)    역행렬의 4행 3열에 구의 -z좌표를 넣는다

3) 월드의 역행렬을 이용해 카메라의 좌표를 변환한다.

4) 월드의 역행렬을 이용해 방향벡터를 변환한다.

5) vv는 방향벡터와 방향벡터를 내적한 값이다.

6) qv는 위치좌표와 방향벡터를 내적한 값이다.

7) qq는 위치좌표와 위치좌표를 내적한 값이다.

8) rr은 구의 반지름 * 반지름한 값이다.

9) qv * qv - vv * (qq - rr) >= 0;을 반환한다.

9-1)    0이라면 1개의 점이 교차, 0보다 크다면 2개의 점이 교차, 0보다 작다면 교차하지 않는다.

사실 여기 너무 어려워서 나도잘 모르겠음 ;;; 좀더 찾아봐야 겠다


다음은 세개의 점 (면)과 선이 충돌하는지 확인하는 부분이다.

사용될 함수는 D3DXIntersectTri 이다.

D3DXintersectTri함수는 충돌했다면 True, 충돌하지 않았다면 false값을 반환한다.

여기서 충돌된 지점을 구하는 부분이 중요하다

1번째 식 : 광선의 출발점 + t(출발점에서 충돌된 지점까지의 거리) * 광선의 방향

2번째 식 : v0 + (u * (v1 - v0)) + (v * (v2 - v0));

-> 충돌에 사용된 3개의 점을 이용해서 구한다. v0의 점에서 v1방향으로 u만큼 , v2방향으로 v만큼 움직여서 구한다.

이렇게 하면 충돌된 여부 뿐만 아니라 충돌된 좌표가 어디인지 구해낼 수 있다.

여러 책들은 번거롭게 일일히 점을 입력해서 배열방식으로 삽입하는데

이 글에서는 이미 벡터에 점과 나머지 정보가 담겨있는 상태에서 메쉬에 집어 넣어보자.


시작하기전 LPD3DXMESH == ID3DXMesh* 이다. 이는 정의에서도 확인할 수 있다.


1. 모든 점(Point)들은 std::vector<ST_PNT_VERTEX> vecTotalVertex에 담겨있다.

->ST_PNT_VERTEX는 구조체로 D3DXVECTOR3 p (점), D3DXVECTOR3 n (법선벡터), D3DXVECTOR2 t (텍스쳐)의 정보를 가지고 있다.

ST_PNT_VERTEX::FVF == D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 이다.

이와 비슷한 구조체를 여러분들은 이미 만들어서 사용중일거다.

ST_PNT_VERTEX대신에 여러분들이 사용중인 구조체의 이름을 적어주면 된다.

2. 모든 인덱스(Index)는 std::vector<WORD> vecIndex에 담겨있다.

3. 모든 속성(Attribute)은 std::vector<DWORD> vecAttribute에 담겨있다.


자 그럼 이제 MESH를 생성해보자.


LPD3DXMESH pMesh = NULL;


D3DXCreateMeshFVF(vecTotalVertex.size() / 3,    Mesh가 가지고 있는 모든 면의 개수, 현재 여기서는 삼각형으로 모든 면이 구현되기 때문에 이렇게 계산

vecTotalVertex.size(),        Mesh가 가지고 있는 모든 점의 개수

D3DXMESH_MANAGED,    생성 옵션, 관리 메모리 풀 내에 보관되도록 설정했다.

ST_PNT_VERTEX::FVF,        구조체의 FVF값, 현재 여기서는 D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 값이 들어가고 있다.

g_pD3DDevice,                DirectX Device를 넣어주면 된다. 여기서는 싱글톤으로 관리되는 디바이스를 넣어줬다.

&pMesh);                        방금 생성한 Mesh의 주소를 넣어주면 된다.


현재 모든 데이터가 파일에서 파싱되어 벡터에 저장되어 있기 때문에 벡터의 정보를 그대로 메쉬에 넣어보자


ST_PNT_VERTEX* vertex;                                            다른 구조체 이름으로 정의되었다면 해당 구조체 포인터를 만든다.

pMesh->LockVertexBuffer(0, (void**)&vertex);                Mesh에 점을 복사하기 위해서 메모리를 잠근다.

memcpy(vertex, &vecTotalVertex[0], vecTotalVertex.size() * sizeof(ST_PNT_VERTEX));

메모리에 벡터의 처음부터 끝까지 복사해서 집어 넣는다. ST_PNT_VERTEX대신에 여러분들의 구조체 이름을 적으면 된다.

pMesh->UnlockVertexBuffer();                                    점을 다 입력했다면 무조건 잠금을 해제한다.


WORD* index = 0;                                                          인덱스 버퍼에 접근하기 위한 WORD포인터

pMesh->LockIndexBuffer(0, (void**)&index);                         인덱스 버퍼를 잠근다.

memcpy(index, &vecIndex[0], vecIndex.size() * sizeof(WORD));   메모리에 벡터의 처음부터 끝까지 복사해서 집어넣는다. 인덱스는 무조건 WORD이다.

pMesh->UnlockIndexBuffer();                                            인덱스를 다 입력했다면 무조건 잠금을 해제한다.


DWORD* attributeBuffer = 0;                                속성버퍼에 접근하기위한 DWORD포인터

pMesh->LockAttributeBuffer(0, &attributeBuffer);      속성버퍼를 잠근다.

memcpy(attributeBuffer, &vecAttribute[0], vecAttribute.size() * sizeof(DWORD)); 

       메모리에 벡터의 처음부터 끝까지 복사해서 집어넣는다. 속성 인덱스는 무조건 DWORD 이다.

pMesh->UnlockAttributeBuffer                             속성을 다 입력했다면 무조건 잠금을 해제한다.


이와 같은 과정을 거치면 벡터에 저장된 모든 데이터를 Mesh에 삽입할 수 있다.

3D MAX로 만든 데이터를 DirectX로 그리기 위해서는 여러가지 과정이 필요하다.

그중 이번에는 ASE Animation 데이터를 읽어보자.

애니메이션의 종류는 여러가지인데 그 중 지금하는건 키프레임 방식의 애니메이션이다.


정점부터 애니메이션까지 모든 정보를 가진 상태를 기준으로 진행된다.(파싱이 완료된 상황)


위의 사진과 같이 업데이트를 돌게된다.

nKeyFrame은 현재 프레임을 말한다. (ex: 시작 프레임이 4 , 마지막 프레임이 20이라면 4 ~ 20 사이의 값)

pMatParent는 부모 행렬을 가리킨다. (단 Root는 부모 행렬이 없다.)


Scale -> Rotate -> Translate (크기 , 회전 , 이동)의 과정중 현재 회전 , 이동만 처리되어있다.


1) CalcLocalR을 통해 Rotate행렬을 구한다.

2) CalcLocalT를 통해 Translate 행렬을 구한다.

3) 부모가 없다면 Rotate * Translate 행렬을 곱해 자신의 로컬 행렬을 구하고

부모가 있다면 Rotate * Translate 한 행렬에 부모 행렬을 곱해준다.

4)그다음 노드(자신)가 소유한 자식들에게 현재 가지고 있는 월드 매트릭스(행렬)를 보내준다.


먼저 위의 사진과 같이 업데이트를 하기 위해서는 시간이 일단 흘러가야한다.

시간을 어떻게 증가시키는지 확인해보자.


main.cpp에서 업데이트를 호출할땐 Root에서 nKeyFrame을 구하고 있다. 이함수를 통해 시간이 늘어나는것을 확인하고 있다.


다음 사진은 시간을 구하는 함수와 ASE파일에서 SCENE부분이다. 내용을 먼저 보자.


ASE파일에서 FIRSTFRAME은 4이다. 즉 0 ~ 4 사이의 프레임에 대한 정보가 없다.

LASTFRAME이 20이기 때문에 지금 이 애니메이션 4 ~ 20프레임 까지의 애니메이션 정보밖에 없다.

4보다 작은 프레임 , 20보다 큰 프레임에 대한 정보가 없다는 점에 주의해야 한다.

이제 GetKeyFrame함수를 보자.

GetTickCount는 컴퓨터(윈도우)가 켜진뒤 값이 계속 증가하는데 이 값을 얻어오는 함수이다.


1.First에 첫 프레임값 (4)과 틱에 따른 프레임 증가값 (160)을 곱해서 담아준다.

2.Last에 마지막 프레임 값(16)과 틱에 따른 프레임 증가값 (160)을 곱해서 담아준다.

3.GetTickCount를 하게되면 일단 컴퓨터로부터 어떤 값이 넘어오게 된다.

이 값을 (최대프레임 - 최소프레임) 차를 이용해 나머지 연산을 하게되면 프레임 차이인 16보다 작은 값이 나오게 된다.

4.하지만 지금 애니메이션 정보가 4 ~ 20 까지 있기 때문에 최소값인 4를 더해준다.

이렇게 최소값을 더해주게 되면 4 ~ 20 사이의 값이 저 함수를 통해 나오게 된다.


GetTickCount를 하게되면 컴퓨터가 켜져있는 이상 값이 계속 증가하기 때문에 시간이 증가하게 된다.

단 이함수는 행동이 이어지는것 뿐이지 처음부터 애니메이션을 하지는 않기 때문에

애니메이션이 처음부터 나오게 하길 원한다면 기준이 될 시간값 (여기서는 GetTickCount)을 대체할 다른 변수가 필요하다.


이함수를 통해서 시간이 지남에 따라 프레임이 증가하는 효과를 낼수 있다.

이제 시간은 흘러가게 됬으니 Rotate함수와 Translate함수를 보자.

Rotate 행렬을 구하는 함수를 먼저 보자.

1) matR이 초기화가 안되있기 때문에 항등행렬(Identity)로 초기화 해준다.

m_vecRotTrack은 ASE파일에서 읽어온 ROT_TRACK 데이터를 담아둔 곳이다.

2) 만약 아무 정보도 없다면 자신의 좌표인 LocalTM으로 초기화 해준다.


3)프레임이 RotTrack에 맨처음에 담긴 n보다 작거나 같다면 Rotation행렬을 RotTrack[0]에 담긴 쿼터니언 값만큼 회전한뒤 함수를 끝낸다.

-> 0 ~ 4 프레임까지의 정보가 없기 때문에 nKeyFrame이 4보다 작은 값이 들어오면 최소프레임인 4의 데이터를 넣어준다.

4)프레임이 RotTrack에 맨마지막에 담긴 n보다 크거나 같다면 RotTrack[Size - 1]에 담긴 쿼터니언 값만큼 회전한뒤 함수를 끝내버린다.

-> 20프레임 까지의 데이터밖에 없기 때문에 nKeyFrame이 20보다 큰값이 들어온다면 마지막 값이 20의 데이터를 넣어준다.


5) 만약 여기까지 왔는데도 아무 조건에 해당하지 않는다면 이제 RotTrack에 담긴 모든값들을 확인한다

-> 이 과정은 현재 프레임이 어디인지를 찾는 과정이다.

6) 이렇게 해서 현재 프레임을 찾은뒤 t를 구하게 된다 (t는 이전 프레임 , 다음 프레임 에서 어느정도만큼 움직였는지 확인하는 거다)

EX : 1프레임일때 X좌표가 0, 2프레임일때 X좌표가 10, 지금 X좌표가 5라면 현재 내 t값은 0.5다. (0 ~ 10 에서 5는 절반만큼 왔으니까

비율로 나타낸다면 0 ~ 1 의 절반인 0.5로 나타내 진것)


7) 이런식으로 이전 프레임 , 다음 프레임 사이에서 내가 얼마나 움직였는지 t값을 구하고 그값만큼 쿼터니언 함수를 이용해서 행렬을 이동한다.

->쿼터니언을 이용해서 회전하지 않으면 짐벌락이라는 문제가 생길수 있기 때문에 쿼터니언을 이용한다.


이단계를 마무리 하면 Rotation행렬이 모두 구해진다.

이제 Translate행렬을 구해보자.

1) 먼저 MatT를 항등행렬(Identity)로 초기화 해준다.

2) 만약 POS_TRACK의 값이 없다면 자신의 Local좌표 (LocalTM의 4행 1열 , 4행 2열, 4행 3열의값 코드기준시 -1씩)를 넣어준다.

3) 현재 프레임이 맨처음 프레임보다 적거나 같다면 x,y,z를 맨처음 프레임 애니메이션 값으로 초기화한다.

   ->0 ~ 4프레임 까지의 정보가 없기 때문에 nKeyFrame이 최소프레임보다 적게 들어온 상황을 위한 예외처리

4) 현재 프레임이 맨 마지막 프레임보다 크거나 같다면 x,y,z를 마지막 프레임 애니메이션 값으로 초기화한다.

   ->20프레임 이후의 정보가 없기때문에 nKeyFrame이 20프레임보다 크게 들어온 상황을 위한 예외처리


5) 두 예외상황이 모두 아니라면 이제 현재 프레임이 어디에 있는지 벡터를 모두 돌면서 찾는다.

벡터를 모두 돌아서 현재 프레임이 어느 프레임 사이에 있는지 찾아냈다면

6) 이전 프레임 , 다음 프레임 사이에서 어느정도에 위치하고 있는지 T값을 찾는다.

7) 그다음 출발점 [nPrevIndex]에서 T값만큼 쿼터니언 회전을 해서 위치를 찾아낸다.

그뒤 그위치를 적용시킨다.


이렇게 R , T를 적용시킨뒤 부모 행렬이 만약 존재한다면 부모행렬을 곱해주면 된다. (main의 cpp에서 처리되고 있다)

이와같은 흐름을 통해 애니메이션을 재생할 수 있다.

다이렉트 X9를 공부하기위해 책에서 설치 방법을 따라하던 도중

너무 예전에 만들어진책 (용책)이라 다른 부분이 많았다.

요즘 환경에 맞는 DirectX 9.0 SDK를 윈도우 10환경에서 설치해 보자.


준비물 : DirectX SDK , VisualStudio 2015 또는 VisualStudio2017 (C++  포함 !!)

DirectX SDK 다운로드 주소 : https://www.microsoft.com/en-us/download/details.aspx?id=6812

최신 VisualStudio Community(무료버전) : https://www.visualstudio.com/ko/thank-you-downloading-visual-studio/?sku=Community&rel=15

만약 2015, 2017버전 그대로가 필요하다면 첨부된 파일중 골라서 설치하자.

VS2017.7z

VS2015.7z

마이크로소프트 다운로드 센터에서도 가능하지만 로그인, Dev Essential에 가입해야하는 등 번거롭다.



1) DirectX SDK 설치

1-1) S1203오류 해결

2) SDK Path(경로) 설정

3) 라이브러리 연결

4) 설치가 제대로 됬는지 확인



링크를 통해 들어간 곳에서 파일을 다운로드 한다.


다운로드 폴더를 확인하면 다음 그림과 같은 아이콘이 있을거다. 더블클릭으로 실행한다.



다음과 같은 창이 나타난다. 항상 그랬듯이 전부 다음을 눌러주면 된다.



다음을 넘기다 보면 해당창이 뜨면서 골라야 하는데

내가 쓰는동안 문제가 생기거나 했을때 마이크로소프트에게 정보를 보내겠냐고 확인하는 부분이다.

Yes에 권장한다고 적혀있으니 Yes에 체크하고 다음으로 넘기자



이제 설치 경로를 정하게 되는데 저 경로는 잠시후에 다시 써야한다. 가급적이면 지금 미리 한번 봐두자 

다음을 누른다.



까는김에 X표시 된부분도 설치하자. (X옆의 화살표를 누른뒤 This feature, and all subfeatures를 클릭한다)



자 그러면 이제 설치가 시작된다. 여기서 문제없이 끝난다면 바로 2단계로 넘어가면 된다.


1) DirectX SDK 설치

1-1) S1203오류 해결

2) SDK Path(경로) 설정

3) 라이브러리 연결

4) 설치가 제대로 됬는지 확인


하지만 대부분의 컴퓨터라면 다음과 같은 오류가 발생한다.


빠르게 설명한다면 이미 깔려있는 파일과 충돌해서 일어난다.

우리는 디버그 버전을 사용해야 하는데 일반사용자에게 배포되는 런타임 버전이 설치되있어서 그렇다.

지금 깔려있는 버전을 지워야한다.

지우러 가보자.



Microsoft Visual C++ 2010 x64 , x84 모두 지워준다. (4개일수도 있는데 이때도 전부 지워준다.)




그후 다시 재설치 하면 다음과 같은 화면을 볼 수 있다.


1) DirectX SDK 설치

1-1) S1203오류 해결

2) SDK Path(경로) 설정

3) 라이브러리 연결

4) 설치가 제대로 됬는지 확인


자 그럼 이제 비주얼 스튜디오와 연동해보자.



지금은 설치를 위해 아무거나 하나 만들었지만

자신이 사용할 프로젝트를 하나 만들어야 한다. Win32 프로젝트를 선택하고 자기가 원하는 프로젝트 이름을 정해주자.



화면이 나온다면 프로젝트를 오른쪽 클릭한뒤 속성을 누른다.


구성 속성 -> VC++ 디렉터리를 클릭한다.




포함 디렉터리 옆에 글이 있는부분을 한번 클릭하면 오른쪽에 화살표가 나타난다.

화살표를 누른뒤 밑의 편집을 다시한번 눌러주자.




사진에 나온 순서대로 버튼을 눌러주자.




아까 설치된 경로를 따라가서 Lib폴더 저부분을 선택하고 폴더 선택 버튼을 눌러야 한다.

설치과정에서 따로 설정을 안했다면 

C드라이브 -> Program Files(x86) -> Microsoft DirectX SDk(June 2010) -> Lib 폴더이다.

대부분 예제가 32비트기준이기 때문에 x86폴더를 선택한다.


이렇게 됬다면 이제 경로설정이 완료되었다.


1) DirectX SDK 설치

1-1) S1203오류 해결

2) SDK Path(경로) 설정

3) 라이브러리 연결

4) 설치가 제대로 됬는지 확인


아쉽게도 바로 연결했다고 쓸수 있지는 않다.

이제 어떤 라이브러리를 꺼내서 쓸건지 설정해보자



위에 있는 프로젝트를 누르고 속성을 누른다.



왼쪽의 링커를 누르고 입력탭을 클릭하자. 그러면 사진과 같은 화면을 볼수 있다.

DirectX 9.0을 공부할 것이기 때문에 우리가 사용할 라이브러리는 d3d9.lib , d3dx9.lib , winmm.lib 이렇게 총 3가지 이다.



추가 종속성 옆을 한번 클릭한다.

그러면 1번 화살표가 보인다. -> 1번 화살표를 클릭한다.

그뒤 2번 (편집)을 누른다.




추가 종속성 부분에 우리가 사용할 d3d9.lib , d3dx9.lib , winmm.lib 이 3개를 써주자

적을때 한개 적고 세미콜론 (;) 을 꼭 써주자.

이 모든 과정을 거쳤다면 프로젝트에 DirectX 9 설정은 끝났다.


1) DirectX SDK 설치

1-1) S1203오류 해결

2) SDK Path(경로) 설정

3) 라이브러리 연결

4) 설치가 제대로 됬는지 확인


32비트 (x86)를 선택했기 때문에 프로젝트 에서 저 두번째 칸은 x86으로 바꿔줘야 한다.



자 그러면 이제 제대로 설치가 됬는지 확인해보자.



4-1) 프로젝트 맨 위에서 #include <d9만 쳐도 라이브러리 들이 다음처럼 표시된다. (정상적으로 연결되었다는 뜻)

-> #include <d3d9.h>를 써주자.



4-2) 헤더를 include 한뒤에 변수에 IDirect3DDevide9* device = 0; 라고 한번 선언해보자. 사진처럼 정상적으로 떠야한다.

4-3) 컨트롤 + F5를 통해 한번 실행해본다.


이렇게 해도 안될경우 나는 안되서 프로젝트 -> 속성 -> 링커 -> 입력 -> 추가 종속성 -> 편집 -> 부모 또는 프로젝트 기본값에서 상속 으로 변경했다.



+ Recent posts