[DirectX11] 068 Unprojection - 2D 좌표를 3D 좌표로 변환
2D 공간을 3D 공간으로 변환한다. 가까운면(근면)부터 거리가 먼면(원면)까지 방향을 구한 다음, 그것을 이어주는 선을 TrytoIntercept에 넣어주면 해당 삼각형의 위치가 나온다. 마우스의 위치를 2D로 변환한 좌표를 활용하여 삼각형을 그려 3D 좌표로 변환한다.
목차
Unprojection - 2D 좌표를 3D 좌표로 변환
Shader | |
Framework | |
Environment | |
Terrain.h .cpp | |
Viewer | |
Viewport.h .cpp | |
UnitTest | |
Terrain | |
GetRaycastDemo.h .cpp 생성 | |
Main.h .cpp |
Terrain
Terrain.h
더보기
#pragma once
class Terrain
{
public:
typedef VertexNormal TerrainVertex;
public:
Terrain(Shader* shader, wstring heightFile);
~Terrain();
void Update();
void Render();
void Pass(UINT val) { pass = val; }
float GetHeight(Vector3& position);
float GetVerticalRaycast(Vector3& position);
Vector3 GetRaycastPosition(); //반직선을 쏴서 선택한다.
private:
void CreateVertexData();
void CreateIndexData();
void CreateNormalData();
void CreateBuffer();
private:
UINT pass = 0;
Shader* shader;
Texture* heightMap;
UINT width, height;
UINT vertexCount;
TerrainVertex* vertices;
ID3D11Buffer* vertexBuffer;
UINT indexCount;
UINT* indices;
ID3D11Buffer* indexBuffer;
Matrix world;
};
Vector3 GetRaycastPosition()
- 반직선을 쏴서 선택한다.
Terrain.cpp
더보기
#include "Framework.h"
#include "Terrain.h"
Terrain::Terrain(Shader * shader, wstring heightFile)
: shader(shader)
{
heightMap = new Texture(heightFile);
CreateVertexData();
CreateIndexData();
CreateNormalData();
CreateBuffer();
D3DXMatrixIdentity(&world);
}
Terrain::~Terrain()
{
SafeDelete(heightMap);
SafeDeleteArray(vertices);
SafeDeleteArray(indices);
SafeRelease(vertexBuffer);
SafeRelease(indexBuffer);
}
void Terrain::Update()
{
static Vector3 direction = Vector3(-1, -1, 1);
ImGui::SliderFloat3("Direction", direction, -1, 1);
shader->AsVector("Direction")->SetFloatVector(direction);
shader->AsMatrix("World")->SetMatrix(world);
shader->AsMatrix("View")->SetMatrix(Context::Get()->View());
shader->AsMatrix("Projection")->SetMatrix(Context::Get()->Projection());
}
void Terrain::Render()
{
//for (int i = 0; i < vertexCount; i++)
//{
// Vector3 start = vertices[i].Position;
// Vector3 end = vertices[i].Position + vertices[i].Normal * 2;
// DebugLine::Get()->RenderLine(start, end, Color(0, 1, 0, 1));
//}
UINT stride = sizeof(TerrainVertex);
UINT offset = 0;
D3D::GetDC()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
D3D::GetDC()->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
D3D::GetDC()->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0);
shader->DrawIndexed(0, pass, indexCount);
}
float Terrain::GetHeight(Vector3 & position)
{
UINT x = (UINT)position.x;
UINT z = (UINT)position.z;
if (x < 0 || x > width) return FLT_MIN;
if (z < 0 || z > height) return FLT_MIN;
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 v[4];
for (int i = 0; i < 4; i++)
v[i] = vertices[index[i]].Position;
float ddx = (position.x - v[0].x) / 1.0f;
float ddz = (position.z - v[0].z) / 1.0f;
Vector3 result;
if(ddx + ddz <= 1.0f)
result = v[0] + (v[2] - v[0]) * ddx + (v[1] - v[0]) * ddz;
else
{
ddx = 1.0f - ddx;
ddz = 1.0f - ddz;
result = v[3] + (v[1] - v[3]) * ddx + (v[2] - v[3]) * ddz;
}
return result.y;
}
float Terrain::GetVerticalRaycast(Vector3 & position)
{
UINT x = (UINT)position.x;
UINT z = (UINT)position.z;
if (x < 0 || x > width) return FLT_MIN;
if (z < 0 || z > height) return FLT_MIN;
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;
Vector3 start(position.x, 50, position.z);
Vector3 direction(0, -1, 0);
float u, v, distance;
Vector3 result(-1, FLT_MIN, -1);
if (D3DXIntersectTri(&p[0], &p[1], &p[2], &start, &direction, &u, &v, &distance) == TRUE)
result = 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)
result = p[3] + (p[1] - p[3]) * u + (p[2] - p[3]) * v;
return result.y;
}
Vector3 Terrain::GetRaycastPosition()
{
Matrix V = Context::Get()->View();
Matrix P = Context::Get()->Projection();
Viewport* vp = Context::Get()->GetViewport();
Vector3 mouse = Mouse::Get()->GetPosition();
//방향 = 원면에서의 마우스 위치 - 근면에서의 마우스 위치
//direction = far - near
Vector3 n, f; //near, far
mouse.z = 0.0f;
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; //far - near
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;
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 Vector3(-1, FLT_MIN, -1);
}
void Terrain::CreateVertexData()
{
vector<Color> heights;
heightMap->ReadPixel(DXGI_FORMAT_R8G8B8A8_UNORM, &heights);
width = heightMap->GetWidth();
height = heightMap->GetHeight();
vertexCount = width * height;
vertices = new TerrainVertex[vertexCount];
for (UINT z = 0; z < height; z++)
{
for (UINT x = 0; x < width; x++)
{
UINT index = width * z + x;
UINT pixel = width * (height - 1 - z) + x;
vertices[index].Position.x = (float)x;
vertices[index].Position.y = heights[pixel].r * 255.0f / 10.0f;
vertices[index].Position.z = (float)z;
}
}
}
void Terrain::CreateIndexData()
{
indexCount = (width - 1) * (height - 1) * 6;
indices = new UINT[indexCount];
UINT index = 0;
for (UINT y = 0; y < height - 1; y++)
{
for (UINT x = 0; x < width - 1; x++)
{
indices[index + 0] = width * y + x;
indices[index + 1] = width * (y + 1) + x;
indices[index + 2] = width * y + x + 1;
indices[index + 3] = width * y + x + 1;
indices[index + 4] = width * (y + 1) + x;
indices[index + 5] = width * (y + 1) + x + 1;
index += 6;
}
}
}
void Terrain::CreateNormalData()
{
for (UINT i = 0; i < indexCount / 3; i++)
{
UINT index0 = indices[i * 3 + 0];
UINT index1 = indices[i * 3 + 1];
UINT index2 = indices[i * 3 + 2];
TerrainVertex v0 = vertices[index0];
TerrainVertex v1 = vertices[index1];
TerrainVertex v2 = vertices[index2];
Vector3 a = v1.Position - v0.Position;
Vector3 b = v2.Position - v0.Position;
Vector3 normal;
D3DXVec3Cross(&normal, &a, &b);
vertices[index0].Normal += normal;
vertices[index1].Normal += normal;
vertices[index2].Normal += normal;
}
for (UINT i = 0; i < vertexCount; i++)
D3DXVec3Normalize(&vertices[i].Normal, &vertices[i].Normal);
}
void Terrain::CreateBuffer()
{
//Create Vertex Buffer
{
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
desc.ByteWidth = sizeof(TerrainVertex) * vertexCount;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA subResource = { 0 };
subResource.pSysMem = vertices;
Check(D3D::GetDevice()->CreateBuffer(&desc, &subResource, &vertexBuffer));
}
//Create Index Buffer
{
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
desc.ByteWidth = sizeof(UINT) * indexCount;
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
D3D11_SUBRESOURCE_DATA subResource = { 0 };
subResource.pSysMem = indices;
Check(D3D::GetDevice()->CreateBuffer(&desc, &subResource, &indexBuffer));
}
}
Vector3 Terrain::GetRaycastPosition()
- 반직선을 쏴서 선택한다.
- 방향 = 원면에서의 마우스 위치 - 근면에서의 마우스 위치
- direction = far - near
- Vector3 n, f;
Viewport
Viewport.h
더보기
#pragma once
class Viewport
{
public:
Viewport(float width, float height, float x = 0, float y = 0, float minDepth = 0, float maxDepth = 1);
~Viewport();
void RSSetViewport();
void Set(float width, float height, float x = 0, float y = 0, float minDepth = 0, float maxDepth = 1);
float GetWidth() { return width; }
float GetHeight() { return height; }
void Project(Vector3* pOut, Vector3& source, Matrix& W, Matrix& V, Matrix& P);
void Unproject(Vector3* pOut, Vector3& source, Matrix& W, Matrix& V, Matrix& P);
private:
float x, y;
float width, height;
float minDepth, maxDepth;
D3D11_VIEWPORT viewport;
};
Viewport.cpp
더보기
#include "Framework.h"
#include "Viewport.h"
Viewport::Viewport(float width, float height, float x, float y, float minDepth, float maxDepth)
{
Set(width, height, x, y, minDepth, maxDepth);
}
Viewport::~Viewport()
{
}
void Viewport::RSSetViewport()
{
D3D::GetDC()->RSSetViewports(1, &viewport);
}
void Viewport::Set(float width, float height, float x, float y, float minDepth, float maxDepth)
{
viewport.TopLeftX = this->x = x;
viewport.TopLeftY = this->y = y;
viewport.Width = this->width = width;
viewport.Height = this->height = height;
viewport.MinDepth = this->minDepth = minDepth;
viewport.MaxDepth = this->maxDepth = maxDepth;
RSSetViewport();
}
void Viewport::Project(Vector3 * pOut, Vector3 & source, Matrix & W, Matrix & V, Matrix & P)
{
// W -> V -> P -> Vp
Vector3 position = source;
Matrix wvp = W * V * P;
D3DXVec3TransformCoord(pOut, &position, &wvp);
pOut->x = ((pOut->x + 1.0f) * 0.5f) * width + x;
pOut->y = ((-pOut->y + 1.0f) * 0.5f) * height + y;
pOut->z = (pOut->z * (maxDepth - minDepth)) + minDepth;
}
void Viewport::Unproject(Vector3 * pOut, Vector3 & source, Matrix & W, Matrix & V, Matrix & P)
{
// Viewport(Vp) -> Projection(P) -> View(V) -> World(W)
Vector3 position = source;
//화면공간에서 3D(NDC)공간으로 변환 //위의 Projection에서 한 좌표변환을 되돌려준다고 생각하면 된다.
pOut->x = ((position.x - x) / width) * 2.0f - 1.0f;
pOut->y = (((position.y - y) / height) * 2.0f - 1.0f) * -1.0f;
pOut->z = ((position.z - minDepth) / (maxDepth - minDepth));
Matrix wvp = W * V * P;
D3DXMatrixInverse(&wvp, NULL, &wvp); //역행렬
D3DXVec3TransformCoord(pOut, pOut, &wvp);
}
void Viewport::Unproject
- Viewport(Vp) -> Projection(P) -> View(V) -> World(W) 과정을 거친다.
- 화면공간에서 3D(NDC)공간으로 변환한다.
- Projection에서 한 좌표변환을 되돌려준다고 생각하면 된다.
GetRaycastDemo
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;
Terrain* terrain;
};
GetRaycastDemo.cpp
더보기
#include "stdafx.h"
#include "GetRaycastDemo.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/Gray256.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();
}
실행화면
'⭐ DirectX > DirectX11 3D' 카테고리의 다른 글
[DirectX11] 070 OBB(Oriented Bounding Box) collision (3) | 2023.03.14 |
---|---|
[DirectX11] 069 Collider (0) | 2023.03.13 |
[DirectX11] 067 Projection (0) | 2023.03.09 |
[DirectX11] 066 3D 공간에서 충돌체 선택하기 (0) | 2023.03.08 |
[DirectX11] 064~65 Bone 위치에 충돌체 삽입하기 (0) | 2023.03.06 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 070 OBB(Oriented Bounding Box) collision
[DirectX11] 070 OBB(Oriented Bounding Box) collision
2023.03.14 -
[DirectX11] 069 Collider
[DirectX11] 069 Collider
2023.03.13 -
[DirectX11] 067 Projection
[DirectX11] 067 Projection
2023.03.09 -
[DirectX11] 066 3D 공간에서 충돌체 선택하기
[DirectX11] 066 3D 공간에서 충돌체 선택하기
2023.03.08