새소식

인기 검색어

게임 개발/그래픽스

텍스처링 (Texturing), 포인트 샘플링, 리니어 샘플링, bilinear interpolation

  • -

텍스처링?

3D 오브젝트에 이미지를 가져와서 덧씌우는 것.

디테일한 비주얼을 보여줄 수 있다.

텍스처링은 어떻게 해야 할까?

이전 포스트에서 무게중심 좌표계 (Barycentric Coordinates)를 사용했을때는 세 점의 중간의 색깔들을 interpolation해서 자동으로 채우도록 했었다.

같은원리로 이번에는 점 4개(사각형)을 채우는데, 이미지로 채울 것이고, 각 점들은 이미지의 어느 부위에서 색을 가져와서 채울것인지만 결정해주면 텍스처링을 할 수 있다. (텍스처 좌표 Texture coordinates)

텍스처는 이미지의 가로 세로 1x1로 가정한다. (텍스처 좌표는 u 축과 v축으로 나눠서 uv 라고도 부른다)

사각형은 삼각형 두개로 나눌 수 있고, 무게중심 좌표계를 이용해 위치를 특정할 수 있다.

이렇게 텍스처 좌표계의 어떤 지점으로부터 색깔값을 가져오는 것을 샘플링(sampling)이라고 한다.

텍스처링

Point Sampling(Nearest Sampling) vs Linear Sampling

point vs linear Sampling

point sampling: 구획으로 나눠서 딱딱 나눠지게 sampling

linear sampling: 색깔값은 interpolation 해줌

Clamp vs Wrap

  • clamp를 이용한 샘플링은 값을 가로세로 픽셀에 맞게 clamp해서 사용
  • wrap을 이용하면 처음과 끝이 이어지게, 처음에서 더 앞으로가면 뒤쪽으로 이동, 뒤에서 더 뒤로가면 다시 앞으로 돌아오는 구조. (원통형으로 말아놓고 처음과 끝이 순환하며 이어지게 한다고 생각하면 됨)

아래 이미지는 wrap을 이용한 이미지이고, 위의 point 와 linear 비교 이미지는 clamp를 이용한 결과임

wrap

이미지를 살펴보면 point sampling은 변화가 없고 linear sampling에 변화가있다.

포인트 샘플링 구현은 어떻게?

텍스처링 구현

각 픽셀의 값들은 각 사각형 가운데에 있다고 생각해야 한다.

즉,

픽셀은 (0,0) ~ (width-1, height-1) 이 범위에 있는데

이미지 좌표는 0.5씩 상하좌우로 확장해서

(-0.5, -0.5) ~ (width - 1 + 0.5, height -1 + 0.5) 이 범위에 있다고 생각해야 한다.

그리고 텍스처 좌표의 범위는 0~1 사이이다.

마지막으로 랜덤한 한 점을 찍었을때 거기서 가장 가까운 '사각형의 가운데점(픽셀값)'을 가져오면 된다.

-> 어디가 제일 가까운 픽셀값일까는 반올림(round)을 사용하면 쉽게 구할 수 있다.

vec3 SamplePoint(const vec2 &uv) // Nearest sampling이라고 부르기도 함
{
    // 텍스춰 좌표의 범위 uv [0.0, 1.0] x [0.0, 1.0]
    // 이미지 좌표의 범위 xy [-0.5, width - 1 + 0.5] x [-0.5, height - 1 + 0.5]
    // 배열 인덱스의 정수 범위 ij [0, width-1] x [0, height - 1]

    vec2 xy = uv * vec2(static_cast<float>(width), static_cast<float>(height)) - vec2(0.5); // vec2(uv*width-0.5, uv*height-0.5) 와 동일
    int i = glm::round(xy).x;
    int j = glm::round(xy).y;

    return GetClamped(i, j);
}

4X4 point sampling

Linear Sampling 구현

point sampling의 경우에는 가장 가까운 픽셀값을 찾아서 그 값만 가져오는 방식이었다.

linear sampling은 임의의 점을 찍었을때, 주변의 4개의 점의 값을 적절히 interpolation 해 줘야 한다.

주변 4개의 점은 어떻게 찾냐?

-> floor (버림)을 이용하면 왼쪽 상단의 한 점을 구할 수 있고, 그 점 좌표의 각각 1씩 더해가면서 4개의 점을 구할 수 있다.

interpolation은 어떻게?

-> linear interpolation을 두번하면 된다. x축에 대해서 한번, y축에 대해서 한번.

-> 이처럼 2차원 공간에서 1차원 linear interpolation을 두 번 반복해서 2차원 linear interpolation을 구현한 것을 bilinear interpolation이라고 한다.

linear sampling을 bilinear interpolation으로 구현하는 법

vec3 InterpolateBilinear(
    const float &dx,
    const float &dy,
    const vec3 &c00,
    const vec3 &c10,
    const vec3 &c01,
    const vec3 &c11)
{
    // c00 c10
    // c01 c11
    const vec3 a = c00 * (1.0f - dx) + c10 * dx;
    const vec3 b = c01 * (1.0f - dx) + c11 * dx;

    return a * (1.0f - dy) + b * dy;
}

vec3 SampleLinear(const vec2 &uv)
{
    // 텍스춰 좌표의 범위 uv [0.0, 1.0] x [0.0, 1.0]
    // 이미지 좌표의 범위 xy [-0.5, width - 1 + 0.5] x [-0.5, height - 1 + 0.5]
    // std::cout << floor(-0.3f) << " " << int(-0.3f) << std::endl; // -1 0

    const vec2 xy = uv * vec2(width, height) - vec2(0.5f);
    const int i = static_cast<int>(glm::floor(xy.x));
    const int j = static_cast<int>(glm::floor(xy.y));
    const float dx = xy.x - i;
    const float dy = xy.y - j;

    // clamp
    return InterpolateBilinear(dx, dy, GetClamped(i, j), GetClamped(i + 1, j), GetClamped(i, j + 1), GetClamped(i + 1, j + 1));

    // wrap
    // return InterpolateBilinear(dx, dy, GetWrapped(i, j), GetWrapped(i + 1, j), GetWrapped(i, j + 1), GetWrapped(i + 1, j + 1));
}

Clampped

 

Wrapped
Clampped (실제 텍스처 이미지 가져왔을 때)
Wrapped (실제 텍스처 이미지 가져왔을 때)

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.