Byn's Research Note

AI based Mixed Reality, Human-Computer Interaction

↓ My Web CV & Portfolio 자세히보기

카테고리 없음

Graphics Theory [4]: 텍스쳐 (Texture)

JaehyeonByun 2025. 1. 11. 07:39

4. Texturing

Texturing

일반적으로 텍스쳐는 배열 구조를 가지는데, 이 배열이 색상 정보를 저장하고 있다면 이를 이미지 텍스쳐(Image Texture)라고 부른다. 텍스쳐는 텍셀(texel)로 구성되고 이미지 텍스쳐의 텍설은 픽셀과 동일한 것이지만, 구분상 편의를 위해 이름을 따로 붙였다. 텍스쳐링은 물체 표면에 벽지 바르듯이 텍스쳐를 입히는 것으로 이해하면 된다. 폴리곤 메시의 경우 각 정점마다 텍스쳐 좌표(Texture Coordinates)를 할당해야한다.


1. Texturing

Texture Coordinates

스캔 전환과 텍스쳐링

왼쪽 그림에 세 정점에 각각 (0,0),(1,0),(0,1)의 텍스쳐 좌표가 주어졌는데 스캔 전환 알고리즘은 먼저 변을 따라, 그 다음에 스캔라인을 따라 이들 텍스쳐 좌표를 보간한다. 스캔 전환이 완료되면 각각의 프래그먼트는 보간된 (s,t) 좌표를 가지게 된다. 해상도가 r_x x r_y인 텍스쳐가 주어졌을 때, 정규화된 텍스쳐 좌표(s,t)는 아래와 같은 방식으로 실제 텍스쳐 공간으로 매핑된다.

이를 텍스쳐 좌표 (s,t)가 텍스쳐 공간의 (s',t')로 투영되었다고 표현한다.

 

정규화된 텍스쳐 좌표와 텍스쳐 매핑

원통면 폴리곤 메시의 텍스쳐 좌표 변경 없이도 상이한 해상도를 가진 두 개의 텍스쳐를 사용할 수 있다. 텍스쳐는 2차원으로 국한되지 않고 층층이 쌓인 영상은 3차원 텍스쳐에 해당한다.

Texture Wrapping

Texture Wrapping

텍스쳐 좌표는 원칙적으로 [0.1] 범위 안에서 정의되지만, Gl은 그 범위 밖에서 좌표를 지정하는 것을 허용하고 이를 처리해준다. 사용자는 텍스쳐 포장 모드 (Texture wrapping mode)를 설정하여 그 처리 방식을 조절할 수 있다. 폴리곤 메시의 세 정점은 [0,1] 범위 밖의 텍스쳐 좌표를 가지고 있다. 이에 따라 레스터라이저가 생성하는 프래그먼트의 상당수는 [0,1] 범위 밖의 텍스쳐 좌표를 가지게 된다.

(c) 이러한 텍스처 좌표는 [0, 1] 범위의 경계(edge)로 고정(clamp)시킬 수 있다. 이를 경계 고정 모드(clamped-to-edge)라고 한다. 한 프래그먼트의 s값이 1.1이라면 이를 경계 값인 1.0으로 바꾸는 것이다.

(d) [0, 1] 범위를 벗어나는 텍스처 좌표가 주어지면 그 소수점 이하만 이용할 수도 있다. s값이 1.1이라면 이를 0.1로 대체한다. 이렇게 하면 텍스처를 타일처럼 이어붙인 결과가 나온다. 이를 반복 모드(repeat mode)라고 부르는데, 작은 이미지로 넓은 영역을 텍스처링할 수 있으므로 경계 고정 모드보다 훨씬 많이 사용된다. 하지만 텍스처 타일 사이의 경계선이 두드러져 보일 수도 있다.

(e) 반사 반복 모드(mirrored-repeat mode)s값이 1.1이라면 이를 0.9로 바꾸고, s값이 1.2라면 0.8로 바꾼다. 이렇게 하면 텍스처 타일은 정수의 텍스처 좌표마다 반사된 형태로 놓이게 되어 부드러운 효과를 얻을 수 있다.

(f) 반복 모드와 반사 반복 모드를 함께 적용할 수도 있다.

Texture Filtering

텍스쳐 확대

텍스쳐 축소

프래그먼트의 텍스처 좌표 (s, t)는 텍스처 공간의 (s', t')로 투영된다. 그러면 (s', t') 주변의 텍셀을 고려해서 해당 프래그먼트의 색상을 결정해야 하는데, 이를 텍스처 필터링(texture filtering)이라고 부른다. 텍스처 확대의 경우 스크린 공간 사각형의 크기는 이미지 텍스처보다 크다. 따라서, 텍스처는 사각형에 맞도록 확대(magnify)되어야 한다. 점선 격자는 텍스처 공간을 나타내는데, 격자 한 칸에 텍셀이 하나씩 위치한다. 한편, 동그란 점은 텍스처 공간에 투영된 픽셀을 나타낸다. 텍셀보다 픽셀이 많음에 주목해야한다. 즉 텍스쳐 확대의 경우 한 픽셀에 여러 개의 텍셀이 매핑된다. 반면, 텍스처 축소의 경우 스크린 공간 사각형의 크기는 이미지 텍스처보다 작다. 따라서, 텍스처는 사각형에 맞도록 축소(minify)되어야 한다. 텍셀보다 픽셀이 적으므로, 픽셀들은 텍스처 공간에 듬성듬성 투영된다.

Texture Magnification

 

Texture Magnification

텍스처 확대에 사용되는 필터링 방법 중 하나는 근접점 샘플링(nearest point sampling)이다. 즉, (s', t')와 가장 가까운 텍셀을 사용하는 것이다. 여러 개의 픽셀이 하나의 텍셀을 참조하게 되어, 그림 오른쪽에 보인 것처럼 마치 모자이크 처리된 듯한 결과를 얻게 된다. 겹선형보간(Bilinear interpolation)주위를 둘러싸는 네 개의 텍셀을 겹선형보간하여 해당 픽셀의 텍스처 색상을 결정하는 방법이다.겹선형보간을 사용하면 인접한 픽셀일지라도 필터링된 색상이 달라질 수 있고, 근접점 샘플링보다 부드러운 텍스처링 결과를 얻을 수 있다.대부분의 경우에 겹선형보간 기법은 근접점 샘플링보다 우수한 결과를 생성한다.

Visualization of filtering algorithms

Texture Minifiaction

텍스처 축소의 경우, 픽셀들이 텍스처 공간에 듬성듬성 투영됨을 기억하자.왼쪽의 경우 네 개의 픽셀은 모두 검은색 텍셀에 둘러싸여 있다. 이러한 경우 픽셀의 텍스처 색상은 검은색이 된다. 모든 픽셀들이 이렇게 검은색 텍셀에 둘러싸이게 되면, 텍스처링된 프리미티브는 완벽한 검은색이 된다.이 문제는 이른바 에일리어싱(aliasing)의 한 예이다. (픽셀에 대비해서 텍셀이 너무 많다.)

Mipmapping

에일리어싱이 일으키는 부작용을 최소화하기 위한 안티에일리어싱(anti-aliasing)기술이 필요하다. 그 중 대표적인 안티에일리어싱 기법을 소개할 것이다. 텍스처 축소 시 발견한 에일리어싱의 원인을 한 마디로 요약하면, 픽셀에 비해 텍셀이 너무 많아서 텍스처링에 참여하지 못하는 텍셀이 발생하기 때문이다. 문제 해결 방법은 텍스처를 작게 만들어서 텍셀 수를 픽셀 수에 맞추면 된다.

Mipmapping

 

밉맵 생성

왼쪽에 있는 원본 텍스처는 8 x 8 해상도를 가졌다. 이 원본 텍스처를 구성하는 2 x 2 텍셀들의 색상 평균을 구하는 작업을 반복하면, 원본 텍스처의 1/4 크기를 가진 4 x 4 해상도 텍스처를 만들 수 있다. 이러한 다운샘플링(down-sampling)을 반복하면 원본 텍스처 전체의 평균 색상을 담고 있는 1 x 1 크기의 텍스처를 얻을 수 있다. 원본 텍스처의 해상도가 2^l x 2^l일 때, 총 (l + 1)개의 레벨을 가지는 텍스처 피라미드로 묶일 수 있다. 이 피라미드를 밉맵(mipmap)이라 부른다. 밉맵을 사용하면, 텍셀 개수를 픽셀 개수에 맞출 수 있다.

Mipmap Filtering [1/3]

이제까지 픽셀을 스크린 공간의 한 '점'으로 간주해 왔고, 이 픽셀이 텍스처 공간의 한 '점'인 (s', t')로 투영된다고 설명해 왔다. 텍스처 공간에 투영된 픽셀은 (s', t')에 놓인 '점'이 아니라 (s', t')를 중심으로 일정한 '영역'을 차지하게 된다. 이 영역을 픽셀의 발자국(footprint)이라고 부른다. 레벨 0번처럼 픽셀 발자국은 2 x 2 텍셀 영역을 차지한다. 텍셀이 픽셀보다 너무 많다. 레벨 1번은 픽셀 발자국은 하나의 텍셀을 차지한다. 즉, 픽셀과 텍셀의 개수가 같다. 그러면 이 레벨에서 필터링이 수행된다.

Mipmap Filtering [2/3]

밉맵 0번 레벨에 있는 원본 텍스처의 RGB값은 그 상위 레벨에 모두 포함되어 있다. 밉맵의 어느 레벨을 골라 필터링을 하건, 원본 텍스처의 텍셀이 배제되는 경우는 없다. 이렇게 해서 에일리어싱 문제를 해결하는 것이다.필터링을 수행할 밉맵의 레벨을 통상적으로 λ로 표기하는데, 픽셀의 발자국이 0번 레벨 텍스처에서 m x m 텍셀을 차지하는 경우 λ는 log2m으로 설정된다. m이 2의 거듭제곱이라면 λ는 정수가 된다.

하지만, 대개의 경우 m은 2의 거듭제곱이 아닐 것이다. 따라서 λ는 정수가 아닌 실수가 된다.정수가 아닌 λ가 계산되면, λ에 가장 가까운 레벨을 선택할 수 있다. 물론 해당 레벨은 근접점 샘플링 혹은 겹선형보간을 통해 필터링될 수 있다. 위 그림에서는 그 보다 좋은 방법인 λ가 끼여있는 레벨 1과 레벨 2 두 레벨을 모두 선택한 뒤, 두 레벨 모두 겹선형보간으로 필터링한 후 λ의 소수점 이하 값 0.585를 이용해 선형보간한 결과를 보여준다. 이것을 특히 삼선형보간(trilinear interpolation)이라 부른다.하지만 이러한 계산은 GPU가 전담하므로 신경 쓰지 않아도 된다.

2. Normal Mapping

 

고해상도 메시의 불규칙한 노멀을 사용하여 오돌토돌한 표면을 렌더링함

(d)에서 세 개의 점 a, b, c에서의 디퓨즈 반사를 보면 이는 노멀 n과 빛 벡터 l에 의해 결정되는데, b의 경우 n과 l 사이 각도가 작아서 빛을 많이 받아 밝게 보인다. 하지만, a와 c는 n과 l 사이 각도가 커서 빛을 적게 받고 어두워 보인다. 표면 전체에 걸쳐 이처럼 노멀이 불규칙하게 변하므로, 표면 밝기도 불규칙하게 변해서 오돌토돌한 표면을 잘 표현해 준다. 하지만 폴리곤 메시의 해상도가 너무 높은 고해상도 메시는 처리에 많은 시간이 소요된다.

 

매끄러운 메시에 이미지 텍스처만 적용하면 오돌토돌한 표면 표현이 어려움

빠른 연산을 위해 삼각형 두 개로 구성된 메시를 사용하였지만, 이런 메시로는 오돌토돌한 표면을 잘 표현할 수 없다. (d)를 보면 a에서 b를 거쳐 c로 갈 때, n과 l 사이의 각도는 서서히 작아져서 점차 밝아보이게 된다. 서서히 변화하는 표면 밝기로 인해서 표면의 오돌토돌한 특징이 드러나지 않게 된다.

 

이러한 문제를 해결하기 위한 방법 중에 하나가 바로 고해상도 메시의 노멀을 미리 계산하고, 이를 노멀맵(normal map)이라고 하는 특수한 텍스처에 저장한 후, 런타임에는 그림 (a)의 저해상도 메시를 처리하되 노멀맵으로부터 노멀을 읽어서 이를 라이팅에 사용하는 것이다. 이러한 텍스처링 기법을 노멀 매핑(normal mapping) 혹은 범프 매핑(bump mapping)이라 부른다.


Height Map

균일하게 샘플된 (x, y) 좌표에 높이 값이 저장된다. 하이트맵은 회색조의 이미지로 가시화

왼쪽 표면은 이른바 하이트 필드(height field)로 표현할 수 있다.2차원 좌표(x, y)가 주어졌을 때 높이(height) 혹은 z값을 반환하는 함수 h(x,y)이다. 왼쪽 그림은 일정한 간격의 (x, y) 좌표에서 샘플된 하이트 필드를 보여준다.이러한 높이 값을 저장한 2차원 텍스처를 하이트맵(height map)이라고 한다.높이 값을 회색조(gray-scale) 색상으로 해석하면 하이트맵은 회색조 이미지로 그릴 수 있다.색상이 비교적 일정한 부분은 상대적으로 매끈한 영역을 나타내고, 색상이 불규칙한 부분은 오돌토돌한 영역을 나타낸다.

(a) 이미지 텍스처. (b) 하이트맵은 이미지 텍스처로부터 반자동 생성 (c) 노멀맵은 하이트맵으로부터 자동으로 생성

(a)의 이미지 텍스처가 주어졌다면, 각 픽셀의 RGB 값을 회색조로 바꾸고, 필요에 따라 이를 수작업으로 편집하여 (b)의 하이트맵을 얻을 수 있다. 이렇게 생성된 하이트맵은 원본 텍스처와 동일한 해상도를 가진다.

 

Normal Map

 

하이트맵의 한 점에서의 노멀은 그 이웃 점들의 높이를 이용해 계산한다. (a) (x, y, h(x, y))의 노멀은 벡터곱으로 정의된다. (b) (x, y, h(x, y))의 노멀은 (x, y)에 저장된다. (c) RGB 색상 대신 노멀 벡터들을 저장하고 있는 노멀맵을 개념적으로 그렸는데, 모든 노멀은 (0, 0, 1) 방향의 벡터를 '흔들어 놓은' 것으로 묘사할 수 있다.

노멀맵을 만드는 방법 중 하나는 하이트맵을 사용하는 것이다. (a)는 아까의 하이트맵을 다시 그린것이고, 하이트맵 텍셀 9개를 이용해 사각형 메시를 만들었다. 이 메시 표면의 가운데 점 (x, y, h(x, y))의 노멀을 계산해보자. 이렇게 계산된 노멀은 하이트 필드의 바깥쪽을 가리키며 (x, y, h(x, y)) 주변의 표면 경사도를 반영한다. (c)는 각 텍셀에 노멀이 저장된 노멀맵의 개념을 보여준다. 일반적으로 노멀맵은 하이트맵과 동일한 해상도를 가진다.

[-1, 1]범위에서 [0, 1] 범위로의 전환

정규화된 노멀 (n_x, n_y, n_z)의 각 좌표는 모두 [-1, 1] 범위의 실수값이다.반면, 이런 노멀을 저장할 텍스처의 RGB 채널은 모두 [0, 1] 범위에 있다. 따라서 다음과 같은 범위 전환이 필요하다. (c)에서 보인 것 처럼, 노멀맵은 (0, 0, 1)을 이쪽저쪽으로 조금씩 '흔들어 놓은' 노멀들의 집합으로 이해할 수 있다. 따라서, n_z가 n_x와 n_y에 비해 상대적으로 클 것이고, 계산된 R, G, B 중 B값이 크게 된다. 그렇게 되면 결국 노멀맵을 이미지 텍스처로 취급하여 그리면 전체적으로 파란 색조를 띄게 될 것이다.

 

'이미지 텍스처링'과 '노멀 매핑이 결합된 이미지 텍스처링' 비교

노멀 매핑을 사용하지 않을 경우, 모든 프래그먼트의 노멀은 z축을 향하는 단위 벡터인 (0, 0, 1)로 고정된다. 노멀 매핑을 사용하는 경우, 프래그먼트의 노멀은 모두 노멀맵에서 읽어오는데, 이 노멀은 (0, 0, 1)을 이쪽저쪽으로 조금씩 '흔들어 놓은' 벡터일 것이다. 인접한 프래그먼트라 하더라도 다른 방향의 노멀을 가질 수 있고, 이에 따라 쉐이딩 결과는 매우 불규칙하게 되어 그림 하단과 같은 영상을 얻게 된다.

노멀 매핑은 기초 표면 자체를 오돌토돌하게 바꾸는 게 아니라 노멀을 사용하여 오돌토돌한 표면을 '흉내' 내는 것임을 이해해야한다. 위 그림의 하단 렌더링 결과를 보면 테두리가 일직선임을 발견할 수 있는데, 이 같은 한계는 피할 수 없다.


Tangent-space Normal Mapping

노멀 매핑. (a) 노멀맵 가시화. (b) 다양한 표면에 입혀진 노멀맵.

텍스처링은 물체 표면에 벽지 바르듯이 텍스처를 입히는 것으로 이해하면 된다. 노멀 매핑을 위해서는 이미지 텍스처링에서는 불필요했던 한 가지 작업을 더 수행해야 한다.

탄젠트 공간 노멀 매핑. (a) 탄젠트 공간. (b) 탄젠트 공간 노멀.

물체 표면의 한 점에서의 노멀을 N으로 표기하자. (원래의 노말) N에 수직인 접면(tangent plane)에는 두 개의 서로 수직인 단위 벡터 T와 B를 정의할 수 있다. (T는 tangent를, B는 bitangent를 의미) 한 점에서의 { T, B, N }은 해당 점과 함께 탄젠트 공간(tangent space)을 구성한다. 표면의 각 점은 자신만의 탄젠트 공간을 가진다.

(b)에서 노멀 매핑을 사용하지 않았다면 라이팅에 N_p를 썼겠지만, 지금은 N_p 대신 n(s_p, t_p)로 표기했다.N_p는 p의 탄젠트 공간에서 (0, 0, 1)이고, n(s_p, t_p)는 N_p를 약간 흔들어 놓은 것으로 해석할 수 있다. 이러한 해석은 n(s_p, t_p)를 p나 q(n(s_q,t_q))의 탄젠트 공간에서 정의 된 벡터로 보아야 가능하다.

물체 표면의 어떤 점에 노멀 매핑을 적용하건, 노멀맵에서 읽어온 노멀은 바로 그 점의 탄젠트 공간에서 정의된 벡터로 보면 된다. 이런 관점에서 우리는 탄젠트 공간 노멀맵이라는 용어를 쓴다.

 

Normal Mapping Shader

 

사각형 표면의 어느 점을 고르건 그 점에서의 탄젠트 공간 기저는 월드 공간 기저와 동일하다.

만약, 이 기초 표면이 기울어진다면, 탄젠트 공간과 월드 공간의 기저는 달라지게 될 것이다. 이러한 문제를 해결하는 방법은 두 가지가 있다.

 

  1. 탄젠트 공간에 있는 이 normal월드 공간으로 변환하는 것
  2. 월드 공간에 있는 light탄젠트 공간으로 변환하는 것

 

여기서 2번을 선택하기로 하면, 물체 표면의 각 점은 자신만의 탄젠트 공간 기저를 가질 수 있으므로, light를 탄젠트 공간으로 변환하는 행렬은 각 프래그먼트마다 새로 계산되어야 한다. 폴리곤 메시의 정점은 자신의 노멀을 가지고 있음을 상기하고, 이를 N으로 취하고 여기에 T와 B를 더하면 '정점별 { T, B, N }'을 정의할 수 있다.

 

기저 이전 행렬

{ T, B, N }은 전처리 단계에서 정점별로 계산되고 정점 배열에 저장되어 정점 쉐이더로 넘겨진다.(vertex shader, 그럴려면 vertex array에 저장되어 있어야 한다.)

정점 쉐이더는 여기에 먼저 월드 변환을 적용하여 이들을 월드 공간으로 옮긴다. 그 다음, 월드 공간 벡터 T, B, N을 행으로 가지는 3 x 3 회전 행렬을 구성한다. 이는 바로 월드 공간 벡터를 탄젠트 공간으로 변환하는 기저 이전 행렬이다.