폴리곤 메쉬(polygon mesh)
이러한 객체는 여러 기법으로 구성될 수 있지만, 게임과 같은 실시간으로 처리되는 물체들은 거의 대부분 폴리곤(polygon) 모델링 기법을 사용한다.(GPU가 폴리곤 메쉬 처리에 최적화되어 있기 때문) 폴리곤의 원래 뜻은 다각형이지만 컴퓨터 그래픽에선 주로 삼각형을 의미한다. 그리고 다음과 같이 여러개의 폴리곤으로 구성된 하나의 객체를 폴리곤 메쉬라고 한다. 그림에서 볼 수 있듯이 더 많은 폴리곤을 사용할 수록 물체를 더욱 정교하게 표현할 수 있다. 이러한 폴리곤 메쉬는 정확한 곡면이 아닌 곡면에 근사한 표현이라는 것을 알아두자.
폴리곤 메쉬를 표현하는 방법
3ds Max, Maya와 같은 프로그램을 통해 그래픽 아티스트가 만든 폴리곤 메쉬를 파일로서 저장하면 게임의 입력으로 사용할 수 있다. 이러한 폴리곤 메쉬를 컴퓨터 프로그램에선 정점(vertex)의 집합으로 표현한다. 예를 들어 폴리곤이 1개면 정점(꼭지점)이 3개이므로 3개의 점을 저장함으로써 폴리곤을 표현한다. 이처럼 정점을 저장하는 공간을 Direct3D에선 정점 버퍼라고 한다. 다음은 폴리곤 3개로 구성된 폴리곤 메쉬와 그에 대응하는 정점 버퍼이다.
그런데 위 정점 버퍼엔 비효율적인 부분이 존재한다. 삼각형이 3개이기 때문에 총 9개의 정점이 존재하는 것은 맞지만 삼각형이 서로 접해있기 때문에 중복되는 정점이 존재한다. 이렇게 중복이 되더라도 하나의 정점 버퍼로 각 삼각형을 표현하는 방법을 삼각형 리스트(triangle list)라고 한다. 삼각형 리스트는 간단하게 연속으로 3개의 정점을 읽어 하나의 삼각형을 구성할 수 있다는 장점이 있지만 중복되는 정점들에 의해 메모리가 낭비된다는 단점이 있다.
이러한 메모리 낭비를 막기 위해 2가지의 버퍼를 사용하는 방법이 있다. 바로 다음과 같이 정점 버퍼와 인덱스 버퍼를 이용하여 중복되는 정점 저장을 막는다. 이러한 방식을 인덱스 삼각형 리스트(indexed triangle list) 라고 한다. 우선 정점 버퍼에 unique(유일)한 정점을 모두 저장한다. 그리고 인덱스 버퍼엔 정점 버퍼의 인덱스를 이용하여 삼각형의 세 정점을 연속으로 저장한다. 버퍼가 2개라 오히려 더 많은 메모리를 사용하는 것이 아니냐고 생각할 수 있지만, 정점은 부동 소수점을 이용하고 실제론 2차원 좌표가 아닌 3차원 좌표를 저장하게 된다. 그에 비해 인덱스 버퍼의 각 요소는 정수 값이기 때문에 실제로 더 적은 메모리 공간을 사용하는 것이 맞다. 그리고 정점 버퍼엔 실제로 정점만 저장되는 것은 아니다. 정점 외에도 vertex normal(정점의 법선 벡터), 텍스처 좌표 등 다양한 데이터가 저장된다.
또 다른 방법으로 삼각형 스트립(triangle strip)이 있다. 삼각형 스트립은 다음과 같이 정점 버퍼 하나만 이용한다. 인덱스 버퍼를 사용하지 않는 삼각형 리스트와 다른 점은 중복되는 정점을 저장하지 않는다는 점이다. 우선 t1 삼각형을 렌더링하기 위해 첫 세 정점을 처리 후 캐시에 저장한다. t2를 처리하기 위해 첫 두 정점은 캐시에서 읽고 마지막 정점만 정점 버퍼에서 가져와 처리한다. 이를 위해선 정점을 저장하는 순서가 중요하다. 인덱스 삼각형 리스트와 삼각형 스트립이 가장 많이 사용된다.
앞서 설명한 내용에 맞추어 이전에 구현한 vertex buffer, index buffer 클래스를 Mesh클래스 안에 감싸서 구현했다. 또한 정점정보를 어떻게 그릴것인지에 대한 primitive topology 정보도 추가 해주었다.
또한 정점 데이터와 인덱스 데이터를 따로 저장해서 보관해두었다. 나중에 수정하여 사용가능할 수도 있기 때문이다.
#pragma once
#include "yaResource.h"
#include "yaVertexBuffer.h"
#include "yaIndexBuffer.h"
namespace ya
{
class Mesh : public Resource
{
public:
struct Data
{
Data();
~Data();
D3D11_PRIMITIVE_TOPOLOGY mTopology;
std::vector<graphics::Vertex> vertices;
std::vector<UINT> indices;
};
Mesh();
~Mesh();
virtual HRESULT Save(const std::wstring& path) override;
virtual HRESULT Load(const std::wstring& path) override;
bool CreateVB(const std::vector<graphics::Vertex>& vertices);
bool CreateIB(const std::vector<UINT>& indices);
void Bind();
private:
graphics::VertexBuffer mVB;
graphics::IndexBuffer mIB;
Data mData;
};
}
C++
복사
Bind에는 정점, 인덱스, 프리미티브 토폴로지 3가지 정보를 파이프라인에 묶을수 있도록 설계되었다.
#include "yaMesh.h"
namespace ya
{
Mesh::Data::Data()
: mTopology(D3D11_PRIMITIVE_TOPOLOGY::D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
, vertices{}
, indices{}
{
}
Mesh::Data::~Data()
{
}
Mesh::Mesh()
: Resource(enums::eResourceType::Mesh)
{
}
Mesh::~Mesh()
{
}
HRESULT Mesh::Save(const std::wstring& path)
{
return S_OK;
}
HRESULT Mesh::Load(const std::wstring& path)
{
return S_OK;
}
bool Mesh::CreateVB(const std::vector<graphics::Vertex>& vertices)
{
mData.vertices = vertices;
return mVB.Create(vertices);
}
bool Mesh::CreateIB(const std::vector<UINT>& indices)
{
mData.indices = indices;
return mIB.Create(indices);
}
void Mesh::Bind()
{
mVB.Bind();
mIB.Bind();
graphics::GetDevice()->BindPrimitiveTopology(mData.mTopology);
}
}
C++
복사