13장 중급 그래픽스 - ① 텍스처 샘플링 기법
더욱 현실감 있는 그래픽 구현을 위해서는 다양한 그래픽 기술들이 사용된다. 여기에서는 몇 가지 중급 그래픽스 개념을 다룬다. 텍스처 품질을 향상하거나 텍스처로 렌더링 하는 방법. 그리고 지연 셰이딩같이 장면을 라이팅 하는 방법을 살펴본다.
💡 13장의 목차
- 텍스처 샘플링 기법
- 텍스처로 렌더링
- 지연 셰이딩
텍스처 샘플링 기법
텍스처에 대한 개념은 5장에서 다룬 적있다. 텍스처에 대한 기본적인 내용은 이 글을 참고하자.
샘플링 아티팩트
텍스처 이미지는 사각형 격자에 나열된 픽셀들의 집합이다. 그리고 텍스처의 픽셀을 텍셀이라고 불렀다. 텍셀은 렌더링 과정을 거쳐 2D 화면상의 픽셀로 출력된다. 만약 텍셀과 화면상의 픽셀이 1:1로 매칭 되어서 출력된다면 항상 우리가 원하는 품질의 이미지 출력을 얻을 수 있을 것이다. 하지만, 원근감을 지원하는 3차원 게임에서는 플레이어와 텍스처 이미지 사이의 거리에 따라서 텍스처 이미지의 크기와 모양은 시시각각으로 변할 수 있다.
플레이어와 텍스처 이미지가 가까워 지면 실제 이미지 리소스 보다 큰 이미지가 화면에 출력될 것이다. 만약 이미지가 두 배로 커졌다면 하나의 텍셀이 화면상에는 두 개의 픽셀로 출력된다면 텍셀과 화면의 픽셀 비율은 1:2이다. 이런 상황을 텍셀의 밀도가 낮아졌다고 한다.
반대로 플레이어가 텍스처 이미지와 거리가 멀어지면 이미지의 크기는 작아질 것이다. 만약 이미지 크기가 절반이 되어서 2개의 텍셀 중 하나의 텍셀만 선택되어 화면상 픽셀로 출력된다면 텍셀과 화면의 픽셀 비율은 2:1이다. 이런 상황을 텍셀의 밀도가 높아졌다고 한다.
텍스처 이미지가 원본보다 커지거나 작아짐에 따라 원본 그래픽의 품질에 결함이 발생한 이미지가 출력될 수 있다. 이렇게 그래픽 알고리즘의 결과에 따른 그래픽 결함을 샘플링 아티팩트(Sampling artifact)라고 부른다. 이런 문제를 보완하기 위해서 다양한 텍스처 필터링 기법이 존재하고, 여기서 몇 가지를 다룬다.
최근접 이웃 필터링
원본 텍스처 이미지를 2배로 확대하는 경우에 하나의 텍셀이 두개의 화면상 픽셀로 선택되어야 한다. 이때 사용할 수 있는 필터링 기법은 최근접 이웃 필터링링과 이중 선형 필터링이 존재한다.
최근접 이웃 필터링은 최근접 이웃 샘플링 이라고도 불린다. 이유는 확대된 이미지 출력을 위해서 별도의 보간 없이 대응되는 텍셀 주소를 계산하고 가장 가까운 색상값을 필터링 없이 샘플링(복사)하여 사용하기 때문이다. 최근접 이웃 샘플링이 어떻게 동작하는지 살펴보자.
일반적으로 텍스처는 단색 블록으로 구성 된 것으로 표시되지만 실제로는 각 텍셀의 정 중앙 위치에 색상이 정의되어 있다.
최근접 이웃 필터링을 적용하면 대상 픽셀에서 원본 텍스처에 대응하는 주소를 계산해서 가장 가까운 색상 주소의 색상값을 그대로 샘플링(복사)하게 된다. 아래의 그림을 살펴보면 화면 픽셀 좌표 (0.125, 0.125)는 원복 텍스처의 (0.0625, 0.0625) 주소에 대응하기 때문에 가장 가까운 색상인 흰색을 복사해온다.
최근접 이웃 필터링을 사용하여 화면상의 텍스처 크기를 늘리면 인식되는 각 텍셀의 크기도 커져서 그림 13.2(b)처럼 이미지의 픽셀화를 쉽게 확인할 수 있는 단점이 생긴다.
이중 선형 필터링
이중 선형 필터링은 샘플링 지점에서 가장 가까운 4개의 텍셀 색상을 보간하여 적용한다. 그림 13.5을 보자. (C)는 화면상의 픽셀을 나타낸다. 이중 선형 필터링에서 (0.375, 0.375) 위치의 픽셀의 색상을 결정하기 위해서는 (b)에서 텍스처 좌표 (0.1875, 0.1875)에 대응하는 위치의 색상을 계산해야 한다.
이때 (0.1875, 0.1875)에 가장 근접한 텍셀 4곳의 색상을 거리에따라 보간하여 (c)의 (0.375, 0.375)의 색상을 결정하게 된다. 4곳의 색상 중 흰색의 텍셀이 가장 가깝기 때문에 (0.375, 0.375) 최종 출력되는 이미지는 흰색에 가까운 빨간색이 된다. 텍셀 사이의 색상 변화를 부드럽게 보간해주는 기법이기 때문에 최근접 이웃 필터링처럼 픽셀화는 나타나지 않지만 전체적으로 흐리게 보일 수는 있다.
이중 선형 필터링의 계산 방법은 다음과 같다. 아래의 그림 13.6은 설명을 위해 그림 13.5 (B)를 확대한 모습이다. 먼저 u 방향에서 A와 B를 보간한 R, C와 D를 보간한 S를 구한다. 그리고 v 방향에서 R과 S를 보간한 최종 P를 구한 값을 화면에 출력한다.
이러한 이중 선형 필터링 계산은 텍스처에 이중 선형 필터링이 활성화돼 있다면 자동적으로 그래픽 카드에서 수행한다. 현대 그래픽 카드 하드웨어는 이중 선형 필터링을 위한 연산을 하드웨어적으로 지원하기 때문에 이 연산을 위한 추가 비용은 없다고 봐도 무방하다.
밉매핑
텍스처를 확대할 때 사용된 기술에 따라 이미지가 픽셀화 되거나 흐리게 보인다. 반대로 텍스처의 크기를 줄이는 경우 그림 13.1의 (C)처럼 테두리가 사라지는 현상이 나타날 수 있다. 아래의 그림 13.7은 \(4 \times 4 \) 개의 텍셀을 \(2 \times 2\) 화면상 픽셀로 이중 선형 필터링된 모습이다. 텍스처를 축소하게 되면 이같이 텍스처의 데이터를 잃어버릴 수 있다.
텍스처를 축소하여 렌더링 할 경우 텍스처의 데이터 손실이 불가피하기 때문에 축소된 텍스처들을 추가로 로드하여 렌더링 하는 기법을 밉매핑이라 한다. 밉맵은 크기별 텍스처의 집합으로 이루어져 있다. 밉맵에 저장된 이미지들은 기본 텍스처를 일정한 수준만큼 미리 축소한 것이다. 예를 들어 소스 텍스처가 \(256 \times 256\)이라면 밉맵은 \(128 \times 128\), \(64 \times 64\), \(32 \times 32\) 이미지를 사용한다.
텍스처를 그릴 때 그래픽 하드웨어는 텍셀 밀도가 1:1에 가장 가까운 밉맵 텍스처를 선택하여 렌더링 한다. 밉맵을 사용하면 높은 해상도로 텍스처를 확대할 때 텍스처 품질을 향상해주지는 않지만 텍스처의 크기를 줄이는 경우에는 텍스처 품질을 크게 향상할 수 있다.
밉매핑 자동 생성
밉맵을 사용하기 위해 그림 13.8 처럼 축소된 텍스처 집합을 수작업으로 만드는일은 상당히 번거로울 수 있다. 다행이도 그래픽 API들은 밉맵을 자동으로 생성하는 기능을 제공해준다. OpenGL에서는 glGenerateMipmap
함수를 통해서 자동으로 밉맵을 생성할 수 있다.
최근접 이웃 밉매핑
밉맵을 적용하는 방식에도 최근접 이웃 밉매핑, 삼중 선형 필터링 두 가지가 있다. 이들 중 최근접 이웃 밉매핑은 1:1에 가장 까까운 텍셀 밀도를 제공하는 밉맵을 선택한다. 이 방법은 대체로 잘 동작하지만 바닥 텍스처와 같은 경우에는 텍스처가 변경되는 경계 부분에서 줄무늬와 같은 샘플링 아티팩트가 발생할 수 있다.
삼중 선형 필터링
삼중 선형 필터링에서는 1:1 텍셀 밀도에 까까운 2개의 밉 레벨로 먼저 필터링(이중 선형 필터링)을 진행한다. 그리고 이 두 필터링된 밉 레벨을 블렌딩(보간)하여 사용한다. 기존의 이중 선형 필터링에 추가로 2개의 밉 레벨에 대한 블렌딩을 진행하기 때문에 삼중 선형 필터링이라 한다.
밉매핑 구현
밉매핑을 위한 모든 기능은 OpenGL에 내장되어 있다. 우리는 몇가지 함수를 호출하여 밉매핑기능을 활성화만 하면된다. 기존에 구현했던 Textuer 클래스의 Load함수에 아래와 같이 추가하면 밉매핑을 사용할 수 있다. 만약 삼중 선형 필터링을 사용하려면 텍스처 파라미터의 마지막 인자로 GL_LINEAR_MIPMAP_LINEAR
를 넘겨주고 최근접 이웃 밉매핑을 사용하려면 마지막 인자로 GL_LINEAR_MIPMAP_NEAREST
를 넘겨주면 된다.
// Mipmap 자동 생성
glGenerateMipmap(GL_TEXTURE_2D);
// 이미지 축소시 삼중 선형 필터링 사용
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// 이미지 축소시 최근접 이웃 맵매핑 사용
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// 이미지 확대시에는 밉맵이 도움되지 않음. 그대로 이중 선형 필터링 사용
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
아래의 그림 13.9에서 바닥 텍스처를 집중해서 보자. 이중 선형 필터링에서 발생하던 픽셀화가 밉매핑이 적용되고 부드럽게 변한 모습을 확인할 수 있다. 하지만 이번에는 텍스처의 경계선이 너무 흐릿하게 표현되는 샘플링 아티팩트가 발생했다. 이문제는 이어서 나오는 이방성 필터링에서 다룬다.
이방성 필터링
밉매핑은 대부분의 경우 샘플링 아티팩트를 크게 줄여주지만, 카메라가 비스듬한 각도에서 텍스처를 보면 텍스처는 매우 흐릿하게 보인다. 이러한 왜곡현상을 이방성(異方性, Anisotropic)이라고 한다. 이방성 필터링은 이런 상황을 완화한다. 이방성의 수학적 원리는 여기서 다루지 않고 이방성 필터링 사용방법만 짚고 넘어간다.
이방성 필터링은 OpenGL 3.3 버전에서는 확장 기능이므로 그래픽 카드가 이방성 필터링을 지원하는지를 검증해야 한다. 최근 10년간 제작된 모든 그래픽 카드는 이방성 필터링을 지원하므로 대부분 문제가 발생하지 않을 테지만, 그래도 하위 호완을 위해서 테스트는 필요하다. Texture 클래스의 Load 함수에 아래와 같이 코드를 추가하여 이방성 필터링을 활성화하자.
// 그래픽 하드웨어가 이방성 필터링 지원하는지 테스트 후 사용
if (GLEW_EXT_texture_filter_anisotropic)
{
// 이방성 최대값을 얻는다.
GLfloat largest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest);
// 이방성 필터링을 최대값으로 활성화한다.
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest);
}
아래의 그림 13.10에서 이방성 필터링의 적용 전(a)과 적용 후(b)를 비교해 보자. 비스듬한 바닥 텍스처가 흐릿한 (a)와 다르게 이방성 필터링을 적용한 (b)에서는 선명한 바닥 텍스처를 확인할 수 있다.
출처:
에이콘 출판 <Game Programming in C++>
MSDN - https://docs.microsoft.com/en-us/windows/win32/direct3d9/direct3d-textures
위키백과: https://ko.wikipedia.org/wiki/%EB%B0%89%EB%A7%B5
Learn OpenGL: https://learnopengl.com/Getting-started/Textures