[DirectX11] 024 Vertical Raycast
그리드의 크기가 일정하지 않는 경우. 두 지면의 크기가 다른 경우. 다음과 같은 지형들을 구현할 때 사용하는 방식에 대해 알아보자. 삼각형 충돌 방식. 실제 게임에서 이 방법이 더 많이 쓰인다.
목차
Vertical Raycast
수식 -> 반직선 삼각형 충돌
D3DXIntersection 사용
- start
- direction
- 두 요소가 필요하다.
- 삼각형의 위치를 구한다.
용어 정의
- Picking: 무언가를 선택하겠다. 여기서 무언가는 플레이어가 위치하고 있는 삼각형.
- Raycast: 반직선을 쏴서 선택했다
팁 TIP!
Window에서 대문자 TRUE, FALSE는 int형으로 1, 0 취급한다. 0이 아닌 값이 나왔을 때 왜 TRUE가 나왔는지 각각 파악하기 위해 0이 아닌 값을 TRUE를 만들어주는 대신에 1만 TRUE로 잡아준다. 예를 들어 3이란 값이 나오면 왜 TRUE인지 따로 처리해준다.
Terrain
float GetVerticalRaycast(Vector3& position); 생성
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);
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;
};
Terrain.cpp
더보기
#include "Framework.h"
#include "Terrain.h"
Terrain::Terrain(Shader * shader, wstring heightFile)
: shader(shader)
{
heightMap = new Texture(heightFile);
CreateVertexData();
CreateIndexData();
CreateNormalData();
CreateBuffer();
}
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);
Matrix world;
D3DXMatrixIdentity(&world);
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; //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;
}
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));
}
}
메인코드
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; //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;
}
Vertical Raycast
GetVerticalRaycast.h
더보기
#pragma once
#include "Systems/IExecute.h"
class GetVerticalRaycast : 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;
Vector3 position = Vector3(0, 0, 0);
Shader* triShader;
ID3D11Buffer* vertexBuffer;
};
GetVerticalRaycast.cpp
더보기
#include "stdafx.h"
#include "GetVerticalRaycast.h"
void GetVerticalRaycast::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);
triShader = new Shader(L"09_World.fx");
Vertex vertices[3];
vertices[0].Position = Vector3(+0, +1, 0);
vertices[1].Position = Vector3(-1, +0, 0);
vertices[2].Position = Vector3(+1, +0, 0);
//Create Vertex Buffer
{
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
desc.ByteWidth = sizeof(Vertex) * 3;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA subResource = { 0 };
subResource.pSysMem = vertices;
Check(D3D::GetDevice()->CreateBuffer(&desc, &subResource, &vertexBuffer));
}
}
void GetVerticalRaycast::Destroy()
{
SafeDelete(shader);
SafeDelete(terrain);
SafeDelete(triShader);
SafeRelease(vertexBuffer);
}
void GetVerticalRaycast::Update()
{
terrain->Update();
if (Keyboard::Get()->Press(VK_RIGHT))
position.x += 20.0f * Time::Delta();
else if (Keyboard::Get()->Press(VK_LEFT))
position.x -= 20.0f * Time::Delta();
if (Keyboard::Get()->Press(VK_UP))
position.z += 20.0f * Time::Delta();
else if (Keyboard::Get()->Press(VK_DOWN))
position.z -= 20.0f * Time::Delta();
//float Terrain::GetHeight()에서 return result.y값을 가져다쓴다.
//기준점인 vertices[0].Position = Vector3(0, 1, 0). 기준점 y값이 1이므로 1.0f를 더해준다.
position.y = terrain->GetHeight(position) + 1.0f;
Vector3 start(position.x, 50, position.z);
Vector3 end = Vector3(position.x, 0.0f, position.z);
DebugLine::Get()->RenderLine(start, end, Color(0, 1, 0, 1));
Matrix R, T;
D3DXMatrixRotationX(&R, Math::ToRadian(180));
D3DXMatrixTranslation(&T, position.x, position.y, position.z);
Matrix world = R * T;
triShader->AsMatrix("World")->SetMatrix(world);
triShader->AsMatrix("View")->SetMatrix(Context::Get()->View());
triShader->AsMatrix("Projection")->SetMatrix(Context::Get()->Projection());
}
void GetVerticalRaycast::Render()
{
terrain->Render();
UINT stride = sizeof(Vertex);
UINT offset = 0;
D3D::GetDC()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
D3D::GetDC()->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
triShader->Draw(0, 0, 3);
string str = to_string(position.x) + ", " + to_string(position.y) + ", " + to_string(position.z);
Gui::Get()->RenderText(5, 60, str);
}
추가부분
Vector3 start(position.x, 50, position.z);
Vector3 end = Vector3(position.x, 0.0f, position.z);
DebugLine::Get()->RenderLine(start, end, Color(0, 1, 0, 1));
실행화면
'⭐ DirectX > DirectX11 3D' 카테고리의 다른 글
[DirectX11] 027 Mesh Cube (0) | 2023.01.25 |
---|---|
[DirectX11] 025~26 Mesh (0) | 2023.01.21 |
[DirectX11] 023 Get Height (0) | 2023.01.20 |
[DirectX11] 020~22 Normal Vector (0) | 2023.01.18 |
[DirectX11] 019 Heightmap (0) | 2023.01.16 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 027 Mesh Cube
[DirectX11] 027 Mesh Cube
2023.01.25 -
[DirectX11] 025~26 Mesh
[DirectX11] 025~26 Mesh
2023.01.21 -
[DirectX11] 023 Get Height
[DirectX11] 023 Get Height
2023.01.20 -
[DirectX11] 020~22 Normal Vector
[DirectX11] 020~22 Normal Vector
2023.01.18