38 Geometry

gksrudtlr
|2025. 3. 10. 21:02
void SamplerDemo::Init()
{
	shader = make_shared<Shader> (L"05.Texture.fx");

	geometry = make_shared<Geometry<VertexTextureData>>();
	//GeometryHelper::CreateQuad(geometry);
	//GeometryHelper::CreateCube(geometry);
	//GeometryHelper::CreateSphere(geometry);
	GeometryHelper::CreateGrid(geometry,10,10);

	vertexBuffer = make_shared<VertexBuffer>();
	vertexBuffer->Create(geometry->GetVertices());

	indexBuffer = make_shared<IndexBuffer>();
	indexBuffer->Create(geometry->GetIndices());

	m_pCamera = make_shared<GameObject>();
	m_pCamera->GetOrAddTransform();
	m_pCamera->AddComponent(make_shared<Camera>());
	m_pCamera->AddComponent(make_shared<CameraScript>());
	m_pCamera->GetTransform()->SetPosition(Vec3(0, 0, -10));

	m_pTexture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.JPG");
}

큐브 만들기

  • 큐브를 그리기 위해서는 정점이 최소 8개가 필요하다(Index Buffer를 사용하기 때문)

  • 하지만 UV 매핑을 통해서 각 면에 위의 이미지처럼 붙여 넣을 것이다

  • 이때 UV좌표를 위의 사진처럼 사용하고 이미지를 붙이게 되면 내가 바라보고 있는 방향은 이미지가 원하는 방향으로 들어갈 것이지만 다른 면들은 이미지가 뒤집히는 등 원하지 않는 방향으로 이미지가 붙게될 것이다
  • 그래서 UV좌표를 우리가 원하는 형태로 매핑을 하고싶다면 각 면마다 사각형을 그려 그에 맞도록 매핑을 해야하므로 정점이 24개가 필요하게 된다
void GeometryHelper::CreateCube(shared_ptr<Geometry<VertexTextureData>> geometry)
{
	
	float w2 = 0.5f;
	float h2 = 0.5f;
	float d2 = 0.5f;

	vector<VertexTextureData> vtx(24);

	// 앞면
	vtx[0] = VertexTextureData{Vec3(-w2, -h2, -d2), Vec2(0.0f, 1.0f)};
	vtx[1] = VertexTextureData{Vec3(-w2, +h2, -d2), Vec2(0.0f, 0.0f) };
	vtx[2] = VertexTextureData{Vec3(+w2, +h2, -d2), Vec2(1.0f, 0.0f)};
	vtx[3] = VertexTextureData{Vec3(+w2, -h2, -d2), Vec2(1.0f, 1.0f)};
	// 뒷면
	vtx[4] = VertexTextureData{Vec3(-w2, -h2, +d2), Vec2(1.0f, 1.0f)};
	vtx[5] = VertexTextureData{Vec3(+w2, -h2, +d2), Vec2(0.0f, 1.0f)};
	vtx[6] = VertexTextureData{Vec3(+w2, +h2, +d2), Vec2(0.0f, 0.0f)};
	vtx[7] = VertexTextureData{Vec3(-w2, +h2, +d2), Vec2(1.0f, 0.0f)};
	// 윗면
	vtx[8] = VertexTextureData{Vec3(-w2, +h2, -d2), Vec2(0.0f, 1.0f)};
	vtx[9] = VertexTextureData{Vec3(-w2, +h2, +d2), Vec2(0.0f, 0.0f)};
	vtx[10] = VertexTextureData{Vec3(+w2, +h2, +d2), Vec2(1.0f, 0.0f)};
	vtx[11] = VertexTextureData{Vec3(+w2, +h2, -d2), Vec2(1.0f, 1.0f)};
	// 아랫면
	vtx[12] = VertexTextureData{Vec3(-w2, -h2, -d2), Vec2(1.0f, 1.0f)};
	vtx[13] = VertexTextureData{Vec3(+w2, -h2, -d2), Vec2(0.0f, 1.0f)};
	vtx[14] = VertexTextureData{Vec3(+w2, -h2, +d2), Vec2(0.0f, 0.0f)};
	vtx[15] = VertexTextureData{Vec3(-w2, -h2, +d2), Vec2(1.0f, 0.0f)};
	// 왼쪽면
	vtx[16] = VertexTextureData{Vec3(-w2, -h2, +d2), Vec2(0.0f, 1.0f)};
	vtx[17] = VertexTextureData{Vec3(-w2, +h2, +d2), Vec2(0.0f, 0.0f)};
	vtx[18] = VertexTextureData{Vec3(-w2, +h2, -d2), Vec2(1.0f, 0.0f)};
	vtx[19] = VertexTextureData{Vec3(-w2, -h2, -d2), Vec2(1.0f, 1.0f)};
	// 오른쪽면
	vtx[20] = VertexTextureData{Vec3(+w2, -h2, -d2), Vec2(0.0f, 1.0f)};
	vtx[21] = VertexTextureData{Vec3(+w2, +h2, -d2), Vec2(0.0f, 0.0f)};
	vtx[22] = VertexTextureData{Vec3(+w2, +h2, +d2), Vec2(1.0f, 0.0f)};
	vtx[23] = VertexTextureData{Vec3(+w2, -h2, +d2), Vec2(1.0f, 1.0f)};

	geometry->SetVertices(vtx);

	vector<uint32> idx(36);

	// 앞면
	idx[0] = 0; idx[1] = 1; idx[2] = 2;
	idx[3] = 0; idx[4] = 2; idx[5] = 3;
	// 뒷면
	idx[6] = 4; idx[7] = 5; idx[8] = 6;
	idx[9] = 4; idx[10] = 6; idx[11] = 7;
	// 윗면
	idx[12] = 8; idx[13] = 9; idx[14] = 10;
	idx[15] = 8; idx[16] = 10; idx[17] = 11;
	// 아랫면
	idx[18] = 12; idx[19] = 13; idx[20] = 14;
	idx[21] = 12; idx[22] = 14; idx[23] = 15;
	// 왼쪽면
	idx[24] = 16; idx[25] = 17; idx[26] = 18;
	idx[27] = 16; idx[28] = 18; idx[29] = 19;
	// 오른쪽면
	idx[30] = 20; idx[31] = 21; idx[32] = 22;
	idx[33] = 20; idx[34] = 22; idx[35] = 23;

	geometry->SetIndices(idx);
}
  • 육면체의 원하는 가로, 세로, 높이를 변수로 설정하고 정점의 정보를 저장할 컨테이너 vector를 만들어 준다
  • 이 정점들의 정보는 VertexTextureData 구조체로 이루어진 값을 저장할 것인데 정점의 위치와 UV좌표 값을 저장해 준다
  • 정점의 정보를 모두 저장했으면 geometry에 정점 정보를 저장할 수 있도록 넘겨준뒤, Index를 만들어 그릴 순서를 저장해준다
  • 그 후 geometry에 index 정보를 넘겨주어 저장하도록 한다
void SamplerDemo::Init()
{
	shader = make_shared<Shader> (L"05.Texture.fx");

	geometry = make_shared<Geometry<VertexTextureData>>();
	//GeometryHelper::CreateQuad(geometry);
	GeometryHelper::CreateCube(geometry);

	vertexBuffer = make_shared<VertexBuffer>();
	vertexBuffer->Create(geometry->GetVertices());

	indexBuffer = make_shared<IndexBuffer>();
	indexBuffer->Create(geometry->GetIndices());

	m_pCamera = make_shared<GameObject>();
	m_pCamera->GetOrAddTransform();
	m_pCamera->AddComponent(make_shared<Camera>());
	m_pCamera->AddComponent(make_shared<CameraScript>());
	m_pCamera->GetTransform()->SetPosition(Vec3(0, 0, -10));

	m_pTexture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\IMG_0463(1).JPG");
}
  • SimplerDemo.cpp에 Init함수에 GeometryHelper에 CreateQuad를 CreateCube로 변경해준뒤 실행을 해보자

  • 베이가 사진이 찍힌 큐브가 원하는 방향으로 잘 나오는 것을 볼 수 있다

구체 만들기

  • 구체를 만들기 위해서는 그냥 원을 그리고 끝나는 것이 아니다
  • 원을 그린 후 기준점을 잡아 몇개의 원을 이용하여 그릴지 정하고, 그 원을 몇개의 삼각형으로 이어 분할해서 그릴지 정한 후 원을 그린다
  • 삼각형과 원들의 갯수가 많아질수록 더 정확한 구에 가까워지지만 그만큼 연산량이 많아지게 된다
  • 이는 삼각함수등 수학 공식을 이용하여 연산을 해주는데, 이 코드는 보통 DX 책에 나와있는 것을 많이 사용한다
void GeometryHelper::CreateSphere(shared_ptr<Geometry<VertexTextureData>> geometry)
{
	float radius = 0.5f; // 구의 반지름
	uint32 stackCount = 10; // 가로 분할
	uint32 sliceCount = 10; // 세로 분할

	vector<VertexTextureData> vtx;

	VertexTextureData v;

	// 북극
	v.position = Vec3(0.0f, radius, 0.0f);
	v.uv = Vec2(0.5f, 0.0f);
	vtx.push_back(v);

	float stackAngle = XM_PI / stackCount;
	float sliceAngle = XM_2PI / sliceCount;

	float deltaU = 1.f / static_cast<float>(sliceCount);
	float deltaV = 1.f / static_cast<float>(stackCount);

	// 고리마다 돌면서 정점을 계산한다 (북극/남극 단일점은 고리가 X)
	for (uint32 y = 1; y <= stackCount - 1; ++y)
	{
		float phi = y * stackAngle;

		// 고리에 위치한 정점
		for (uint32 x = 0; x <= sliceCount; ++x)
		{
			float theta = x * sliceAngle;

			v.position.x = radius * sinf(phi) * cosf(theta);
			v.position.y = radius * cosf(phi);
			v.position.z = radius * sinf(phi) * sinf(theta);

			v.uv = Vec2(deltaU * x, deltaV * y);

			vtx.push_back(v);
		}
	}

	// 남극
	v.position = Vec3(0.0f, -radius, 0.0f);
	v.uv = Vec2(0.5f, 1.0f);
	vtx.push_back(v);

	geometry->SetVertices(vtx);

	vector<uint32> idx(36);

	// 북극 인덱스
	for (uint32 i = 0; i <= sliceCount; ++i)
	{
		//  [0]
		//   |  \
		//  [i+1]-[i+2]
		idx.push_back(0);
		idx.push_back(i + 2);
		idx.push_back(i + 1);
	}

	// 몸통 인덱스
	uint32 ringVertexCount = sliceCount + 1;
	for (uint32 y = 0; y < stackCount - 2; ++y)
	{
		for (uint32 x = 0; x < sliceCount; ++x)
		{
			//  [y, x]-[y, x+1]
			//  |		/
			//  [y+1, x]
			idx.push_back(1 + (y)*ringVertexCount + (x));
			idx.push_back(1 + (y)*ringVertexCount + (x + 1));
			idx.push_back(1 + (y + 1) * ringVertexCount + (x));
			//		 [y, x+1]
			//		 /	  |
			//  [y+1, x]-[y+1, x+1]
			idx.push_back(1 + (y + 1) * ringVertexCount + (x));
			idx.push_back(1 + (y)*ringVertexCount + (x + 1));
			idx.push_back(1 + (y + 1) * ringVertexCount + (x + 1));
		}
	}

	// 남극 인덱스
	uint32 bottomIndex = static_cast<uint32>(vtx.size()) - 1;
	uint32 lastRingStartIndex = bottomIndex - ringVertexCount;
	for (uint32 i = 0; i < sliceCount; ++i)
	{
		//  [last+i]-[last+i+1]
		//  |      /
		//  [bottom]
		idx.push_back(bottomIndex);
		idx.push_back(lastRingStartIndex + i);
		idx.push_back(lastRingStartIndex + i + 1);
	}

	geometry->SetIndices(idx);
}
  • 그리기 위한 구의 반지름, 얼만큼의 삼각형으로 수평, 수직을 나눌지 설정한다
  • 정점을 북극점부터 추가한 후 구 본체를 위한 정점들을 계산한뒤, 남극점을 추가해준다
  • 마지막으로 index를 북극점 부분부터 첫번째 고리의 정점들을 연결하는삼각형, 첫번째 고리와 다음 고리를 연결하는 정점 순으로 이어주다가 남극점과 마지막 고리의 정점을 연결한다
void SamplerDemo::Init()
{
	shader = make_shared<Shader> (L"05.Texture.fx");

	geometry = make_shared<Geometry<VertexTextureData>>();
	//GeometryHelper::CreateQuad(geometry);
	//GeometryHelper::CreateCube(geometry);
	GeometryHelper::CreateSphere(geometry);

	vertexBuffer = make_shared<VertexBuffer>();
	vertexBuffer->Create(geometry->GetVertices());

	indexBuffer = make_shared<IndexBuffer>();
	indexBuffer->Create(geometry->GetIndices());

	m_pCamera = make_shared<GameObject>();
	m_pCamera->GetOrAddTransform();
	m_pCamera->AddComponent(make_shared<Camera>());
	m_pCamera->AddComponent(make_shared<CameraScript>());
	m_pCamera->GetTransform()->SetPosition(Vec3(0, 0, -10));

	m_pTexture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.JPG");
}
  • SamplerDamo에 Init 함수에서 GeometryHelpler를 CreateSphere로 변경하면 구체가 나오는 것을 볼 수 있다

  • 이때 구가 그려진 것을 확인하기 위해 Shafer에서 만들어뒀던 와이어프레임 모드로 변경하기 위해 Render 함수에서 Pass를 1로 변경해주면 구가 어떻게 그려졌는지 확인할 수 있다

그리드 만들기

  • 그리드는 무수히 많은 격자들을 그려 지형등에 이용한다
  • 이 그리드의 높낮이를 이용해 산, 언덕, 평지 등을 만들어 내는 것이다
  • 가로 세로의 크기를 지정해, 작은 사각형을 몇개로 채워 그릴지 - 즉 삼각형 두개를 합친 사각형을 몇개로 만들어 그릴지에 따라 연산이 달라질 것이다

  • 사각형의 한 변을 1로 생각했을 때 그리드의 크기를 10이라 한다면 총 10개의 사각형이 그려질 것이다
  • 이때 필요한 정점의 갯수는 그리드의 크기 + 1을 갖게 될 것이다
  • 세로도 마찬가지일 것이다
  • 이렇게 여러개의 사각형을 삼각형으로 쪼개 연결해주면 될것이다
void GeometryHelper::CreateGrid(shared_ptr<Geometry<VertexTextureData>> geometry, int32 sizeX, int32 sizeZ)
{
	vector<VertexTextureData> vtx;

	for (int32 z = 0; z < sizeZ + 1; z++)
	{
		for (int32 x = 0; x < sizeX + 1; x++)
		{
			VertexTextureData v;
			v.position = Vec3(static_cast<float>(x), 0, static_cast<float>(z));
			v.uv = Vec2(static_cast<float>(x), static_cast<float>(z));

			vtx.push_back(v);
		}
	}

	geometry->SetVertices(vtx);

	vector<uint32> idx;

	for (int32 z = 0; z < sizeZ; z++)
	{
		for (int32 x = 0; x < sizeX; x++)
		{
			//  [0]
			//   |	\
			//  [2] - [1]
			idx.push_back((sizeX + 1) * (z + 1) + (x));
			idx.push_back((sizeX + 1) * (z)+(x + 1));
			idx.push_back((sizeX + 1) * (z)+(x));
			//  [1] - [2]
			//   	\  |
			//		  [0]
			idx.push_back((sizeX + 1) * (z)+(x + 1));
			idx.push_back((sizeX + 1) * (z + 1) + (x));
			idx.push_back((sizeX + 1) * (z + 1) + (x + 1));
		}
	}

	geometry->SetIndices(idx);
}
  • 가로세로 크기보다 +1한 만큼 정점을 만들어 정점 정보들을 저장해준다
  • 그 후 geometry에 정점 정보를 넘겨 저장해준다
  • 그 후 index 정보를 만들것인데 이번에는 가로세로 크기의 +1한 값을 사용하지 않고 가로세로 만큼만 사용해 삼각형을 그릴 순서의 정보를 정점을 저장한 순서대로 저장해준다
  • 역시 geometry에 index정보를 넘겨 저장해준다
void SamplerDemo::Init()
{
	shader = make_shared<Shader> (L"05.Texture.fx");

	geometry = make_shared<Geometry<VertexTextureData>>();
	//GeometryHelper::CreateQuad(geometry);
	//GeometryHelper::CreateCube(geometry);
	//GeometryHelper::CreateSphere(geometry);
	GeometryHelper::CreateGrid(geometry,10,10);

	vertexBuffer = make_shared<VertexBuffer>();
	vertexBuffer->Create(geometry->GetVertices());

	indexBuffer = make_shared<IndexBuffer>();
	indexBuffer->Create(geometry->GetIndices());

	m_pCamera = make_shared<GameObject>();
	m_pCamera->GetOrAddTransform();
	m_pCamera->AddComponent(make_shared<Camera>());
	m_pCamera->AddComponent(make_shared<CameraScript>());
	m_pCamera->GetTransform()->SetPosition(Vec3(0, 0, -10));

	m_pTexture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.JPG");
}
  • CreateGrid로 변경해서 실행해보자

  • 이미지를 띄울땐 약간 이상하게 나오지만 그리드로 찍어보면 잘 나오는 것을 볼 수 있다
  • 이미지가 이상하게 나오는 이유는 Shader에 SamplerState에 UV를 매핑할 때 UV좌표의 범위를 벗어났기 때문이다

'DirectX' 카테고리의 다른 글

37 Texture  (0) 2025.01.20
36 Camera  (0) 2025.01.20
35 Constant Buffer  (0) 2025.01.16
34 사각형 띄우기  (0) 2025.01.16
33 새 프로젝트 시작  (0) 2025.01.16