Download presentation
Presentation is loading. Please wait.
1
제 15 장 픽킹
2
픽킹 스크린 좌표를 이용해 선택한 물체를 알아내는 테크닉
원점에서 시작하여 p를 통과하도록 광선을 발사하면 p를 둘러싼 투영을 가지는 물체와 교차 픽킹 광선을 계산한 다음에는 장면 내의 각 물체를 대상으로 광선과 교차하는지를 테스트 광선과 교차하는 물체가 바로 사용자가 선택한 물체임 픽킹은 모든 종류의 게임과 3D 애플리케이션에 적용 2009-1학기 컴퓨터게임(DirectX)
3
(그림1) p를 통과하도록 발사된 광선은 p를 둘러싸는 물체와 교차
투영창, Direct3D는 평면 z=1에 일치하도록 정의하고 있다 투영의 중심 (그림1) p를 통과하도록 발사된 광선은 p를 둘러싸는 물체와 교차 2009-1학기 컴퓨터게임(DirectX)
4
픽킹 구현 방법 스크린 포인트 s를 통해 투영창에 대응되는 포인트 p를 얻는다.
픽킹 광선과 모델을 동일한 공간으로 변환한다. 픽킹 광선과 교차하는 모델을 알아낸다. 교차된 물체는 선택된 스크린 물체와 대응된다. 2009-1학기 컴퓨터게임(DirectX)
5
스크린에서 투영창으로의 변환 Height Height Height 2009-1학기 컴퓨터게임(DirectX)
6
2009-1학기 컴퓨터게임(DirectX)
7
2009-1학기 컴퓨터게임(DirectX)
8
픽킹 광선의 계산 광선의 위치를 나타내는 원점이 p0이고 방향을 나타내는 벡터가 u라 하면, 인수 방정식 p(t)=p0+tu를 통해 광선을 표현할 수 있음 광선의 원점은 또한 뷰 스페이스의 원점임을 알 수 있으므로 p0=(0, 0, 0) 방향벡터 u = p – p0 =(px, py, 1)-(0, 0, 0)=p로 얻을 수 있음 2009-1학기 컴퓨터게임(DirectX)
9
d3d::Ray CalcPickingRay(int x, int y) { float px = 0.0f;
다음의 메서드는 스크린 스페이스 상의 클릭된 포인트의 x와 y 좌표를 이용해 픽킹 광선을 계산해 냄 d3d::Ray CalcPickingRay(int x, int y) { float px = 0.0f; float py = 0.0f; D3DVIEWPORT9 vp; Device->GetViewport(&vp); D3DXMATRIX proj; Device->GetTransform(D3DTS_PROJECTION, &proj); 2009-1학기 컴퓨터게임(DirectX)
10
px = ((( 2.0f*x) / vp.Width) - 1.0f) / proj(0, 0);
py = (((-2.0f*y) / vp.Height) + 1.0f) / proj(1, 1); d3d::Ray ray; ray._origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f); ray._direction = D3DXVECTOR3(px, py, 1.0f); return ray; } Where Ray is defined as: struct Ray { D3DXVECTOR3 _origin; D3DXVECTOR3 _direction; }; 2009-1학기 컴퓨터게임(DirectX)
11
광선의 변환 앞에서의 픽킹 광선은 뷰 스페이스에서 표현된 것이며,
광선-물체 교차 테스트를 위해서는 광선과 물체가 반드시 동일한 좌표 시스템 내에 위치해야 함 모든 물체를 뷰 스페이스로 변환하는 것보다는 월드 스페이스나 혹은 물체의 지역 스페이스로 픽킹 광선을 변환하는 것이 수월 변환 행렬을 이용해 광선 r(t)=p0+tu의 원점 p0과 방향 u를 변환할 수 있음. 다음과 같은 함수를 이용해 광선을 변환 2009-1학기 컴퓨터게임(DirectX)
12
void TransformRay(d3d::Ray* ray, D3DXMATRIX* T) {
// transform the ray's origin, w = 1. D3DXVec3TransformCoord( &ray->_origin, T); // transform the ray's direction, w = 0. D3DXVec3TransformNormal( &ray->_direction, 2009-1학기 컴퓨터게임(DirectX)
13
// normalize the direction
D3DXVec3Normalize(&ray->_direction, &ray->_direction); } 2009-1학기 컴퓨터게임(DirectX)
14
광선-물체 교차 동일한 좌표 시스템에 픽킹 광선과 물체가 준비되면 광선이 물체를 교차하는지를 테스트할 준비가 완료된 것
첫 번째 방법은 물체를 이루는 모든 삼각형들에 차례대로 광선 교차 테스트를 수행 하지만 장면의 모든 삼각형을 대상으로 교차 테스트를 수행한다는 것은 효율 면에서 그리 좋은 선택이 아님 다소 정확성이 떨어지기는 하지만 각 물체의 경계 구체를 이용하는 방법이 효율 면에서 좀더 유리. 2009-1학기 컴퓨터게임(DirectX)
15
(그림15.3) p-c로 구성된 벡터의 길이 ||p-c||는
구의 중심점 c와 반지름 r이 있다고 할 때 다음과 같은 암시적 방정식을 이용해 포인트 p를 테스트할 수 있음 ||p-c||-r=0 방정식이 만족되면 p가 구 내에 있음. +y c p-c p +x (그림15.3) p-c로 구성된 벡터의 길이 ||p-c||는 p가 구체 내에 있을 경우 구체의 반지름과 같음 2009-1학기 컴퓨터게임(DirectX)
16
광선과 구체의 교차 확인 광선 p(t) = p0 + tu가 구체와 교차하는지를 확인하기 위해서는 광선을 암시적 구방정식에 넣고 구 방정식을 만족시키는 인자 t를 풀어 교차점 (s)를 구해야 함 우선 구 방정식에 광선을 넣음 ||p(t) - c|| - r = 0 ||p0 + tu - c|| - r = 0 ||p0 + tu - c||2 – r2 = 0 u2 t2 + 2u(p0 – c)t + (p0 – c)2 – r2 = 0 2009-1학기 컴퓨터게임(DirectX)
17
A=u·u, B=2(u·(p0-c)), 그리고 C=(p0-c) ·(p0-c)-r2에서 u를 정규화할 경우 A=1임
여기에서 다음과 같은 2차 방정식을 얻음 At2+Bt+C=0 A=u·u, B=2(u·(p0-c)), 그리고 C=(p0-c) ·(p0-c)-r2에서 u를 정규화할 경우 A=1임 u가 정규화 되었다고 가정하면 t0과 t1을 풀어낼 수 있음 2009-1학기 컴퓨터게임(DirectX)
18
다음의 메서드는 전달된 광선이 전달된 구체를 교차할 경우 True를 리턴하며, 빗나갈 경우 False를 리턴
Bool PickApp::RaySphereIntersectionTest(Ray* ray, BoundingSphere* sphere) { D3DXVECTOR3 v = ray->_origin - sphere->_center; float b = 2.0f * D3DXVec3Dot(&ray->_direction, &v); float c = D3DXVec3Dot(&v, &v) - (sphere->_radius * sphere->_radius); // find the discriminant float discriminant = (b * b) - (4.0f * c); 2009-1학기 컴퓨터게임(DirectX)
19
// test for imaginary number if( discriminant < 0.0f )
return false; discriminant = sqrtf(discriminant); float s0 = (-b + discriminant) / 2.0f; float s1 = (-b - discriminant) / 2.0f; // if a solution is >= 0, then we intersected the sphere if( s0 >= 0.0f || s1 >= 0.0f ) return true; } 2009-1학기 컴퓨터게임(DirectX)
20
(a) (b) (c) (d) (e) 광선이 구체내부에 있음. 하나는 양수, 하나는 음수 광선이 구체와 접촉, t0=t1,
구체를 빗나감 광선이 구체내부에 있음. 하나는 양수, 하나는 음수 광선이 구체와 접촉, t0=t1, 해는 양수 광선이 구체를 교차 T0, t1 이 모두 양수 광선이 구체의 중심을 교차 t0, t1이 다 음수 2009-1학기 컴퓨터게임(DirectX)
21
예제 애플리케이션: 픽킹 주전자를 둘러싼 구를 마우스로 클릭할 수 있고, 만약 구체를 맞추면 이를 알려주는 메시지 상자가 열림
WM_LBUTTONDOWN 메시지를 이용해 마우스 클릭 이벤트를 처리 case WM_LBUTTONDOWN: // compute the ray in view space given the clicked screen point d3d::Ray ray = CalcPickingRay(LOWORD(lParam), HIWORD(lParam)); // transform the ray to world space D3DXMATRIX view; Device->GetTransform(D3DTS_VIEW, &view); 2009-1학기 컴퓨터게임(DirectX)
22
D3DXMATRIX viewInverse; D3DXMatrixInverse(&viewInverse, 0, &view);
TransformRay(&ray, &viewInverse); // test for a hit if( RaySphereIntTest(&ray, &BSphere) ) ::MessageBox(0, "Hit!", "HIT", 0); break; 2009-1학기 컴퓨터게임(DirectX)
23
2009-1학기 컴퓨터게임(DirectX)
24
예제 프로그램: 픽킹 #include "d3dUtility.h" IDirect3DDevice9* Device = 0;
const int Width = 640; const int Height = 480; ID3DXMesh* Teapot = 0; ID3DXMesh* Sphere = 0; D3DXMATRIX World; d3d::BoundingSphere BSphere; d3d::Ray CalcPickingRay(int x, int y) { float px = 0.0f; float py = 0.0f; 2009-1학기 컴퓨터게임(DirectX)
25
Device->GetViewport(&vp); D3DXMATRIX proj;
D3DVIEWPORT9 vp; Device->GetViewport(&vp); D3DXMATRIX proj; Device->GetTransform(D3DTS_PROJECTION, &proj); px = ((( 2.0f*x) / vp.Width) f) / proj(0, 0); py = (((-2.0f*y) / vp.Height) + 1.0f) / proj(1, 1); d3d::Ray ray; ray._origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f); ray._direction = D3DXVECTOR3(px, py, 1.0f); return ray; } 2009-1학기 컴퓨터게임(DirectX)
26
void TransformRay(d3d::Ray* ray, D3DXMATRIX* T) {
// transform the ray's origin, w = 1. D3DXVec3TransformCoord( &ray->_origin, T); // transform the ray's direction, w = 0. D3DXVec3TransformNormal( &ray->_direction, // normalize the direction D3DXVec3Normalize(&ray->_direction, &ray->_direction); } 2009-1학기 컴퓨터게임(DirectX)
27
bool RaySphereIntTest(d3d::Ray* ray, d3d::BoundingSphere* sphere) {
D3DXVECTOR3 v = ray->_origin - sphere->_center; float b = 2.0f * D3DXVec3Dot(&ray->_direction, &v); float c = D3DXVec3Dot(&v, &v) - (sphere->_radius * sphere->_radius); // find the discriminant float discriminant = (b * b) - (4.0f * c); // test for imaginary number if( discriminant < 0.0f ) return false; discriminant = sqrtf(discriminant); 2009-1학기 컴퓨터게임(DirectX)
28
float s0 = (-b + discriminant) / 2.0f;
// if a solution is >= 0, then we intersected the sphere if( s0 >= 0.0f || s1 >= 0.0f ) return true; return false; } bool Setup() { D3DXCreateTeapot(Device, &Teapot, 0); BYTE* v = 0; Teapot->LockVertexBuffer(0, (void**)&v); 2009-1학기 컴퓨터게임(DirectX)
29
D3DXComputeBoundingSphere( (D3DXVECTOR3*)v,
Teapot->GetNumVertices(), D3DXGetFVFVertexSize(Teapot->GetFVF()), &BSphere._center, &BSphere._radius); Teapot->UnlockVertexBuffer(); // Build a sphere mesh that describes the teapot's bounding sphere. D3DXCreateSphere(Device, BSphere._radius, 20, 20, &Sphere, 0); // Set light. D3DXVECTOR3 dir(0.707f, -0.0f, 0.707f); D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f); D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col); 2009-1학기 컴퓨터게임(DirectX)
30
Device->SetLight(0, &light); Device->LightEnable(0, true);
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true); Device->SetRenderState(D3DRS_SPECULARENABLE, false); // Set view matrix. D3DXVECTOR3 pos(0.0f, 0.0f, -10.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH(&V, &pos, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); // Set projection matrix. D3DXMATRIX proj; 2009-1학기 컴퓨터게임(DirectX)
31
D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI * 0.25f, // 45 - degree
(float)Width / (float)Height, 1.0f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj); return true; } void Cleanup() { d3d::Release<ID3DXMesh*>(Teapot); d3d::Release<ID3DXMesh*>(Sphere); 2009-1학기 컴퓨터게임(DirectX)
32
bool Display(float timeDelta) { if( Device ) // Update: Update Teapot.
static float r = 0.0f; static float v = 1.0f; static float angle = 0.0f; D3DXMatrixTranslation(&World, cosf(angle) * r, sinf(angle) * r, 10.0f); // transfrom the bounding sphere to match the teapots position in the // world. BSphere._center = D3DXVECTOR3(cosf(angle)*r, sinf(angle)*r, 10.0f); 2009-1학기 컴퓨터게임(DirectX)
33
v = -v; // reverse direction if( r <= 0.0f )
r += v * timeDelta; if( r >= 8.0f ) v = -v; // reverse direction if( r <= 0.0f ) angle += 1.0f * D3DX_PI * timeDelta; if( angle >= D3DX_PI * 2.0f ) angle = 0.0f; // Render Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); 2009-1학기 컴퓨터게임(DirectX)
34
Device->SetTransform(D3DTS_WORLD, &World);
// Render the teapot. Device->SetTransform(D3DTS_WORLD, &World); Device->SetMaterial(&d3d::YELLOW_MTRL); Teapot->DrawSubset(0); // Render the bounding sphere with alpha blending so we can see // through it. Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); D3DMATERIAL9 blue = d3d::BLUE_MTRL; blue.Diffuse.a = 0.25f; // 25% opacity Device->SetMaterial(&blue); Sphere->DrawSubset(0); 2009-1학기 컴퓨터게임(DirectX)
35
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; // WndProc LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) case WM_DESTROY: ::PostQuitMessage(0); break; 2009-1학기 컴퓨터게임(DirectX)
36
if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break;
case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break; case WM_LBUTTONDOWN: // compute the ray in view space given the clicked screen point d3d::Ray ray = CalcPickingRay(LOWORD(lParam), HIWORD(lParam)); // transform the ray to world space D3DXMATRIX view; Device->GetTransform(D3DTS_VIEW, &view); D3DXMATRIX viewInverse; D3DXMatrixInverse(&viewInverse, 0, &view); 2009-1학기 컴퓨터게임(DirectX)
37
TransformRay(&ray, &viewInverse); // test for a hit
if( RaySphereIntTest(&ray, &BSphere) ) ::MessageBox(0, "Hit!", "HIT", 0); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam); // WinMain int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { 2009-1학기 컴퓨터게임(DirectX)
38
if(!d3d::InitD3D(hinstance,
Width, Height, true, D3DDEVTYPE_HAL, &Device)) { ::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)
Similar presentations