텍스처링?
3D 오브젝트에 이미지를 가져와서 덧씌우는 것.
디테일한 비주얼을 보여줄 수 있다.
텍스처링은 어떻게 해야 할까?
이전 포스트에서 무게중심 좌표계 (Barycentric Coordinates)를 사용했을때는 세 점의 중간의 색깔들을 interpolation해서 자동으로 채우도록 했었다.
같은원리로 이번에는 점 4개(사각형)을 채우는데, 이미지로 채울 것이고, 각 점들은 이미지의 어느 부위에서 색을 가져와서 채울것인지만 결정해주면 텍스처링을 할 수 있다. (텍스처 좌표 Texture coordinates)
텍스처는 이미지의 가로 세로 1x1로 가정한다. (텍스처 좌표는 u 축과 v축으로 나눠서 uv 라고도 부른다)
사각형은 삼각형 두개로 나눌 수 있고, 무게중심 좌표계를 이용해 위치를 특정할 수 있다.
이렇게 텍스처 좌표계의 어떤 지점으로부터 색깔값을 가져오는 것을 샘플링(sampling)이라고 한다.
Point Sampling(Nearest Sampling) vs Linear Sampling
point sampling: 구획으로 나눠서 딱딱 나눠지게 sampling
linear sampling: 색깔값은 interpolation 해줌
Clamp vs Wrap
- clamp를 이용한 샘플링은 값을 가로세로 픽셀에 맞게 clamp해서 사용
- wrap을 이용하면 처음과 끝이 이어지게, 처음에서 더 앞으로가면 뒤쪽으로 이동, 뒤에서 더 뒤로가면 다시 앞으로 돌아오는 구조. (원통형으로 말아놓고 처음과 끝이 순환하며 이어지게 한다고 생각하면 됨)
아래 이미지는 wrap을 이용한 이미지이고, 위의 point 와 linear 비교 이미지는 clamp를 이용한 결과임
이미지를 살펴보면 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);
}
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이라고 한다.
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));
}