Cube, Sphere, Grid도 만들어 보자.
1. Cube 그리기
Cube를 전략적으로 하나씩 생각해 보자.
정점이 몇 개 필요할까?
단순히 색상만 표현하려면 8개로 충분하다.
근데 UV 매핑까지 할 것이다.
면마다 하나씩 그림을 붙이는 걸 목표로 한다.
그런 경우는 점이 8면 uv 좌표를 세팅하게 될 텐데
정점을 줄일 수 없고, 4개씩 6면체에 그릴 수밖에 없다.
즉 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);
}
정점 24개를 만들어서 각각의 uv 좌표들을 연결하는 것이다.
TexutureDemo::Init에서
//GeometryHelper::CreateQuad(_geometry);
GeometryHelper::CreateCube(_geometry);
이렇게 해주고
실행을 하고 옆으로 가서 보면
큐브 모양을 확인할 수 있다.
2. Sphere 그리기
Sphere부터 어려워진다.
void GeometryHelper::CreateSphere(shared_ptr<Geometry<VertexTextureData>> geometry)
{
float radius = 0.5f; // 구의 반지름
uint32 stackCount = 20; // 가로 분할
uint32 sliceCount = 20; // 세로 분할
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);
}
북극 남극을 정하고, 도넛을 몇 개로 만들지 정한다.
각 도넛마다 중점으로 피자처럼 분할한 정점을 찍는다.
그리고 그 정점들을 연결하여 삼각형을 만든다.
프랭크 루나 책에 나온 코드를 그대로 쓴 것이다.
uv좌표는 어떤 모양으로 그려질지 자체가 핵심이다.
TextureDemo::Init으로 가서
//GeometryHelper::CreateQuad(_geometry);
//GeometryHelper::CreateCube(_geometry);
GeometryHelper::CreateSphere(_geometry);
이렇게 해준다.
큐브의 경우는 면마다 그림을 붙이는 게 목적이었지만,
Sphere는 이런 느낌이다.
TextureDemo::Render에서
_shader->DrawIndexed(0, 1, _indexBuffer->GetCount(), 0, 0);
두 번째 인자를 1로 해서 wireframe으로 해서 보면
이렇게 나온다.
구라는 게 결국 삼각형이었다.
더 정밀하게 하려면
void GeometryHelper::CreateSphere(shared_ptr<Geometry<VertexTextureData>> geometry)
{
float radius = 0.5f; // 구의 반지름
uint32 stackCount = 20; // 가로 분할
uint32 sliceCount = 20; // 세로 분할
이 부분을 얼마나 정밀하게 하냐에 따라 더 세밀하게 하거나 대충 만들거나 할 수 있다.
한번 만들면 평생 쓸 일 없어서 자세히 분석할 거 까진 없다.
3. Grid 그리기
마지막으로 Grid로 넘어간다.
땅(terrain)을 깔아야 하는데 그때 사용되는 거 다.
총 몇 개짜리의 사각형으로 만들지가 관건이다. 10개를 만들려면 가로, 세로 정점은 11개여야 한다.
2차 배열로 할 수 있다.
다른 것과 다르게 크기를 지정해줘야 한다.
GeometryHelper.h에서
static void CreateGrid(shared_ptr<Geometry<VertexTextureData>> geometry, int32 sizeX, int32 sizeZ);
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>(sizeZ - 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);
}
TextureDemo::Init에서
//GeometryHelper::CreateQuad(_geometry);
//GeometryHelper::CreateCube(_geometry);
//GeometryHelper::CreateSphere(_geometry);
GeometryHelper::CreateGrid(_geometry, 256, 256);
실행을 하면
이상하게 나온다.
wierefame으로 보면
Grid는 정상적으로 나오는 듯하다.
GeometryHelper::CreateGrid에서
v.uv = Vec2(static_cast<float>(x), static_cast<float>(z));
이렇게 SizeZ-z에서 z로 수정하고 실행하면
뒤집히긴 했지만 이미지가 보이긴 한다.
uv좌표에서 0, 0은 이미지의 왼쪽 상단에 위치하기 때문에 SizeZ-z에서 z로 수정하니 뒤집혀 보이게 된다.
한 칸은 이미지가 나오지만 나머지는 이상하게 나온다
04. Texture.fx의
SamplerState Sampler0;
여기에 뭔가 옵션을 설정할 수 있었는데
uv좌표는 0~1이 공식적인 uv 좌표인데 그 범위를 벗어나면 어떻게 할 것인가가 이런 부분을 설정하는 것이라 볼 수 있다.
1을 넘어가는 1~2면 어떻게 그릴 것인가와 관련된 옵션을 smapleState라는 클래스를 만들어서 하면 된다.
1을 넘어가는 범위를 어떻게 할지 세팅하지 않았기 때문에 지금처럼 나오는 것이다.
'DirectX' 카테고리의 다른 글
39. DirectX11 3D 입문_HeightMap (0) | 2024.02.08 |
---|---|
38. DirectX11 3D 입문_Sampling (0) | 2024.02.07 |
36. DirectX11 3D 입문_텍스처 (0) | 2024.02.06 |
35. DirectX11 3D 입문_카메라 (0) | 2024.02.05 |
34. DirectX11 3D 입문_Constant Buffer (0) | 2024.02.02 |
댓글