제8장 스텐실
스텐실 버퍼 스텐실 버퍼는 특수한 효과를 위한 오프 스크린 버퍼 후면 버퍼 및 깊이 버퍼와 동일한 해상도를 가지므로 스텐실 버퍼 내의 ij번째 픽셀은 후면 버퍼나 깊이 버퍼의 ij번째 픽셀과 대응 후면 버퍼의 일정 부분이 렌더링 되는 것을 막는 스텐실 효과에 이용 2009-1학기 컴퓨터게임(DirectX)
스텐실 버퍼 이용하기 스텐실 버퍼를 이용하기 위해서는 먼저 Direct3D를 초기화하는 시점에 스텐실 버퍼를 요청해야 하며, 이용할 때 이를 활성화시켜야 함 활성화할 때는 D3DRS_STENCILENABLE 렌더 상태를 true로 지정, 비활성화 하려면 false로 지정 Device->SetRenderState(D3DRS_STENCILENABLE,true); …// 스텐실 관련 작업을 수행 Device->SetRenderState(D3DRS_STENCILENABLE,false); 2009-1학기 컴퓨터게임(DirectX)
스텐실 버퍼의 디폴트 값 IDirect3DDevice9::Clear 메서드를 이용. 이 메서드는 후면 버퍼와 깊이 버퍼를 소거하는 데 이용하던 것과 동일 Device->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ ZBUFFER|D3DCLEAR_STENCIL,0xff000000,1.0f,0); 함수는 타겟(후면 버퍼) 및 깊이 버퍼와 함께 세 번째 인자로 D3DCLEAR_STENCIL을 추가 여섯번째 인자는 스텐실 버퍼를 소거하는데 이용될 값을 지정하는 것으로 여기에는 0을 이용 2009-1학기 컴퓨터게임(DirectX)
스텐실 버퍼 요청하기 스텐실 버퍼는 깊이 버퍼를 만들 때 함께 만들 수 있으며, 깊이 버퍼의 포맷을 지정할 때 스텐실 버퍼의 포맷도 함께 지정할 수 있음 실제로 스텐실 버퍼와 깊이 버퍼는 동일한 오프 스크린 표면 버퍼를 공유하며, 각 픽셀 내의 메모리 세그먼트만 해당 버퍼로 이용됨 세 가지 깊이/스텐실 버퍼 포맷 D3DFMT_D24S8 – 32비트 깊이/스텐실 버퍼를 만들어 깊이 버퍼에는 픽셀당 24비트를, 스텐실 버퍼에는 픽셀당 8비트 할당 D3DFMT_D24X4S4 – 32비트 싶이/스텐실 버퍼를 만들어 깊이 버퍼에는 픽셀당 24비트를, 스텐실 버퍼에는 픽셀당 4비트를 할당. 나머지 4비트는 이용되지 않음 D3DFMT_D15S1 – 16비트 깊이/스텐실 버퍼를 만들어 깊이 버퍼에는 픽셀 당 15비트를, 스텐실 버퍼에는 픽셀당 1비트를 할당 2009-1학기 컴퓨터게임(DirectX)
스텐실 테스트 특정 픽셀의 렌더링을 막기 위해 다음 표현식 사용 (참조 & 매스크) 비교 연산자 (값 & 매스크) 모든 픽셀에 대해 스텐실 테스트가 이루어지며 이 때 사용되는 두 개 피연산자는 왼쪽 피연산자(LHS=참조&매스크) 애플리케이션이 정의한 스텐실 참조값(참조)과 애플리케이션이 정의한 매스크값(매스크)의 AND연산으로 얻어짐 오른쪽 피연산자(RHS=값&매스크) 현재 테스트하려는 픽셀의 스텐실 버퍼(값)와 애플리케이션이 정의한 매스크값(매스크)의 AND 연산으로 얻어짐 비교 연산자에 지정된 방법으로 LHS와 RHS를 비교하는 스텐실 테스트가 수행되며, 표현식은 결국 부울(true나 false)값으로 평가 True로 평가되면 후면 버퍼의 픽셀 출력 False 로 평가되면 픽셀이 출력되는 것을 막는다. 2009-1학기 컴퓨터게임(DirectX)
스텐실 테스트 제어하기 스텐실 참조 값과 매스크값, 비교 연산자까지도 우리가 지정할 수 있다. 스텐실 참조값 스텐실 참조값은 디폴트로 0이지만, D3DRS_STENCILREF 렌더 상태를 이용해 값을 바꿀 수 있음 Device->SetRenderState(D3DRS_STENCILREF,0x1); 이 코드는 스텐실 참조 값을 1로 바꿈 스텐실 매스크 스탠실 매스크 값은 참조와 값 변수 양쪽의 비트를 매스크하는 데 이용됨 디폴트 매스크는 0xffffffff이며, 이는 어떤 비트도 매스크하지 않는다는 의미 D3DRS_STENCILMASK 렌더 상태를 이용하여 매스크 상태 변경 Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff); 2009-1학기 컴퓨터게임(DirectX)
스텐실 테스팅을 수행하고 있는 현재 픽셀의 스텐실 버퍼값 스텐실 값 스텐실 테스팅을 수행하고 있는 현재 픽셀의 스텐실 버퍼값 예를 들어, 현재 ij번째 픽셀을 테스트하고 있다면 이 값은 스텐실 버퍼의 ij번째 항목이 되는 것임 2009-1학기 컴퓨터게임(DirectX)
typedef enum _D3DCMPFUNC { D3DCMP_NEVER=1, D3DCMP_LESS=2, 비교 연산자 D3DRS_STENCILFUNC 렌더 상태를 이용하면 비교 연산자를 지정할 수 있고, 이 비교 연산자로는 D3DCMPFUNC 열거형의 멤버 중 하나를 사용 typedef enum _D3DCMPFUNC { D3DCMP_NEVER=1, D3DCMP_LESS=2, D3DCMP_EQUAL=3, D3DCMP_LESSEQUAL=4, D3DCMP_GREATER=5, D3DCMP_NOTEQUAL=6, D3DCMP_GREATEREQUAL=7, D3DCMP_ALWAYS=8, D3DCMP_FORCE_DWORD=0x7fffffff } D3DCMPFUNC; 2009-1학기 컴퓨터게임(DirectX)
D3DCMP_NEVER-스텐실 테스트가 항상 실패 D3DCMP_LESS - LHS<RHS일 경우 스텐실 테스트가 성공 D3DCMP_EQUAL - LHS=RHS일 경우 스텐실 테스트가 성공 D3DCMP_LESSEQUAL - LHS≤RHS일 경우 스텐실 테스트가 성공 D3DCMP_GREATER – LHS>RHS일 경우 스텐실 테스트가 성공 D3DCMP_NOTEQUAL - LHS≠RHS일 경우 스텐실 테스트가 성공 D3DCMP_GREATEREQUAL - LHS≥RHS일 경우 스텐실 테스트가 성공 D3DCMP_ALWAYS – 스텐실 테스트가 항상 성공 2009-1학기 컴퓨터게임(DirectX)
스텐실 버퍼 갱신하기 다음 세 상황에 따라 스텐실 버퍼 항목이 갱신되는 방법 정의 1) ij번째 픽셀에서 스텐실 테스트가 실패. D3DRS_STENCILFAIL렌더 상태를 지정하여 스텐실 버퍼 내의 ij번째 항목을 갱신 Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation); 2) ij번째 픽셀에서 깊이 테스트가 실패. D3DRS_STENCILZFAIL 렌더 상태를 지정하여 ij번째 항목을 정의 Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation); 3) ij번째 픽셀에서 깊이 테스트와 스텐실 테스트가 성공. D3DRS_STENCILPASS 렌더 상태를 지정하여 ij번째 항목을 갱신 Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation); 2009-1학기 컴퓨터게임(DirectX)
StencilOperation의 값으로는 다음 상수를 사용 D3DSTENCILOP_KEEP – 스텐실 버퍼 항목을 변경하지 않도록 지정 D3DSTENCILOP_ZERO – 스텐실 버퍼 항목을 0으로 지정 D3DSTENCILOP_REPLACE – 스텐실 버퍼 항목을 스텐실 참조 값으로 대체하도록 지정 D3DSTENCILOP_INCRSAT – 스텐실 버퍼 항목을 증가시키도록 지정. 만약 증가된 값이 허용 최대 값을 넘을 경우, 해당 항목을 최대치로 고정 D3DSTENCILOP_DECRSAT – 스텐실 버퍼 항목을 감소시키도록 지정. 만약 감소된 값이 0보다 작을 경우, 해당 항목을 0으로 고정 D3DSTENCILOP_INVERT – 스텐실 버퍼 항목을 반전시키도록 지정 D3DSTENCILOP_INCR – 스텐실 버퍼 항목을 증가시키도록 지정. 만약 증가된 값이 허용 최대 값을 넘을 경우, 0으로 돌려짐 D3DSTENCILOP_DECR – 스텐실 버퍼 항목을 감소시키도록 지정. 만약 감소된 값이 0보다 작을 경우 허용 최대 값으로 돌려짐 2009-1학기 컴퓨터게임(DirectX)
스텐실 쓰기 매스크 스텐실 렌더 상태 이외에도 스텐실 버퍼에 쓰여지는 모든 값을 매스크 하는 쓰기 매스크를 설정할 수도 있음. 쓰기 매스크의 디폴트 값은 0xffffffff이며, D3DRS_STENCILWRITEMASK 렌더 상태를 이용하여 쓰기 매스크를 지정 Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0x0000ffff); 2009-1학기 컴퓨터게임(DirectX)
예제 애플리케이션: 거울 프로그래밍으로 거울을 구현하기 위해서는 두 가지 문제를 해결해야 함 올바르게 반사를 그려내기 위해 임의의 평면에 물체가 반사되는 방법을 파악 벡터 기하학을 통해 해결 거울 영역에만 반사를 그려야 함 거울로 표시된 부분에만 반사된 물체를 그리도록 함 스텐실 버퍼를 통해 해결 2009-1학기 컴퓨터게임(DirectX)
임의의 평면에 대한 반사 k 는 v에서 평면으로의 부호를 가진 최단거리 v’ n -2kn kn v n • p + d = 0 2009-1학기 컴퓨터게임(DirectX)
반사를 위한 수학 2009-1학기 컴퓨터게임(DirectX)
D3DX 라이브러리는 R과 같은 임의의 평면에 대한 반사 행렬을 만들어내는 함수를 제공 D3DXMATRIX *D3DXMatrixReflect( D3DXMATRIX *pOut, //결과 반사행렬 CONST D3DXPLANE *pPlane //반사할 평명 ); 반사 변환의 세 가지 특수한 경우 yz 평면 반대쪽의 포인트를 반사하기 위해서는 x-성분의 반대를 취하고, xz 평면 반대쪽의 포인트를 반사하기 위해서는 y-성분의 반대를 취하고, xy평면 반대쪽의 포인트를 반사하기 위해서는 z-성분의 반대를 취함 2009-1학기 컴퓨터게임(DirectX)
거울 구현의 개관 거울을 구현할 때 중요한 요점 한 가지는 거울의 앞에서만 물체가 반사된다는 것 거울 앞에 있는지를 공간적으로 확인한다면 너무 복잡한 작업이 필요로 함 따라서 항상 물체를 반사시키고 어떤 표면이든지 이를 렌더링하는 방법을 선택 물체의 반사가 거울이 아닌 다른 표면에도 렌더링 됨 스텐실 버퍼가 후면 버퍼 내의 특정 영역이 렌더링 되는 것을 막아줄 수 있으므로 이 문제의 해결책으로 적합 2009-1학기 컴퓨터게임(DirectX)
바닥과 벽, 거울, 주전자를 포함하는 전체 장면을 보통 때와 마찬가지로 렌더링 함. 스텐실 버퍼를 0값으로 소거 작업 처리 순서 바닥과 벽, 거울, 주전자를 포함하는 전체 장면을 보통 때와 마찬가지로 렌더링 함. 스텐실 버퍼를 0값으로 소거 거울을 구성하는 기본형을 스텐실 버퍼에만 렌더링 함 스텐실 테스트가 항상 성공하도록 하고 테스트가 성공하면 스텐실 버퍼 항목을 1로 대체하도록 지정 거울만을 렌더링하는 것이므로 거울에 해당하는 픽셀들만 1값을 가지며, 나머지 모든 영역을 0값으로 남음 반사된 물체를 후면 버퍼와 스텐실 버퍼로 렌더링 함 스텐실 테스트를 통과한 부분만 후면 버퍼 렌더링이 됨 스텐실 버퍼 항목이 1인 경우에만 스텐실 테스트를 통과하도록 지정 스텐실 버퍼내의 거울에 해당하는 항목들만 1값을 가지므로 반사된 주전자는 거울에만 렌더링 됨 2009-1학기 컴퓨터게임(DirectX)
코드와 설명 이 예제와 관련된 코드는 RenderMirror 함수에 포함되어 있음 스텐실 버퍼에 거울 기본형을 렌더링하고 거울에 해당되는 부분에만 반사된 주전자를 렌더링 함 파트 1: 스텐실 버퍼를 활성화하고 관련된 렌더 상태의 값을 지정 void RenderMirror() { Device->SetRenderState(D3DRS_STENCILENABLE, true); Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ ALWAYS); Device->SetRenderState(D3DRS_STENCILREF, 0x1); Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff); Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_ KEEP); Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_ KEEP); Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_ REPLACE); 2009-1학기 컴퓨터게임(DirectX)
스텐실 비교 연산자를 D3CMP_ALWAYS로 지정하여 스텐실 테스트가 항상 성공하도록 함 깊이 테스트가 실패하면 D3DSTENCILOP_KEEP을 지정하여 스텐실 버퍼 항목을 갱신하지 말고 현재의 값 유지 깊이 테스트가 실패한다면 그것은 픽셀이 가려졌음을 의미. 가려진 픽셀에 반사된 물체를 렌더링 할 필요는 없음 깊이 테스트와 스텐실 테스트가 모두 성공한 경우에는 D3DSTENCILOP_REPLACE를 지정하여 스텐실 버퍼 항목을 스텐실 참조값인 0x1로 대체하도록 함 2009-1학기 컴퓨터게임(DirectX)
D3DRS_ZWRITEENABLE를 false로 지정하면 깊이 버퍼로 쓰여지는 것을 막을 수 있음 파트II: 스텐실 버퍼에 거울을 렌더링 D3DRS_ZWRITEENABLE를 false로 지정하면 깊이 버퍼로 쓰여지는 것을 막을 수 있음 원본 블렌드 인수를 D3DBLEND_ZERO로, 목적지 블렌드 인수를 D3DBLEND_ONE으로 지정하고 블렌딩을 이용하면 후면 버퍼 갱신을 막는다. 블렌드 방정식 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_ZWRITEENABLE, false); //깊이 버퍼와 후면 버퍼로의 쓰기를 막는다 Device->SetRenderState(D3DRS_ZWRITEENABLE, false); Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); // 스텐실 버퍼에 거울을 그린다. Device->SetStreamSource(0, VB, 0, sizeof(Vertex)); Device->SetFVF(Vertex::FVF); Device->SetMaterial(&MirrorMtrl); Device->SetTexture(0, MirrorTex); D3DXMATRIX I; D3DXMatrixIdentity(&I); Device->SetTransform(D3DTS_WORLD, &I); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2); 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_ZWRITEENABLE, true ); 파트III // 깊이 버퍼를 활성화 한다. Device->SetRenderState(D3DRS_ZWRITEENABLE, true ); 파트III 스텐실 버퍼 내의 가시 픽셀 중 거울에 해당하는 픽셀들은 0x1 값을 가지며 거울로 렌더링될 부분을 표시 반사된 주전자 렌더링을 위한 준비 단계로 렌더 상태를 지정 Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); 2009-1학기 컴퓨터게임(DirectX)
(ref & mask == (value & mask) 새로운 비교 연산자를 지정하고 다음과 같이 스텐실 테스트를 구성 (ref & mask == (value & mask) (0x1 & 0xffffffff) == (value & 0xffffffff) (0x1) == (value & 0xffffffff) 이제 value=0x1인 경우에만 스텐실 테스트를 통과. 스텐실 버퍼 내에서 0x1값을 가진 영역은 거울에 해당하는 영역이므로 이 영역을 렌더링하는 동안에만 테스트가 성공 D3DRS_STENCILPASS 렌더 상태를 D3DSTENCILOP_KEEP로 지정 테스트가 성공하면 스텐실 버퍼 내의 값을 유지 다음 렌더링 하는 단계에서는 스텐실 버퍼 내의 값은 변경되지 않고, 거울에 해당하는 픽셀들을 표시하는 데만 이용 2009-1학기 컴퓨터게임(DirectX)
파트 IV: 장면 내 반사의 위치를 잡는 행렬을 계산하는 것 // 반사의 위치를 잡는다. D3DXMATRIX W, T, R; D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f); // xy plane D3DXMatrixReflect(&R, &plane); D3DXMatrixTranslation(&T, TeapotPosition.x, TeapotPosition.y, TeapotPosition.z); W = T * R; 먼저 반사되지 않은 주전자 위치로 이동. 위치가 정해지면 xy평면으로 반사를 수행. 2009-1학기 컴퓨터게임(DirectX)
지금 시점에서 주전자를 렌더링하면 반사된 주전자는 나타나지 않음 파트 V 지금 시점에서 주전자를 렌더링하면 반사된 주전자는 나타나지 않음 반사된 주전자의 깊이는 거울의 깊이보다 크며, 따라서 거울의 기본형이 기술적으로 반사된 주전자를 가리기 때문 이를 해결하기 위해 깊이 버퍼를 소거 Device->Clear(0,0,D3DCLEAR_ZBUFFER,0,1.0f,0); 단순히 깊이 버퍼를 소거한다면 반사된 주전자는 거울 전면에 그려지기 때문에 우리가 원하던 결과와의 차이가 있음 원하는 결과를 얻기 위해서는 깊이 버퍼를 소거함과 동시에 반사된 주전자를 거울과 섞어(블렌드) 주어야 함 2009-1학기 컴퓨터게임(DirectX)
반사된 주전자와 거울을 블렌드하기 위해 다음 방정식 이용 원본 픽셀을 반사된 주전자이며 목적지 픽셀은 거울이므로 이들이 어떻게 블렌드 되는지는 방정식을 통해 알 수 있음 Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ DESTCOLOR); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ ZERO); 2009-1학기 컴퓨터게임(DirectX)
Device->SetTransform(D3DTS_WORLD, &W); 이제 반사된 주전자를 그릴 준비 완료 Device->SetTransform(D3DTS_WORLD, &W); Device->SetMaterial(&TeapotMtrl); Device->SetTexture(0, 0); Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); Teapot->DrawSubset(0); W로 반사된 주전자를 장면 내의 적절한 위치로 이동시키고 후면 추려내기 모드를 변경 물체가 반사될 때 물체의 전면과 후면이 뒤바뀌기 때문임 2009-1학기 컴퓨터게임(DirectX)
이를 보정하기 위해 후면 추려내기 조건을 변경해야 함 새로운 전면은 Direct3D가 후면으로 인식하는 두르기 순서를 가지며, 새로운 후면은 Direct3D가 전면으로 인식하는 두르기 순서를 가짐 이를 보정하기 위해 후면 추려내기 조건을 변경해야 함 마지막으로 블렌딩과 스텐실을 비활성화 하였으며, 후면 추려내기 모드를 원래대로 복구 Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); Device->SetRenderState( D3DRS_STENCILENABLE, false); Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); 2009-1학기 컴퓨터게임(DirectX)
실행결과 2009-1학기 컴퓨터게임(DirectX)
IDirect3DDevice9* Device = 0; const int Width = 640; #include "d3dUtility.h" IDirect3DDevice9* Device = 0; const int Width = 640; const int Height = 480; IDirect3DVertexBuffer9* VB = 0; IDirect3DTexture9* FloorTex = 0; IDirect3DTexture9* WallTex = 0; IDirect3DTexture9* MirrorTex = 0; 2009-1학기 컴퓨터게임(DirectX)
D3DMATERIAL9 FloorMtrl = d3d::WHITE_MTRL; D3DMATERIAL9 WallMtrl = d3d::WHITE_MTRL; D3DMATERIAL9 MirrorMtrl = d3d::WHITE_MTRL; ID3DXMesh* Teapot = 0; D3DXVECTOR3 TeapotPosition(0.0f, 3.0f, -7.5f); D3DMATERIAL9 TeapotMtrl = d3d::YELLOW_MTRL; void RenderScene(); void RenderMirror(); struct Vertex { Vertex(){} 2009-1학기 컴퓨터게임(DirectX)
Vertex(float x, float y, float z, float nx, float ny, float nz, float u, float v) { _x = x; _y = y; _z = z; _nx = nx; _ny = ny; _nz = nz; _u = u; _v = v; } float _x, _y, _z; float _nx, _ny, _nz; float _u, _v; static const DWORD FVF; }; 2009-1학기 컴퓨터게임(DirectX)
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1; bool Setup() { WallMtrl.Specular = d3d::WHITE * 0.2f; D3DXCreateTeapot(Device, &Teapot, 0); Device->CreateVertexBuffer( 24 * sizeof(Vertex), 0, // usage Vertex::FVF, D3DPOOL_MANAGED, &VB, 0); 2009-1학기 컴퓨터게임(DirectX)
VB->Lock(0, 0, (void**)&v, 0); // floor Vertex* v = 0; VB->Lock(0, 0, (void**)&v, 0); // floor v[0] = Vertex(-7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f); v[1] = Vertex(-7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); v[2] = Vertex( 7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); v[3] = Vertex(-7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f); v[4] = Vertex( 7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); v[5] = Vertex( 7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f); 2009-1학기 컴퓨터게임(DirectX)
v[6] = Vertex(-7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); // wall v[6] = Vertex(-7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[7] = Vertex(-7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[8] = Vertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[9] = Vertex(-7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[10] = Vertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[11] = Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f); // Note: We leave gap in middle of walls for mirror v[12] = Vertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[13] = Vertex(2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[14] = Vertex(7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); 2009-1학기 컴퓨터게임(DirectX)
v[15] = Vertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); // mirror v[18] = Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[19] = Vertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[20] = Vertex( 2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[21] = Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[22] = Vertex( 2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[23] = Vertex( 2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f); VB->Unlock(); 2009-1학기 컴퓨터게임(DirectX)
D3DXCreateTextureFromFile(Device, "checker.jpg", &FloorTex); D3DXCreateTextureFromFile(Device, "brick0.jpg", &WallTex); D3DXCreateTextureFromFile(Device, "ice.bmp", &MirrorTex); Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); D3DXVECTOR3 lightDir(0.707f, -0.707f, 0.707f); D3DXCOLOR color(1.0f, 1.0f, 1.0f, 1.0f); D3DLIGHT9 light = d3d::InitDirectionalLight(&lightDir, &color); 2009-1학기 컴퓨터게임(DirectX)
Device->SetLight(0, &light); Device->LightEnable(0, true); Device->SetRenderState(D3DRS_NORMALIZENORMALS, true); Device->SetRenderState(D3DRS_SPECULARENABLE, true); D3DXVECTOR3 pos(-10.0f, 3.0f, -15.0f); D3DXVECTOR3 target(0.0, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &pos, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); 2009-1학기 컴퓨터게임(DirectX)
D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI / 4.0f, // 45 - degree D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI / 4.0f, // 45 - degree (float)Width / (float)Height, 1.0f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj); return true; } void Cleanup() { 2009-1학기 컴퓨터게임(DirectX)
d3d::Release<IDirect3DVertexBuffer9*>(VB); d3d::Release<IDirect3DTexture9*>(FloorTex); d3d::Release<IDirect3DTexture9*>(WallTex); d3d::Release<IDirect3DTexture9*>(MirrorTex); d3d::Release<ID3DXMesh*>(Teapot); } bool Display(float timeDelta) { if( Device ) static float radius = 20.0f; if( ::GetAsyncKeyState(VK_LEFT) & 0x8000f ) TeapotPosition.x -= 3.0f * timeDelta; 2009-1학기 컴퓨터게임(DirectX)
스텐실 if( ::GetAsyncKeyState(VK_RIGHT) & 0x8000f ) TeapotPosition.x += 3.0f * timeDelta; if( ::GetAsyncKeyState(VK_UP) & 0x8000f ) radius -= 2.0f * timeDelta; if( ::GetAsyncKeyState(VK_DOWN) & 0x8000f ) radius += 2.0f * timeDelta; static float angle = (3.0f * D3DX_PI) / 2.0f; if( ::GetAsyncKeyState('A') & 0x8000f ) angle -= 0.5f * timeDelta; 2009-1학기 컴퓨터게임(DirectX)
if( ::GetAsyncKeyState('S') & 0x8000f ) angle += 0.5f * timeDelta; D3DXVECTOR3 position( cosf(angle) * radius, 3.0f, sinf(angle) * radius ); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &position, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xff000000, 1.0f, 0L); 2009-1학기 컴퓨터게임(DirectX)
Device->BeginScene(); RenderScene(); RenderMirror(); Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; void RenderScene() { 2009-1학기 컴퓨터게임(DirectX)
Device->SetMaterial(&TeapotMtrl); Device->SetTexture(0, 0); D3DXMATRIX W; D3DXMatrixTranslation(&W, TeapotPosition.x, TeapotPosition.y, TeapotPosition.z); Device->SetTransform(D3DTS_WORLD, &W); Teapot->DrawSubset(0); D3DXMATRIX I; D3DXMatrixIdentity(&I); Device->SetTransform(D3DTS_WORLD, &I); 2009-1학기 컴퓨터게임(DirectX)
Device->SetStreamSource(0, VB, 0, sizeof(Vertex)); Device->SetFVF(Vertex::FVF); // draw the floor Device->SetMaterial(&FloorMtrl); Device->SetTexture(0, FloorTex); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2); // draw the walls Device->SetMaterial(&WallMtrl); Device->SetTexture(0, WallTex); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 6, 4); // draw the mirror Device->SetMaterial(&MirrorMtrl); 2009-1학기 컴퓨터게임(DirectX)
Device->SetTexture(0, MirrorTex); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2); } void RenderMirror() { Device->SetRenderState(D3DRS_STENCILENABLE, true); Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); Device->SetRenderState(D3DRS_STENCILREF, 0x1); Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); // disable writes to the depth and back buffers Device->SetRenderState(D3DRS_ZWRITEENABLE, false); Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); // draw the mirror to the stencil buffer Device->SetStreamSource(0, VB, 0, sizeof(Vertex)); Device->SetFVF(Vertex::FVF); Device->SetMaterial(&MirrorMtrl); 2009-1학기 컴퓨터게임(DirectX)
Device->SetTexture(0, MirrorTex); D3DXMATRIX I; D3DXMatrixIdentity(&I); Device->SetTransform(D3DTS_WORLD, &I); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2); // re-enable depth writes Device->SetRenderState( D3DRS_ZWRITEENABLE, true ); // only draw reflected teapot to the pixels where the mirror // was drawn to. Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); 2009-1학기 컴퓨터게임(DirectX)
// position reflection D3DXMATRIX W, T, R; D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f); // xy plane D3DXMatrixReflect(&R, &plane); D3DXMatrixTranslation(&T, TeapotPosition.x, TeapotPosition.y, TeapotPosition.z); W = T * R; // clear depth buffer and blend the reflected teapot with the mirror Device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 1.0f, 0); 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); // Finally, draw the reflected teapot Device->SetTransform(D3DTS_WORLD, &W); Device->SetMaterial(&TeapotMtrl); Device->SetTexture(0, 0); Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); Teapot->DrawSubset(0); // Restore render states. Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); Device->SetRenderState( D3DRS_STENCILENABLE, false); 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); } LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); 2009-1학기 컴퓨터게임(DirectX)
return ::DefWindowProc(hwnd, msg, wParam, lParam); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam); int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL, &Device)) 2009-1학기 컴퓨터게임(DirectX)
::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) ::MessageBox(0, "Setup() - FAILED", 0, 0); d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); 2009-1학기 컴퓨터게임(DirectX)
예제 애플리케이션: 평면 그림자 그림자는 장면의 어느 곳에서 빛이 비추고 있는지를 인식할 수 있도록 해주며 궁극적으로 장면의 사실감을 높여줌 평면 그림자를 구현하기 위해서는 먼저 물체가 평면에 만들어내는 그림자를 찾고 렌더링이 가능하도록 이를 기하학적으로 구성해야 함 이 과정은 약간의 3D수학을 통해 간단히 해결할 수 있으며, 이어 그림자를 묘사하는 다각형을 50% 투명도를 가진 검은 재질을 이용해 렌더링하면 됨 이와 같은 그림자 렌더링 방식에는 더블 블렌딩이라 불리우는 약간의 부작용이 따름 2009-1학기 컴퓨터게임(DirectX)
평행 그림자 s n·p+d=0 (그림) 평행 광원에 의해 발생하는 그림자 위 그림에서 L방향을 가지는 평행 광원에서 버텍스 p로 통하는 광선은 r(t)=p+tL로 얻을 수 있고, 광선 r(t)와 평면 n·p+d=0을 교차하여 s를 얻음 P L 2009-1학기 컴퓨터게임(DirectX)
광선 r(t)를 물체의 각 버텍스에서 평면으로 발사해 얻은 교차점의 집합으로 그림자의 기하정보를 정의할 수 있음 교차점 s는 광선/평면 교차 테스트를 통해 간단히 얻음 2009-1학기 컴퓨터게임(DirectX)
점 조명 그림자 (그림) 점 조명 광원에 의해 발생하는 그림자 L P n • p + d = 0 s 2009-1학기 컴퓨터게임(DirectX)
광선 r(t)와 평면 n·p+d=0과의 교차점으로 s를 얻을 수 있음 r(t)=p+t(p-L)로 얻을 수 있음 광선 r(t)와 평면 n·p+d=0과의 교차점으로 s를 얻을 수 있음 광선 r(t)를 물체의 각 버텍스에서 평면으로 발사해 얻은 교차점의 집합으로 그림자의 기하정보를 얻을 수 있으며, 평면/광선교차로 s를 풀어낼 수 있음 2009-1학기 컴퓨터게임(DirectX)
그림자 행렬 평행 조명의 그림자는 결국 지정된 광선 방향으로 평면 n·p+d=0에 물체를 평행 투영한 것 버텍스 p에서 평면 n·p+d=0으로의 투영 s는 하나의 행렬로 표현될 수 있고, 동일한 행렬로 직각 투영과 원근 투영을 동시에 표현하는 것도 가능 그림자가 만들어질 평면의 공통 평면 방정식의 계수로 4D 벡터(nx,ny,nz,d)를 이용하고, 평행 조명의 방향이나 점 조명의 위치를 표현하는 4D 벡터로 L=(Lx,Ly,Lz,Lw)를 이용함 2009-1학기 컴퓨터게임(DirectX)
w는 다음과 같이 이용됨 2009-1학기 컴퓨터게임(DirectX)
D3DXMATRIX *D3DXMatrixShadow( D3DXMTRIX *pOut, 그림자 행렬을 만들어내는 함수. D3DXMATRIX *D3DXMatrixShadow( D3DXMTRIX *pOut, CONST D3DXVECTOR4 *pLight, // L CONST D3DXPLANE *pPlane // 그림자를 만들 평면 ); w=0일 경우 평행 조명에서, w=1일 경우 점 조명에서 주어진 평면으로 그림자를 투영 2009-1학기 컴퓨터게임(DirectX)
코드와 설명 관련 코드: RenderShadow() 함수 우선 스텐실 렌더 상태를 지정 스텐실 비교 함수를 D3DCMP_EQUAL로 지정하고 D3DRS_STENCILREF 렌더 상태를 0x0으로 지정하여 스텐실 버퍼 내의 대응되는 항목이 0x0일 경우에만 후면 버퍼에 그림자를 렌더링 하도록 함 스텐실 버퍼는 0으로 소거되어 있으므로 처음으로 특정 픽셀을 쓸 때는 항상 테스트가 성공하지만 D3DRS_STENCILPASS를 D3DSTENCILOP_INCR로 지정하였으므로 이미 쓰여진 픽셀을 쓰려고 할 때는 테스트가 실패 한 번 쓰여진 스텐실 항목의 픽셀은 0x1이 되므로 이를 다시 쓸 수 없음. 즉, 픽셀의 덮어쓰기를 막는 방법으로 더블 블렌딩 현상을 제거한 것 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_STENCILENABLE, true); Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); Device->SetRenderState(D3DRS_STENCILREF, 0x1); Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_ KEEP); Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_ REPLACE); 2009-1학기 컴퓨터게임(DirectX)
다음은 그림자 변환을 계산하고 장면 내의 적절한 위치로 그림자를 이동 D3DXVECTOR4 lightDirection(0.707f, -0.707f, 0.707f, 0.0f); D3DXPLANE groundPlane(0.0f, -1.0f, 0.0f, 0.0f); D3DXMATRIX S; D3DXMatrixShadow(&S, &lightDirection, &groundPlane); D3DXMATRIX T; D3DXMatrixTranslation(&T, TeapotPosition.x, TeapotPosition.y, TeapotPosition.z); D3DXMATRIX W = T * S; Device->SetTransform(D3DTS_WORLD, &W); 2009-1학기 컴퓨터게임(DirectX)
알파 블렌딩과 스텐실 테스팅을 비활성화하는 정리 단계를 수행 마지막으로 50% 투명도의 검은 재질을 지정하고 깊이 테스팅을 비활성화한 다음, 그림자를 렌더링하였으며 깊이 버퍼를 다시 활성화하고 알파 블렌딩과 스텐실 테스팅을 비활성화하는 정리 단계를 수행 Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 2009-1학기 컴퓨터게임(DirectX)
mtrl.Diffuse.a = 0.5f; // 50% transparency. D3DMATERIAL9 mtrl = d3d::InitMtrl(d3d::BLACK, d3d::BLACK, d3d::BLACK, d3d::BLACK, 0.0f); mtrl.Diffuse.a = 0.5f; // 50% transparency. // 바닥에 그림자를 렌더링할 때 z-쟁탈이 일어나지 않도록 // 깊이 버퍼를 끈다. Device->SetRenderState(D3DRS_ZENABLE, false); Device->SetMaterial(&mtrl); Device->SetTexture(0, 0); Teapot->DrawSubset(0); Device->SetRenderState(D3DRS_ZENABLE, true); Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); Device->SetRenderState(D3DRS_STENCILENABLE, false); 2009-1학기 컴퓨터게임(DirectX)
실행결과 2009-1학기 컴퓨터게임(DirectX)
IDirect3DDevice9* Device = 0; const int Width = 640; #include "d3dUtility.h" IDirect3DDevice9* Device = 0; const int Width = 640; const int Height = 480; IDirect3DVertexBuffer9* VB = 0; IDirect3DTexture9* FloorTex = 0; IDirect3DTexture9* WallTex = 0; IDirect3DTexture9* MirrorTex = 0; 2009-1학기 컴퓨터게임(DirectX)
D3DMATERIAL9 FloorMtrl = d3d::WHITE_MTRL; D3DMATERIAL9 WallMtrl = d3d::WHITE_MTRL; D3DMATERIAL9 MirrorMtrl = d3d::WHITE_MTRL; ID3DXMesh* Teapot = 0; D3DXVECTOR3 TeapotPosition(0.0f, 3.0f, -7.5f); D3DMATERIAL9 TeapotMtrl = d3d::YELLOW_MTRL; void RenderScene(); void RenderMirror(); void RenderShadow(); struct Vertex { 2009-1학기 컴퓨터게임(DirectX)
Vertex(float x, float y, float z, float nx, float ny, float nz, float u, float v) { _x = x; _y = y; _z = z; _nx = nx; _ny = ny; _nz = nz; _u = u; _v = v; } float _x, _y, _z; float _nx, _ny, _nz; float _u, _v; static const DWORD FVF; }; 2009-1학기 컴퓨터게임(DirectX)
const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1; bool Setup() { WallMtrl.Specular = d3d::WHITE * 0.2f; D3DXCreateTeapot(Device, &Teapot, 0); Device->CreateVertexBuffer( 24 * sizeof(Vertex), 0, // usage Vertex::FVF, D3DPOOL_MANAGED, &VB, 0); 2009-1학기 컴퓨터게임(DirectX)
VB->Lock(0, 0, (void**)&v, 0); Vertex* v = 0; VB->Lock(0, 0, (void**)&v, 0); v[0] = Vertex(-7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f); v[1] = Vertex(-7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); v[2] = Vertex( 7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); v[3] = Vertex(-7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f); v[4] = Vertex( 7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); v[5] = Vertex( 7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f); v[6] = Vertex(-7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[7] = Vertex(-7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[8] = Vertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); 2009-1학기 컴퓨터게임(DirectX)
v[9] = Vertex(-7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); // Note: We leave gap in middle of walls for mirror v[12] = Vertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[13] = Vertex(2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[14] = Vertex(7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[15] = Vertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[16] = Vertex(7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[17] = Vertex(7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f); 2009-1학기 컴퓨터게임(DirectX)
v[18] = Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); // mirror v[18] = Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[19] = Vertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[20] = Vertex( 2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[21] = Vertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[22] = Vertex( 2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[23] = Vertex( 2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f); VB->Unlock(); D3DXCreateTextureFromFile(Device, "checker.jpg", &FloorTex); D3DXCreateTextureFromFile(Device, "brick0.jpg", &WallTex); D3DXCreateTextureFromFile(Device, "ice.bmp", &MirrorTex); 2009-1학기 컴퓨터게임(DirectX)
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); D3DXVECTOR3 lightDir(0.707f, -0.707f, 0.707f); D3DXCOLOR color(1.0f, 1.0f, 1.0f, 1.0f); D3DLIGHT9 light = d3d::InitDirectionalLight(&lightDir, &color); Device->SetLight(0, &light); Device->LightEnable(0, true); Device->SetRenderState(D3DRS_NORMALIZENORMALS, true); Device->SetRenderState(D3DRS_SPECULARENABLE, true); 2009-1학기 컴퓨터게임(DirectX)
D3DXVECTOR3 pos(-10.0f, 3.0f, -15.0f); D3DXVECTOR3 target(0.0, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &pos, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI / 4.0f, // 45 - degree (float)Width / (float)Height, 1.0f, 1000.0f); 2009-1학기 컴퓨터게임(DirectX)
Device->SetTransform(D3DTS_PROJECTION, &proj); return true; } void Cleanup() { d3d::Release<IDirect3DVertexBuffer9*>(VB); d3d::Release<IDirect3DTexture9*>(FloorTex); d3d::Release<IDirect3DTexture9*>(WallTex); d3d::Release<IDirect3DTexture9*>(MirrorTex); d3d::Release<ID3DXMesh*>(Teapot); 2009-1학기 컴퓨터게임(DirectX)
bool Display(float timeDelta) { if( Device ) static float radius = 20.0f; if( ::GetAsyncKeyState(VK_LEFT) & 0x8000f ) TeapotPosition.x -= 3.0f * timeDelta; if( ::GetAsyncKeyState(VK_RIGHT) & 0x8000f ) TeapotPosition.x += 3.0f * timeDelta; if( ::GetAsyncKeyState(VK_UP) & 0x8000f ) radius -= 2.0f * timeDelta; 2009-1학기 컴퓨터게임(DirectX)
if( ::GetAsyncKeyState(VK_DOWN) & 0x8000f ) radius += 2.0f * timeDelta; static float angle = (3.0f * D3DX_PI) / 2.0f; if( ::GetAsyncKeyState('A') & 0x8000f ) angle -= 0.5f * timeDelta; if( ::GetAsyncKeyState('S') & 0x8000f ) angle += 0.5f * timeDelta; D3DXVECTOR3 position( cosf(angle) * radius, 3.0f, sinf(angle) * radius ); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); 2009-1학기 컴퓨터게임(DirectX)
D3DXMatrixLookAtLH(&V, &position, &target, &up); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &position, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xff000000, 1.0f, 0L); Device->BeginScene(); RenderScene(); RenderShadow(); RenderMirror(); 2009-1학기 컴퓨터게임(DirectX)
Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; void RenderScene() { Device->SetMaterial(&TeapotMtrl); Device->SetTexture(0, 0); D3DXMATRIX W; D3DXMatrixTranslation(&W, TeapotPosition.x, TeapotPosition.y, TeapotPosition.z); 2009-1학기 컴퓨터게임(DirectX)
Device->SetTransform(D3DTS_WORLD, &W); Teapot->DrawSubset(0); D3DXMATRIX I; D3DXMatrixIdentity(&I); Device->SetTransform(D3DTS_WORLD, &I); Device->SetStreamSource(0, VB, 0, sizeof(Vertex)); Device->SetFVF(Vertex::FVF); // draw the floor Device->SetMaterial(&FloorMtrl); Device->SetTexture(0, FloorTex); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2); 2009-1학기 컴퓨터게임(DirectX)
Device->SetMaterial(&WallMtrl); Device->SetTexture(0, WallTex); // draw the walls Device->SetMaterial(&WallMtrl); Device->SetTexture(0, WallTex); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 6, 4); // draw the mirror Device->SetMaterial(&MirrorMtrl); Device->SetTexture(0, MirrorTex); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2); } void RenderMirror() { 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_STENCILENABLE, true); Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); Device->SetRenderState(D3DRS_STENCILREF, 0x1); Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); // disable writes to the depth and back buffers Device->SetRenderState(D3DRS_ZWRITEENABLE, false); Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); 2009-1학기 컴퓨터게임(DirectX)
// draw the mirror to the stencil buffer Device->SetStreamSource(0, VB, 0, sizeof(Vertex)); Device->SetFVF(Vertex::FVF); Device->SetMaterial(&MirrorMtrl); Device->SetTexture(0, MirrorTex); D3DXMATRIX I; D3DXMatrixIdentity(&I); Device->SetTransform(D3DTS_WORLD, &I); Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2); // re-enable depth writes Device->SetRenderState( D3DRS_ZWRITEENABLE, true ); Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); // position reflection D3DXMATRIX W, T, R; D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f); // xy plane D3DXMatrixReflect(&R, &plane); D3DXMatrixTranslation(&T, TeapotPosition.x, TeapotPosition.y, TeapotPosition.z); W = T * R; 2009-1학기 컴퓨터게임(DirectX)
// clear depth buffer and blend the reflected teapot with the mirror Device->Clear(0, 0, D3DCLEAR_ZBUFFER, 0, 1.0f, 0); Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); // Finally, draw the reflected teapot Device->SetTransform(D3DTS_WORLD, &W); Device->SetMaterial(&TeapotMtrl); Device->SetTexture(0, 0); // reverse cull mode Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); Teapot->DrawSubset(0); 2009-1학기 컴퓨터게임(DirectX)
// Restore render states. Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); Device->SetRenderState( D3DRS_STENCILENABLE, false); Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); } void RenderShadow() { Device->SetRenderState(D3DRS_STENCILENABLE, true); Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); Device->SetRenderState(D3DRS_STENCILREF, 0x0); Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff); Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); 2009-1학기 컴퓨터게임(DirectX)
Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR); // increment to 1 // position shadow D3DXVECTOR4 lightDirection(0.707f, -0.707f, 0.707f, 0.0f); D3DXPLANE groundPlane(0.0f, -1.0f, 0.0f, 0.0f); D3DXMATRIX S; D3DXMatrixShadow( &S, &lightDirection, &groundPlane); 2009-1학기 컴퓨터게임(DirectX)
D3DXMatrixTranslation( &T, TeapotPosition.x, TeapotPosition.y, TeapotPosition.z); D3DXMATRIX W = T * S; Device->SetTransform(D3DTS_WORLD, &W); // alpha blend the shadow Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 2009-1학기 컴퓨터게임(DirectX)
mtrl.Diffuse.a = 0.5f; // 50% transparency. D3DMATERIAL9 mtrl = d3d::InitMtrl(d3d::BLACK, d3d::BLACK, d3d::BLACK, d3d::BLACK, 0.0f); mtrl.Diffuse.a = 0.5f; // 50% transparency. Device->SetRenderState(D3DRS_ZENABLE, false); Device->SetMaterial(&mtrl); Device->SetTexture(0, 0); Teapot->DrawSubset(0); Device->SetRenderState(D3DRS_ZENABLE, true); Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); Device->SetRenderState(D3DRS_STENCILENABLE, false); } 2009-1학기 컴퓨터게임(DirectX)
::PostQuitMessage(0); break; case WM_KEYDOWN: LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); } 2009-1학기 컴퓨터게임(DirectX)
return ::DefWindowProc(hwnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL, &Device)) ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; 2009-1학기 컴퓨터게임(DirectX)
::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); 2009-1학기 컴퓨터게임(DirectX)