본문 바로가기

DirectX11 3D/과제

<DirectX11 3D> Terrain을 Renderer로 상속

 

필요한 개념


- Redering 부분을 따로 Renderer 클래스로 처리해서 좀 더 코드가 간편해졌습니다.

 

- Renderer Class

https://cpplab.tistory.com/28

 

<DirectX11 3D> 32. Renderer

필요한 개념 - Renderer : 정점 버퍼, 인덱스 버퍼, Perframe, 한번에 묶어서 관리(+ Transform) Why? 그려질 친구들은 이 친구를 상속 받는다. - PerFrame https://cpplab.tistory.com/26 32. Renderer - PerFram..

cpplab.tistory.com

 

 

 

 

 

 

.h


더보기
#pragma once

class Terrain : public Renderer
{
public:
	typedef VertexNormal TerrainVertex;
public:
	Terrain(Shader* shader, wstring heightFile);
	~Terrain();

	void Update() override;
	void Render() override;

	float GetHeight(Vector3& position);
	float GetVerticalRaycast(Vector3& position);

private:
	void CreateVertexData();
	void CreateIndexData();
	void CreateNormalData();
	void CreateBuffer();

private:
	Texture* height_map;

	UINT width, height;

	UINT vertexCount;
	TerrainVertex* vertices;

	UINT indexCount;
	UINT* indices;
};

 

 

 

 

 

 

 

.Cpp


더보기
#include "Framework.h"
#include "Terrain.h"

Terrain::Terrain(Shader* shader, wstring heightFile) :
	Renderer(shader)
{
	height_map = new Texture(heightFile);

	CreateVertexData();
	CreateIndexData();
	CreateNormalData();

	CreateBuffer();
}

Terrain::~Terrain()
{
	SafeDelete(height_map);
	SafeDeleteArray(vertices);
	SafeDeleteArray(indices);
}

void Terrain::Update()
{
	static Vector3 direction = Vector3(-1, -1, 1);
	ImGui::SliderFloat3("Direction", direction, -1, 1);
	shader->AsVector("Direction")->SetFloatVector(direction);

	Super::Update();
}

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));
	//}

	Super::Render();
	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;

	// 50 = 지면 최대 높이 + 플레이어 사이즈
	Vector3 start(position.x, 50, position.z);
	Vector3 direction(0, -1, 0);

	float u, v, distance;
	Vector3 result(-1, FLT_MIN, -1);

	// 체크할 삼각형 3개, 반직선 방향
	// U(ddx), V(ddz)가 리턴됨
	// 왼쪽 삼각형 체크
	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;
	height_map->ReadPixel(DXGI_FORMAT_R8G8B8A8_UNORM, &heights);
	
	width = height_map->GetWidth();
	height = height_map->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;
			// 10은 작게 보이기 위해 오프셋
			vertices[index].Position.y = heights[pixel].r * 255.0f / 10;
			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++)
	{
		// 이렇게 하면 어느 삼각형이던 시작점 나옴(0, 1, 2, 2, 1, 3)
		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()
{
	vertexBuffer = new VertexBuffer(vertices, vertexCount, sizeof(TerrainVertex));
	indexBuffer = new IndexBuffer(indices, indexCount);
}

 

 

 

 

 

 

 

 

.fx


더보기
#include "00_Global.fx"

float3 Direction;

struct VertexOutput
{
    float4 Position : SV_Position;
    float3 Normal : Normal;
};

VertexOutput VS(VertexNormal input)
{
    VertexOutput output;
    output.Position = WorldPosition(input.Position);
    output.Position = ViewProjection(output.Position);
    
    output.Normal = WorldNormal(input.Normal);
    
    return output;
}

float4 PS(VertexOutput input) : SV_Target
{   
    float3 normal = normalize(input.Normal);
    // 뒤집는다.
    float3 light = -Direction;
    
    return float4(1, 1, 1, 1) * dot(light, normal);
}

technique11 T0
{
    P_VP(P0, VS, PS)
    P_RS_VP(P1, FillMode_WireFrame, VS, PS)
}

 

 

 

 

 

 

 

 

 

 

 

결과


기존이랑 같은 결과 입니다. 차이점은 Terrain class가 Renderer만 상속했습니다.