그리드의 크기가 일정하지 않는 경우. 두 지면의 크기가 다른 경우. 다음과 같은 지형들을 구현할 때 사용하는 방식에 대해 알아보자. 삼각형 충돌 방식. 실제 게임에서 이 방법이 더 많이 쓰인다.

 

목차

     

     


     

     

    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