4 분 소요

Grid

Image

일반적인 ‘사각형’(Vertex 4개) 대신
격자 모양(Grid)을 사용하는 이유?

  • ‘지형’ 같은 ‘굴곡’을 표현 가능
    (높이맵,Wave,진동, 노멀 등의 응용)

  • 기하의 vertex를 늘림에 따라
    ‘윤곽’ 과 그림자 계산을 더욱 사실적으로 계산 가능

  • Sphere, Cylinder 등에 응용 가능

예제 Grid 만들기

Grid는 ‘넓이’와 ‘높이’를 기반으로
slice(가로 개수)와 numStack(세로 개수)를 통해 만든다

따라서 각각의 Slice와 Stack을 적용하여 풀면 된다

Slice(가로 방향만) 구현

MeshData GeometryGenerator::MakeGrid(const float width, const float height,
                                     const int numSlices, const int numStacks) {
    const float dx = width / numSlices;

    MeshData meshData;

    vector<Vertex> &vertices = meshData.vertices;
    vector<uint16_t> &indices = meshData.indices;

    // y = -0.5f * height 인 점들
    Vector3 stackStartPoint = Vector3(-0.5f * width, -0.5f * height, 0.0f);
    for (int i = 0; i <= numSlices; i++) {
        Vertex v;

        // x-y 평면에서 시작점을 x 방향으로 이동
        v.position = stackStartPoint;
        v.position.x += (width / numSlices) * i;

        // 시점을 향하는 방향
        v.normal = Vector3(0.0f, 0.0f, -1.0f);

        v.texcoord = Vector2(0,1);
        v.texcoord.x += (1 / numSlices) * i;

        vertices.push_back(v);
    }

    // y = 0.5f * height 인 점들
    stackStartPoint = Vector3(-0.5f * width, 0.5f * height, 0.0f);
    for (int i = 0; i <= numSlices; i++) {
        Vertex v;

        // x-y 평면에서 시작점을 x 방향으로 이동
        v.position = stackStartPoint;
        v.position.x += (width / numSlices) * i;

        // 시점을 향하는 방향
        v.normal = Vector3(0.0f, 0.0f, -1.0f);

        v.texcoord = Vector2(0, 1);
        v.texcoord.x += (1 / numSlices) * i;

        vertices.push_back(v);
    }

    // 인덱스 추가
    for (int i = 0; i < numSlices; i++) {

        // 첫번째 삼각형
        indices.push_back(i);
        indices.push_back(i + numSlices + 1);
        indices.push_back(i + numSlices + 2);
        
        // 두 번째 삼각형
        indices.push_back(i);
        indices.push_back(i + numSlices + 2);
        indices.push_back(i + 1);
    }

    return meshData;
}

  • x 에 관한 position과 texcoord의 각각의 최댓값인
    Width 와 1.0을 numSlices로 ‘나눈’후 각각의 정점의 x값으로 사용

결과

Image

세로 Stack 적용 방식

MeshData GeometryGenerator::MakeGrid(const float width, const float height,
                                     const int numSlices, const int numStacks) {
    const float dx = width / numSlices;

    MeshData meshData;

    vector<Vertex> &vertices = meshData.vertices;
    vector<uint16_t> &indices = meshData.indices;

    // y = -0.5f * height 인 점들
    Vector3 stackStartPoint = Vector3(-0.5f * width, -0.5f * height, 0.0f);
    for (int h = 0; h <= numStacks; h++)
    {
        for (int i = 0; i <= numSlices; i++) 
        {
            Vertex v;

            // x-y 평면에서 시작점을 x 방향으로 이동
            v.position = stackStartPoint;
            v.position.x += (width / numSlices) * i;
            v.position.y += (height / numStacks) * h;

            // 시점을 향하는 방향
            v.normal = Vector3(0.0f, 0.0f, -1.0f);

            v.texcoord = Vector2(0, 1);
            v.texcoord.x += (1 / numSlices) * i;
            v.texcoord.y -= (1 / numStacks) * h;

            vertices.push_back(v);
        }
    }

    // 인덱스 추가
    for (int i = 0; i <= numSlices * numStacks + 1; i++) 
    {
        if (i % (numSlices + 1) == numSlices )
            continue;

        // 첫번째 삼각형
        indices.push_back(i);
        indices.push_back(i + numSlices + 1);
        indices.push_back(i + numSlices + 2);
        
        // 두 번째 삼각형
        indices.push_back(i);
        indices.push_back(i + numSlices + 2);
        indices.push_back(i + 1);
    }

    return meshData;
}
  • 반복문을 통하여 y에 관한 부분도 나누어 줌

  • 다만 Index를 통하여 ‘마지막’ 점에 해당하는 부분은
    index를 건너띈다

결과

Image

예제 구현 코드 (제공)

vertecies 생성

const float dx = width / numSlices;
const float dy = height / numStacks;

MeshData meshData;

vector<Vertex> &vertices = meshData.vertices;
vector<uint16_t> &indices = meshData.indices;

// y = -0.5f * height 인 점들
Vector3 stackStartPoint = Vector3(-0.5f * width, -0.5f * height, 0.0f);
for (int h = 0; h <= numStacks; h++)
{
    for (int i = 0; i <= numSlices; i++) 
    {
        Vertex v;

        // x-y 평면에서 시작점을 x 방향으로 이동
        v.position =
            Vector3::Transform(stackStartPoint, Matrix::CreateTranslation(Vector3(dx* float(i),dy* float(h),0.0f));

        // 시점을 향하는 방향
        v.normal = Vector3(0.0f, 0.0f, -1.0f);

        v.texcoord = Vector2(float(i) / numSlices,1 - float(h) / numStacks);

        vertices.push_back(v);
    }
}
  • dx,dy를 이용

  • Vector3::Transform 를 사용
    그냥 y,x에 더해도 되나,
    Transform을 통해 Rotation 등의 적용 결과를 방해하지 않을 수 있음
    (즉, 회전/스케일 등의 연산 코드가 추가될때, 수정점이 적어짐)
    (차후, 실린더나 스피어 구현할때 고려)
  • dx의 UV 좌표는 좌측 상단이
    (0,0) 이고 현재 grid는 좌측 하단에서 시작하므로
    y좌표를 1 - float(h) / numStacks 로 구현

Indices 생성

for (int h = 0; h < numStacks; h++)
{
    const int offset = (numSlices + 1) * h;

    for (int i = 0; i < numSlices; i++) 
    {
      indices.push_back(offset + i);
      indices.push_back(offset + i + numSlices + 1);
      indices.push_back(offset + i + 1 + numSlices + 1);

      indices.push_back(offset + i);
      indices.push_back(offset + i + 1 + numSlices + 1);
      indices.push_back(offset + i + 1);
    }
}

TMI : Grid 의 z값을 이용하여 지도 표현

Image

grid의 가장 큰 특징인 ‘변형’을 이용하여
물결치는 듯한 표현을 할 수 있다

// stack과 slice 수를 대량으로 늘릴 수록 부드러운 표현 가능

Vector3 stackStartPoint = Vector3(-0.5f * width, -0.5f * height, 0.0f);
for (int h = 0; h <= numStacks; h++)
{
    for (int i = 0; i <= numSlices; i++) 
    {
        Vertex v;

        // x-y 평면에서 시작점을 x 방향으로 이동
        v.position =
            Vector3::Transform(stackStartPoint, Matrix::CreateTranslation(Vector3(dx* float(i),dy* float(h),sin(i * dx * 10.0f) * 0.1)));

        // 시점을 향하는 방향
        v.normal = Vector3(0.0f, 0.0f, -1.0f);

        v.texcoord = Vector2(float(i) / numSlices,1 - float(h) / numStacks);

        vertices.push_back(v);
    }
}
  • z 값 영역에 sin 을 넣어
    반복되는 표현을 사용

  • 다만 지금은 nomral 값을 수정하지 않기 때문에
    표면 등이 다소 어색할 수 있다

태그:

카테고리:

업데이트:

댓글남기기