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");
}
- 이미지를 띄울땐 약간 이상하게 나오지만 그리드로 찍어보면 잘 나오는 것을 볼 수 있다
- 이미지가 이상하게 나오는 이유는 Shader에 SamplerState에 UV를 매핑할 때 UV좌표의 범위를 벗어났기 때문이다