텍스쳐는 오브젝트에 붙일 수 있는 일종의 스티커라고 이야기할 수 있다. 일반적으로 2D 이미지 파일을 사용한다.
(일부 경우에는 3D 텍스쳐를 쓰기도 한다. (언리얼 엔진의 volumetric fog 등))
텍스쳐는 위와 같이 (0,0) ~ (1,1) 범위의 좌표계를 사용하며, 보통 x축을 S, y축을 T라고 표현한다.
또한 텍스쳐는 이미지 파일이기 때문에 그 자체로도 픽셀을 가지는데, 이를 텍셀(texel)이라고 칭한다.
후술하겠지만, 이 텍셀로 인해 골치아픈 일이 많이 발생한다.
텍스쳐를 오브젝트에 칠할 때는, 우선 모델의 각 버텍스에 텍스쳐 좌표를 지정해준다.
예를들면 삼각형 모양의 모델이 존재하고, 각 버텍스의 텍스쳐 좌표가 (0, 1), (1, 0), (0, 0)이라고 하자.
Vertex Shader로 들어간 Per-Vertex 텍스쳐 좌표는 렌더링 파이프라인 내부의 Rasterizer를 거치며 Per-Fragment 정보로 변환된다. 즉, 모델의 각 fragment가 자신에게 해당하는 텍스쳐 좌표를 가지게 된다.
좌표값들은 (0.4, 0.5), (0.4, 0.6), ... 등 소수 좌표로 나타날 것이다.
Per-Vertex 정보가 Per-Fragment 정보로 변환되는 메커니즘이 바로 이전 포스팅에서도 언급했던 Bilinear Interpolation이다.
(Bilinear Interpolation에 관한 글)
그러면 Sampler가 각 fragment마다 어떤 텍스쳐 좌표에 해당하는지 보고, 텍스쳐의 해당 좌표의 텍셀을 떼와서 fragment에 붙여주는데, 이 과정을 Sampling이라고 한다.
그 결과 삼각형의 내부가 이렇게 채워지게 될 것이다.
여기까지만 보면 간단하지만, 실제로는 텍셀 관련하여 샘플링 중에 많은 문제가 발생하며, 이러한 것들을 Sampling Problem이라고 한다.
예를 들어, 아래 그리드가 모니터의 픽셀을 나타낸다고 생각해보자.
그리고 모니터 내 3D 월드 상에 텍스쳐가 발라진 오브젝트을 가져다 놓았을 때, 이상적인 경우는 모니터의 픽셀과 오브젝트 표면의 텍셀이 1 대 1로 매핑되는 경우일 것이다.
이러면 아주 행복한 경우이지만, 만약 이게 3D 게임이고 플레이어가 앞으로 이동해서 오브젝트에 가까워진다고 가정해보자.
보이다시피, 한 텍셀이 여러 픽셀에 걸치게 되며, 이를 Magnification 이라고 한다.
이러한 현상은 텍스쳐가 흐려보이도록 만든다. 유니티, 언리얼 엔진 등에서 오브젝트의 스케일을 무식하게 늘리면 표면이 흐려보이는 이유가 이것 때문이다.
저해상도 텍스쳐를 쓰는 게임에서 벽에 붙었을 때 벽이 흐려보이는 것도 이것으로 설명이 가능하다.
반대로, 이번에는 플레이어가 뒤로 이동해서 오브젝트가 작게 보인다고 가정하자.
이번에는 한 픽셀에 여러 텍셀이 우겨들어가게 되는데, 이를 Minification이라고 한다.
이 문제는 단순히 텍스쳐가 흐리게 보일 뿐이었던 Magnification 문제보다 더 심각하다.
왜냐하면 픽셀 하나는 한 가지 컬러밖에 나타낼 수 없기 때문이다.
위의 이미지처럼 픽셀 하나에 여러 컬러가 들어가있을 때, 픽셀은 무슨 색을 보여줘야 할까?
또한 비슷한 문제로, 오브젝트가 애매한 위치에 있어 한 픽셀에 여러 텍셀이 걸리거나, 한 텍셀이 한 픽셀 안에 작은 비중으로 들어갔을 때, 색을 어떻게 해줘야 할까?
이러한 문제를 해결하기 위해 사용하는 것이 Texture Filtering이다.
여러 종류가 있는데, 그 중 가장 중요한 것이 Nearest Filtering, Linear Filtering이다.
OpenGL에서는 각각 GL_NEAREST, GL_LINEAR라는 이름으로 사용한다.
Nearest Filtering
- 단순히 픽셀의 중심에 걸치는 텍셀의 컬러를 사용한다.
- 다르게 말하면, 픽셀의 중심에 가장 가까운(nearest) 텍셀을 사용한다고 할 수 있다.
- 상단 이미지의 십자 기호가 픽셀의 중심이라고 하면, 십자 기호가 올라가 있는 파란색 텍셀의 색을 최종 픽셀의 색으로 사용하는 것이다.
- 컬러의 결정이 단순하기 때문에, 화면이 픽셀스럽게(?) 보이게 되며, 특히 Minification 발생 시 화면이 상당히 더러워보이게 된다.
Linear Filtering (a.k.a Bilinear Filtering)
- 마찬가지로 상단 이미지에서 십자 기호가 픽셀의 중심이라고 가정하자.
- 픽셀의 중심에 걸치는 텍셀 뿐만 아니라, 인접한 텍셀들도 적절히 섞어서 최종 픽셀의 색을 결정한다.
- 위의 예시 이미지에서 픽셀의 중심이 파란색 텍셀의 우측 하단에 위치하므로, 그쪽으로 인접한 텍셀 3개를 가져온 것을 볼 수 있다.
- 어떤 텍셀의 중심이 픽셀의 중심에 가까울 수록, 해당 텍셀은 최종 샘플링 컬러에 더 많이 기여하게 된다.
- 즉, 위의 예시 이미지에서는 가장 많이 기여하는 것은 파란색, 그 다음이 남색과 청록색, 가장 적게 기여하는 것이 초록색이 될 것이다.
- 이를 계산하기 위해 또 Bilinear Interpolation을 사용하게 된다. 'Linear'라는 이름이 붙은 것도 그 때문이다.
- 컬러의 결정이 조금 복잡해지긴 했지만 성능에 문제가 될 정도는 아니고, Nearest에 비해 경계를 부드럽게 칠하는 결과를 낳아 화면이 비교적 보기 좋아진다.
- 이 때문에 대부분의 어플리케이션은 Linear Filtering을 사용한다.
글이 조금 길어질 것 같아, Mipmaps, Wrapping, Texture Unit 등에 관한 내용은 다음 포스팅에서 다루려고 한다.
'OpenGL > 공부' 카테고리의 다른 글
[OpenGL] Textures (3) - Wrapping (0) | 2024.06.12 |
---|---|
[OpenGL] Textures (2) - Mipmap (0) | 2024.06.06 |
[OpenGL] 카메라 구현, 키보드/마우스를 통한 조작 (0) | 2024.06.01 |
[OpenGL] Projection Matrix (glm::perspective, glm::ortho) (0) | 2024.06.01 |
[OpenGL] ModelView Matrix (glm::lookAt) (0) | 2024.06.01 |