그래픽스/DX12

D3D12 3D 프로그래밍 입문 - 기본 지식 (3)

게임 개발 2023. 12. 22. 04:55

 

 

다중표본화의 이론

모니터의 픽셀들이 무한히 작지는 않기 때문에,

모니터 화면에 임의의 선을 완벽하게 나타내는 것은 불가능하다.

흔히 이 때, 나타나는 현상이 앨리어싱 효과, 다른 말로는 계단현상을 보여준다.

 

출저 - 나무위키
좌측 첫 번째 이미지는 안티 앨리어싱을 하지 않음.  출저 - 나무위키

 

 

선을 픽셀들의 배열로 "근사"치에 가깝게 연산하더라도 완벽히 일치할 수 없기에 나타나는 현상이다.

삼각형의 변에서도 이와 비슷한 앨리어싱 효과가 나타난다.

 

 근래의 모니터는 모니터의 해상도가 높아,

픽셀 크기를 줄이면 문제가 크게 완화되었기에 계단 현상이 거의 눈에 띄지 않을 정도가 되었다.

그러나 모니터 해상도를 키우는 것이 불가능하거나 키워도 충분하지 않을 경우에는

antialiasing 안티엘리어싱 혹은 앨리어싱 제거 기법을 적용할 수 있다.

그런 기법 중 하나가 초과표본화(supersampling)이다.

초과표본화에서는 후면 버퍼와 깊이 버퍼를 화면 해상도보다 4배(가로, 새로 두 배씩) 크게 잡고,

3차원 장면을 4배 크기의 해상도에서 후면 버퍼에 렌더링 한다.

이미지를 화면에 제시할 때가 되면 후면 버퍼를 원래 크기의 버퍼로 환원(resolving)한다.

하향표본화(downsampling)이라고도 불리는 이 환원 공정은 4픽셀 블록의 네 색상의 평균을

그 블록에 해당하는 픽셀의 최종 색상으로 사용한다.

본질적으로, 초과표본화는 화면 해상도를 소프트웨어에서 증가하는 것이라 할 수 있다.

 

 초과표본화는 픽셀 처리량과 메모리 소비량이 네 배라서 비용이 높다.

Direct3D는 다중표본화(multisampling)라는 절충적인 앨리어싱 제거 기법을 지원한다.

다중표본화는 일부 계산 결과를 부분픽셀(subpixel)들 사이에서 공유하기 때문에 초과표본화보다 비용이 낮다.

4X 다중표본화(픽셀당 부분픽셀 4개)의 경우 다중표본화도 초과표본화처럼 크기가 화면 해상도의

4배인 후면 버퍼와 깊이 버퍼를 사용하지만,

다중표본화는 이미지 색상을 각 부분픽셀마다 계산하는 것이 아니라

픽셀당 한 번만 계산하고(픽셀의 중심에서),

그 색상과 부분픽셀들의 가시성(이를 위해 부분픽셀당 depth-stancil 판정이 일어난다)과

포괄도(부분픽셀을 다각형이 어느 정도나 덮고 있는지를 뜻하는 값)를 이용해서 최종 색상을 결정한다.

 

 

비교군 비용 계산 방식 정확도
초과표본화 높음 *메모리 소모량 4배 각 픽셀당 개별 높음
다중표본화 낮음  각 픽셀 결과를 공유 낮음

 

 

 

Direct3D의 다중표본화

 

 다중표본화를 위해서는 DXGI_SAMPLE_DESC라는 구조체를 적절히 채워야 한다.

이 구조체는 다음과 같이 두 개의 멤버(자료 필드)로 이루어져 있다.

typedef struct DXGI_SAMPLE_DESC
{
    UINT Count;
    UINT Quality;
} DXGI_SAMPLE_DESC;

 

Count 멤버는 픽셀당 추출할 표본의 개수를 지정하고

Quality 멤버는 원하는 품질 수준(quality level)을 지정한다.

('품질 수준'의 구체적인 의미는 하드웨어 제조사마다 다를 수 있다)

표본 개수가 많을수록, 그리고 품질 수준이 높을수록 렌더링 비용도 증가하므로,

비용과 속도 사이의 절충선을 잘 잡아야 한다.

품질 수준들의 범위는 텍스처 형식과 픽셀당 표본 개수에 의존한다.

 

 주어진 텍스처 형식과 표본 개수의 조합에 대한 품질 수준들의 개수는

ID3D12Device::CheckFeatureSupport라는 메서드로 알아낼 수 있다.

 

typedef struct D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS
    {
    _In_  DXGI_FORMAT Format;
    _In_  UINT SampleCount;
    _In_  D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS Flags;
    _Out_  UINT NumQualityLevels;
    } 	D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS;
    
    
        D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
	msQualityLevels.Format = mBackBufferFormat;
	msQualityLevels.SampleCount = 4;
	msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
	msQualityLevels.NumQualityLevels = 0;
	ThrowIfFailed(md3dDevice->CheckFeatureSupport(
		D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
		&msQualityLevels,
		sizeof(msQualityLevels)
	));

 

 

이 메서드의 둘째 매개변수가 입력과 출력 모두에 쓰인다는 점을 주목하자.

이 메서드는 둘째 매개변수로 지정된 구조체에서 텍스처 형슥과 표본 개수를 읽고,

그에 해당하는 품질 수준 개수를 그 구조체의 NumQualityLevels 멤버에 설정한다.

주어진 텍스처 형식과 표본 개수의 조합에 대해
유효한 품질 수준들은 0에서 NumQualityLevels-1까지이다.

한 픽셀에서 추출할 수 있는 최대 표본 개수는 다음과 같이 정의된다.

 

#define D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT (32)

 

 

그러나 실제 응용에서는 다중표본화의 성능 및 메모리 비용을

합리적인 수준으로 유지하기 위해 표본을 4개나 8개만 추출하는 경우가 많다.

다중표본화를 사용하고 싶지 않으면 표본 개수를 1로, 품질 수준을 0으로 설정하면 된다.

모든 Direct3D 12 대응 장치는 모든 렌더 대상 형식에 대해 4X 다중표본화를 지원한다.

 

++ 참고 ++
DXGI_SAMPLE_DESC 구조체는 교환 사슬 버퍼들과 깊이 버퍼 모두에 필요하다.
또한, 후면 버퍼와 깊이 버퍼를 생성할 때 동일한 다중표본화 설정을 적용해야 한다.