그리드 생성에서 높이 설정까지 완성한 다음으로 복잡한 기하도형에 해당하는 원, 원기둥을 그리는 방법과 더불어 한 씬에 여러 물체를 배치( 여러 월드 변환 행렬을 사용하는 법 )와 CubeMap 생성에 대해 공부한다.
원기둥과 원은 구면좌표계로 점의 위치가 정의되는데, 여기 간단하게 구면좌표계의 구성 요소에 대해 설명을 적는다.
원기둥은 밑면, 윗면 반지름, 높이, 조각(slice) 개수와 더미 개수(stack)로 정의된다.
원기둥의 모든 정점은 원기둥의 고리에 놓여있는데 원기둥을 이루기 위해 하나씩 쌓이는 작은 원을 더미, Stack Count라 하면 각 고리는 뚜껑을 구성하는 조각의 수, SliceCount 개 만큼의 정점으로 이루어진다.
각각의 Count가 원기둥의 정점 밀도를 정한다.
> 원기둥 생성 코드
// 정점 위치 계산
vector<MeshVertex> v;
v.push_back(MeshVertex(0, radius, 0, 0, 0, 0, 1, 0));
float phiStep = Math::PI / stackCount;
float thetaStep = Math::PI * 2.0f / sliceCount;
for (UINT i = 1; i <= stackCount - 1; i++)
{
float phi = i * phiStep;
for (UINT k = 0; k <= sliceCount; k++)
{
float theta = k * thetaStep;
Vector3 p = Vector3
(
(radius * sinf(phi) * cosf(theta)),
(radius * cos(phi)),
(radius * sinf(phi) * sinf(theta))
);
Vector3 n;
D3DXVec3Normalize(&n, &p);
Vector2 uv = Vector2(theta / (Math::PI * 2), phi / Math::PI);
v.push_back(MeshVertex(p.x, p.y, p.z, uv.x, uv.y, n.x, n.y, n.z));
}
}
v.push_back(MeshVertex(0, -radius, 0, 0, 0, 0, -1, 0));
// 정점 인덱스 값 계산
vector<UINT> i;
for (UINT k = 1; k <= sliceCount; k++)
{
i.push_back(0);
i.push_back(k + 1);
i.push_back(k);
}
UINT baseIndex = 1;
UINT ringVertexCount = sliceCount + 1;
for (UINT k = 0; k < stackCount - 2; k++)
{
for (UINT j = 0; j < sliceCount; j++)
{
i.push_back(baseIndex + k * ringVertexCount + j);
i.push_back(baseIndex + k * ringVertexCount + j + 1);
i.push_back(baseIndex + (k + 1) * ringVertexCount + j);
i.push_back(baseIndex + (k + 1) * ringVertexCount + j);
i.push_back(baseIndex + k * ringVertexCount + j + 1);
i.push_back(baseIndex + (k + 1) * ringVertexCount + j + 1);
}
}
UINT southPoleIndex = v.size() - 1;
baseIndex = southPoleIndex - ringVertexCount;
for (UINT k = 0; k < sliceCount; k++)
{
i.push_back(southPoleIndex);
i.push_back(baseIndex + k);
i.push_back(baseIndex + k + 1);
}
원은 하나의 구를 반지름과 조각 갯수 및 더미 갯수로 정의한다. 원기둥과의 차이점은 고리의 반지름이 삼각함수에 따라 비선형으로 변한다는것이다.
> 구 생성 코드
vector<MeshVertex> v;
v.push_back(MeshVertex(0, radius, 0, 0, 0, 0, 1, 0));
float phiStep = Math::PI / stackCount;
float thetaStep = Math::PI * 2.0f / sliceCount;
for (UINT i = 1; i <= stackCount - 1; i++)
{
float phi = i * phiStep;
for (UINT k = 0; k <= sliceCount; k++)
{
float theta = k * thetaStep;
Vector3 p = Vector3
(
(radius * sinf(phi) * cosf(theta)),
(radius * cos(phi)),
(radius * sinf(phi) * sinf(theta))
);
Vector3 n;
D3DXVec3Normalize(&n, &p);
Vector2 uv = Vector2(theta / (Math::PI * 2), phi / Math::PI);
v.push_back(MeshVertex(p.x, p.y, p.z, uv.x, uv.y, n.x, n.y, n.z));
}
}
v.push_back(MeshVertex(0, -radius, 0, 0, 0, 0, -1, 0));
// 인덱스 계산
vector<UINT> i;
for (UINT k = 1; k <= sliceCount; k++)
{
i.push_back(0);
i.push_back(k + 1);
i.push_back(k);
}
UINT baseIndex = 1;
UINT ringVertexCount = sliceCount + 1;
for (UINT k = 0; k < stackCount - 2; k++)
{
for (UINT j = 0; j < sliceCount; j++)
{
i.push_back(baseIndex + k * ringVertexCount + j);
i.push_back(baseIndex + k * ringVertexCount + j + 1);
i.push_back(baseIndex + (k + 1) * ringVertexCount + j);
i.push_back(baseIndex + (k + 1) * ringVertexCount + j);
i.push_back(baseIndex + k * ringVertexCount + j + 1);
i.push_back(baseIndex + (k + 1) * ringVertexCount + j + 1);
}
}
UINT southPoleIndex = v.size() - 1;
baseIndex = southPoleIndex - ringVertexCount;
for (UINT k = 0; k < sliceCount; k++)
{
i.push_back(southPoleIndex);
i.push_back(baseIndex + k);
i.push_back(baseIndex + k + 1);
}
생성해둔 메시의 구조를 이용해 크기(Scale), 회전(Rotate), 위치(Transform) 변환 행렬를 포함해 텍스쳐까지 다르게 지정해 같은 구조의 도형도 다른 위치, 다른 크기, 다른 색으로 월드에 배치할 수 있다.
Model(1) - 기초 설명 (1) | 2024.10.10 |
---|---|
Mesh(2) - CubeMap (0) | 2024.08.07 |
Terrain(2) - Normal 값을 통한 음영 (1) | 2024.07.18 |
Terrain(1) - 높이 설정 (0) | 2024.07.18 |
Texture(3) - 좌표지정모드 (0) | 2024.07.11 |