LabHub
배우기 러닝패스 코스

셰이더와 GPU 파이프라인 · UV 와 텍스처 표본화 · 이론

표면 위의 좌표계

LabHub 에서 이어서 보기

한 줄 요약

UV 는 텍스처 이미지 위의 좌표이고, 정점마다 붙여 두면 래스터라이저가 픽셀마다 보간해 줍니다. 그 좌표에서 색을 꺼내는 방식이 표본화이고, 확대할 때와 축소할 때 필요한 방법이 다릅니다.

왜 이게 필요했나

정점마다 색을 붙이면 삼각형 하나에 색 세 개뿐입니다. 벽돌 무늬나 글자를 표현하려면 정점을 무늬만큼 잘게 쪼개야 하는데, 그것은 감당이 되지 않습니다.

텍스처는 그 문제를 뒤집습니다. 색을 기하에서 떼어 내 별도의 이미지에 두고, 표면의 각 지점이 그 이미지의 어디를 가리키는지만 정해 둡니다. 그 "어디" 가 UV 좌표입니다. 그러면 삼각형 두 개짜리 사각형에 얼마든지 복잡한 그림을 붙일 수 있습니다.

어떻게 동작하나

UV 는 보통 0에서 1 사이로 정하고, (0,0)이 이미지의 한 모서리, (1,1)이 대각선 반대 모서리입니다. 어느 모서리가 (0,0)인지는 API 마다 다릅니다 — OpenGL 은 왼쪽 아래, Direct3D 는 왼쪽 위입니다. 텍스처가 뒤집혀 나오는 버그의 절반이 여기서 옵니다.

범위를 벗어난 UV 를 어떻게 다룰지가 감싸기 모드입니다. 반복(repeat)은 소수부만 취해 무늬를 되풀이하고, 잘라내기(clamp)는 가장자리 값을 늘이며, 거울(mirror)은 한 번씩 뒤집어 붙입니다. 타일 무늬에는 반복이, 그림 한 장을 그대로 붙일 때는 잘라내기가 맞습니다.

색을 꺼내는 방법은 두 가지가 기본입니다.

최근접(nearest)   i = floor(u * 폭), j = floor(v * 높이) 자리의 텍셀 하나쌍선형(bilinear)  이웃한 텍셀 네 개를 가로세로 비율로 섞는다

쌍선형에서 중요한 것은 텍셀의 중심이 (i+0.5)/폭 에 있다는 점입니다. 이 반 칸을 빼먹으면 그림이 반 텍셀만큼 밀립니다. 그래서 좌표를 u * 폭 - 0.5 로 옮긴 뒤 정수부와 소수부를 나눕니다.

원근 보정 보간은 텍스처에서 특히 중요합니다. 화면 좌표에서 구한 무게중심 좌표로 UV 를 그냥 섞으면, 원근이 들어간 면에서 무늬가 뒤틀립니다. 화면에서 균등하게 나뉜 간격이 3차원 공간에서는 균등하지 않기 때문입니다. 올바른 방법은 u/w, v/w, 1/w 를 보간한 뒤 앞의 둘을 뒤의 것으로 나누는 것입니다.

iw = Σ bary_k * (1 / w_k)u  = Σ bary_k * (u_k / w_k)  /  iw

초기 콘솔 게임에서 바닥 무늬가 출렁이던 것이 이 보정을 하지 않아서였습니다. 지금은 하드웨어가 알아서 해 주지만, 왜 필요한지는 알아 둘 값어치가 있습니다.

축소는 또 다른 문제입니다. 한 픽셀이 텍셀 수십 개를 덮으면 그중 하나만 골라서는 원래 무늬를 대표하지 못합니다. 카메라가 조금만 움직여도 고르는 텍셀이 바뀌어 화면이 지글거립니다. 해법이 밉맵입니다 — 텍스처를 미리 절반씩 줄여 사슬로 만들어 두고, 픽셀이 덮는 텍셀 수에 맞는 단계를 골라 씁니다. 메모리를 3분의 1 더 쓰는 대신 지글거림이 사라지고, 작게 그릴 때는 오히려 캐시 적중률이 올라 빨라집니다.

이방성 필터링은 밉맵의 한계를 보완합니다. 밉맵은 한 픽셀이 가로세로로 똑같이 텍셀을 덮는다고 가정하는데, 바닥처럼 비스듬히 보이는 면에서는 한쪽으로만 길게 덮습니다. 그때 밉 단계를 긴 쪽에 맞추면 짧은 쪽이 필요 이상으로 흐려집니다. 그래서 게임의 그래픽 설정에 있는 이방성 필터링은 긴 쪽 방향으로 표본을 여러 개 뽑아 그 흐림을 되돌리는 것입니다. 멀리 뻗은 도로가 또렷해지는 것이 이 옵션의 효과입니다.

텍스처가 메모리에서 어떻게 놓이는지도 성능에 영향을 줍니다. 화면의 이웃 픽셀은 대개 텍스처의 이웃 텍셀을 읽는데, 행 우선으로 그냥 놓으면 세로로 이웃한 텍셀이 메모리에서 멀리 떨어집니다. 그래서 GPU 는 텍스처를 작은 사각형 블록 단위로 재배치해 저장합니다. 이것을 스위즐 또는 타일드 레이아웃이라고 하고, 텍스처 업로드가 단순 복사보다 느린 이유 중 하나입니다.

현장에서 만나는 모습

타일 무늬의 경계에 얇은 줄이 보이는 문제가 흔합니다. 반복 모드에서 쌍선형 표본화가 이미지의 오른쪽 끝과 왼쪽 끝을 섞기 때문인데, 이건 사실 반복 모드가 의도한 대로 동작한 것입니다. 대신 잘라내기 모드를 쓰면 반대쪽 끝을 섞지 않습니다.

또 하나는 아틀라스의 새어 나옴입니다. 여러 그림을 한 장에 모아 두고 UV 로 잘라 쓰는 방식인데, 밉맵의 낮은 단계에서는 이웃 그림의 색이 섞여 들어옵니다. 그림 사이에 여백을 두거나 밉 단계를 제한해야 합니다.

마지막으로 UV 를 누가 정하는가 를 짚어 두겠습니다. 정점의 UV 는 모델링 단계에서 사람이나 도구가 정합니다. 3차원 표면을 2차원 평면에 펼쳐 놓는 일이라, 자르는 선을 어디에 둘지와 왜곡을 어디에 몰아 줄지를 정해야 합니다. 이 작업을 UV 펼치기라고 하고, 잘못하면 무늬가 늘어나거나 이음매가 눈에 띕니다. 셰이더 쪽에서는 이미 정해진 UV 를 받아 쓸 뿐이지만, 무늬가 이상할 때 원인이 셰이더가 아니라 모델에 있을 수 있다는 것은 알아 두는 편이 좋습니다.

다음 실습에서 할 것

체커 무늬 텍스처를 만들고, 최근접과 쌍선형 표본화를 각각 구현합니다. 그다음 원근이 들어간 사각형에 무늬를 붙여 보정하지 않은 것과 보정한 것을 나란히 보고 차이 이미지를 뽑습니다. 마지막에는 잘게 흔들리는 무늬로 밉맵 한 단계를 만들어 지글거림이 사라지는 것을 숫자로 확인합니다.