필요한 개념
이전 Projection은 3D 공간에서 2D공간으로 가는 거였음
Unprojection : 2D 공간에서 3D 공간으로 돌아오는
Raycast(Picking) ? 마우스의 위치(2D)를 이용하여 3D공간에서 선을 만들어 교차되는 삼각형을 구하는 방식
(반직선을 쏴서 선택하는 것)
즉 2D좌표를 3D좌표로 변환해줘야 한다.

2D공간을 3D공간으로 변환한 다음에
화면 상에 마우스가 있는 지점에 위치를 구해서 근면(nearPlane)에서 선이 나가고 해당 똑같이 먼면(FarPlane)에 비례가 되서 선이 도착한다.
이 선을 구하려고 하는것
이 선으로 충돌되는 삼각형이 있다면 그 삼각형의 위치(Picking Position)를 구해올 것이다.
근면에서 먼면까지의 방향을 만들고 이어지는 선으로 만들었을때 IntersectTri()에 넣어주면 해당 삼각형의 위치가 나오게 된다.
-> Unprojection
여기서의 삼각형은(모든 Mesh들이나, 지형은 삼각형으로 이루어져 있기때문에 가능)
Unprojection은 Projection의 정확히 반대로 계산
Projection : 3D -> W,V,P -> Vp -> 2D
Unprojection : 2D -> Vp -> P,V,W -> 3D
전체 삼각형을 for문을 돌며 체크해줘야 한다.
왼쪽 하단은 빠른데, 우측 상단은 느려짐
그래서 느려지기 때문에 초대형맵을 쓸때는 이 Picking방식이 아니라 화면공간에서 선택하는
ScreenSpacePicking 기법을 사용하면 된다.(수업에서 다루진 않음)
선의 시작 = 근면 마우스 위치, 선의 종료 = 원면 마우스 위치
선의 방향 : 선의 종료 - 선의 시작(두개를 빼주면 방향을 알아낼 수 있다.)
* 선을 DebugLine으로 그려도 보이지 않는다.(->카메라와 동일한면을 바라보고 있다면 보이지 않는다.)
이유는 정확히 카메라하고 선이 일치해서 보이지 않는다.
실행시켜보면 좌측 하단은 프레임이 2000정도 나오는데 지형 우 상단은 프레임이 100프레임정도 나옴
* 이유는 삼각형의 번호가 0에 가까운 쪽에서 교차되면 속도가 매우 빠르지만 아니라면 점점 느려진다.
교차가 되지 않는다면 전부 다 검사해야 교차 여부를 알 수 있으므로 가장 느리다.(반복문이 0부터 쭉 검사해서 그렇다.)
-> 그래서 지형을 나눈다던가, 화면 공간에서의 picking을 사용한다.
Viewport.h 추가된 내용
class Viewport
{
...
// Unprojection은 Projection의 정확히 반대로 계산
void Unproject(Vector3* pOut, Vector3& source, Matrix& W, Matrix& V, Matrix& P);
};
Viewport.cpp 추가된 내용
void Viewport::Unproject(Vector3* pOut, Vector3& source, Matrix& W, Matrix& V, Matrix& P)
{
// Unprojection은 Projection의 정확히 반대로 계산
// Projection : 3D -> W,V,P -> Vp -> 2D
// Unprojection : 2D -> Vp -> P,V,W -> 3D
Vector3 position = source;
// Viewport 변환
// 다시 NDC로 와야되기 때문에
// 다시 x 빼주고 / width 나눠준다.
// projection때는 반으로(0.5)로 줄였으니까 2.0f(2배) 곱해줌
// 더해준 1.0f를 다시 뺴줌
// 정확히 반대대로 해줌
pOut->x = ((position.x - x) / width) * 2.0f - 1.0f;
// 반대로 뒤집어야 하니까 * -1.0f
pOut->y = (((position.y - y) / height) * 2.0f - 1.0f) * -1.0f;
pOut->z = ((position.z - minDepth) / (maxDepth - minDepth));
// P,V,W 곱해서 역행렬 취하면 됨
Matrix wvp = W * V * P;
D3DXMatrixInverse(&wvp, NULL, &wvp);
D3DXVec3TransformCoord(pOut, pOut, &wvp);
}
Terrain.h 추가된 내용
class Terrain
{
...
// Raycast된 위치를 구해오는 함수
// Raycast : 반직선을 쏴서 선택하는 것(또 다른 말로 Picking이라고도함)
Vector3 GetRaycastPosition();
}
Terrain.cpp 추가된 내용
...
Vector3 Terrain::GetRaycastPosition()
{
// 어떤 삼각형이 광선(반직선)과 충돌될지를 모름
// 전체 삼각형을 for문을 돌며 체크해줘야 한다.
// 왼쪽 하단은 빠른데, 우측 상단은 느려짐
Matrix V = Context::Get()->View();
Matrix P = Context::Get()->Projection();
Viewport* vp = Context::Get()->GetViewport();
Vector3 mouse = Mouse::Get()->GetPosition();
// 선의 시작 = 근면 마우스 위치, 선의 종료 = 원면 마우스 위치
// 선의 방향 : 선의 종료 - 선의 시작(두개를 빼주면 방향을 알아낼 수 있다.)
// 근면에 대한 위치, 원면에 대한 위치 구함
Vector3 n, f;
// NDC로 정규화 되었다고 했을때 가장 가까운 면의 z은 0이고, 가장 먼면의 z은 1이다.
// NDC로 x,y는 -1 ~ 1까지 지만, z은 0 ~ 1까지 이다.
// z은 0 ~ 1까지인 이유? 뒤는 그릴 필요 없다.
// 그래서 근면일때 z은 0이다.(NDC일때)
mouse.z = 0.0f;
// 근면에 대한 위치를 구해옴
// 근면의 2D 위치가 3D 위치로 바뀌어서 near에 리턴된다.
vp->Unproject(&n, mouse, world, V, P);
mouse.z = 1.0f;
// 원면의 위치를 구해옴
vp->Unproject(&f, mouse, world, V, P);
Vector3 start = n;
Vector3 direction = f - n;
// 어떤 삼각형일지 모든 삼각형을 순회함
for (UINT z = 0; z < height - 1; z++)
{
for (UINT x = 0; x < width - 1; x++)
{
UINT index[4];
index[0] = width * z + x;
index[1] = width * (z + 1) + x;
index[2] = width * z + x + 1;
index[3] = width * (z + 1) + (x + 1);
// 위치를 구해옴
Vector3 p[4];
for (int i = 0; i < 4; i++)
p[i] = vertices[index[i]].Position;
float u, v, distance;
// 체크할 삼각형 3개, 반직선 방향
// U(ddx), V(ddz)가 리턴됨
// 왼쪽 삼각형 체크
if (D3DXIntersectTri(&p[0], &p[1], &p[2], &start, &direction, &u, &v, &distance) == TRUE)
return p[0] + (p[1] - p[0]) * u + (p[2] - p[0]) * v;
// 오른쪽 삼각형 체크
if (D3DXIntersectTri(&p[3], &p[1], &p[2], &start, &direction, &u, &v, &distance) == TRUE)
return p[3] + (p[1] - p[3]) * u + (p[2] - p[3]) * v;
}
}
// 충돌때 return이 안되었다면 어느 삼각형하고도 충돌된게 아니다.
// 검사가 안되었을 경우
return Vector3(-1, FLT_MIN, -1);
}
GetRaycastDemo.h
#pragma once
#include "Systems/IExecute.h"
class GetRaycastDemo : public IExecute
{
public:
virtual void Initialize() override;
virtual void Ready() override {};
virtual void Destroy() override;
virtual void Update() override;
virtual void PreRender() override {};
virtual void Render() override;
virtual void PostRender() override {};
virtual void ResizeScreen() override {};
private:
Shader* shader;
class Terrain* terrain;
};
GetRaycastDemo.cpp
#include "stdafx.h"
#include "GetRaycastDemo.h"
#include "Environment/Terrain.h"
void GetRaycastDemo::Initialize()
{
Context::Get()->GetCamera()->RotationDegree(12, 0, 0);
Context::Get()->GetCamera()->Position(35, 10, -55);
((Freedom*)Context::Get()->GetCamera())->Speed(20);
shader = new Shader(L"19_Terrain.fx");
terrain = new Terrain(shader, L"Terrain/google1.png");
terrain->Pass(1);
}
void GetRaycastDemo::Destroy()
{
SafeDelete(shader);
SafeDelete(terrain);
}
void GetRaycastDemo::Update()
{
// 위치만 구해와서 출력
Vector3 position = terrain->GetRaycastPosition();
string str = "";
str += to_string(position.x) + ", ";
str += to_string(position.y) + ", ";
str += to_string(position.z) + ", ";
Gui::Get()->RenderText(Vector2(10, 60), Color(1, 0, 0, 1), str);
terrain->Update();
}
void GetRaycastDemo::Render()
{
terrain->Render();
}
결과
'DirectX11 3D > 기본 문법' 카테고리의 다른 글
| <DirectX11 3D> 70 - Obb Collision(OBB와 OBB 충돌) (0) | 2022.03.02 |
|---|---|
| <DirectX11 3D> 69 - OBB Raycast(OBB와 반직선 교차) (0) | 2022.03.02 |
| <DirectX11 3D> 67 - Projection (0) | 2022.03.01 |
| <DirectX11 3D> 66 - Viewport (0) | 2022.03.01 |
| <DirectX11 3D> 63 - ComputeShader(CS)(3) - StructuredBuffer를 이용한 GetAnimationBone (0) | 2022.03.01 |