[GPU] Anti-Aliasing (AA) (SSAA vs. MSAA)

Graphic 공부를 하면 Anti-Aliasing (AA) 이란 단어를 Texture 다음으로 많이 듣게 된다. AA란 Object의 Edge를 부드럽게 하는 작업이다. 인터넷을 찾아보면 다양한 기법이 있지만 크게 2가지 방법이 제일 유명하다. 하나는 Super-Sampling Anti-Aliasing (SSAA)이고, 다른 하나는 Multi-Sample Anti-Aliasing (MSAA)이다. MSAA와 SSAA기법을 사용하여 만든 Image 결과는 큰 차이가 없다. 출처 6에 SSAA와 MSAA 기법을 사용하여 생성한 Image를 확인 할 수 있다. 하지만, MSAA가 SSAA 대비 성능(Performance)적으로 우수하다고 한다.

Anti-Aliasing (AA)

가장 먼저 AA에 대한 설명을 정리하였다. AA는 앞에 설명한 것과 같이 모니터에 Display 되는 Object Edge를 부드럽게 만들기 위한 방법이다. 보통 Rendering 하기 위해 사용되는 좌표는 (-1, -1)에서 (1, 1) 사이 아무 값을 사용할 수 있다. 하지만, 우리가 보는 모니터 (Display) 장치는 작은 Pixel로 구성되어 있다. 결과적으로 부드러운 직선을 그리고 싶어도 모니터가 출력하는 장치는 Pixel로 구성되어 있어서 부드러운 직선을 출력하는 것이 불가능하다. 예를 들어 선을 Rendering 하여 모니터에 출력한다고 가정하자. 이 경우 아래 그림 1의 왼쪽과 같이 부드러운 선이 아니라 각이 생긴 계단처럼 보이는 것을 확인할 수 있다.

그림 1: Aliased vs. Anti-Aliased 결과 차이 (출처 1) 

AA는 선을 조금 더 부드럽게 만들기 위해서 선 주위의 색상 값을 변경하여 조금은 부드럽게 보이도록 하는 방법이다. 그림 1의 오른쪽 그림은 AA 기법을 적용한 선을 보여준다. 요즘은 모니터가 출력할 수 있는 Display Pixel의 수가 워낙 많기 때문에 AA를 하지 않아도 아주 부드럽게 보일 수도 있다. 하지만 아직도 AA를 적용하고 적용하지 않은 경우 눈으로 차이를 확인할 수 있다.

SSAA (Super-Sampling Anti-Aliasing)

SSAA는 Rendering 되는 이미지를 더 크게 생성하여서 모니터에 Display 되는 크기에 맞도록 줄이는 방법이다. 예를 들어 우리가 현재 1920 x 1080 모니터를 사용한다고 가정하자. GPU는 모니터 Display 크기보다 4배 큰 3840 x 2160 크기로 이미지를 Rendering 한다. 모니터에 출력되기 전 마지막 단계에서 해당 이미지를 1/4 크기로 줄이는 작업을 진행한다. 4개의 Pixel의 색상을 Interpolation 하여서 1개의 Pixel 색상을 결정하게 된다. 이렇게 4배 큰 이미지를 Rendering 하여 마지막에 크기를 줄여서 출력하면 Object의 Edge가 부드럽게 출력된다. SSAA의 경우 Object Edge 뿐만 아니라 모든 이미지의 Quality가 향상된다.

그림 2: Downscale vs. Upscale (SSAA) 이미지 차이 (출처 2)

그림 2는 SSAA 기법을 사용하여 출력한 Image결과이다. 오른쪽 끝에 5120 x 2880 결과를 1440p로 Downscale (SSAA)를 한 결과이다. 조금 더 큰 이미지를 보고 싶은 경우 아래 출처2에서 확인할 수 있다. SSAA 방법은 아주 단순히 더 큰 이미지를 만들어서 크기를 줄이는 방법이다. 그렇기 때문에 더 큰 이미지를 그리기 위해서 연산량이 배로 증가한다.

MSAA (Multi-Sample Anti-Aliasing)

MSAA는 SSAA의 단점을 보완하기 위해서 만들어진 방법이다. 앞에서 작성한 것과 같이 SSAA는 연산량이 많이 증가한다. 현재까지 공부한 부분으로 요약하면 MSAA는 SSAA대비 Pixel (Fragment Shader) 연산이 줄어든다. 그 외 Pixel 값을 저장하는 Depth, Stencil, Color Buffer의 크기 차이는 존재하지 않는다.

그림 3: MSAA Sampling 방법 (출처 3)

MSAA는 하나의 Pixel 색상 값을 결정하기 위해서 4개의 Sample Pixel 값을 사용한다. 보통 MSAA x4와 같이 표현하는데 여기서 x4의 의미는 하나의 Pixel 연산에 4개의 Sample을 사용한다는 의미이다. 그림 3은 보통 MSAA에서 최종 Pixel 값을 결정하기 위해 사용하는 Sampling 방법이다. 그림 중간의 노랑색은 보통 Pixel 연산 (Fragment Shader)에서 사용하는 좌표이다. 그 외 4개의 빨간색 포인트는 MSAA 연산에서 최종 Pixel 값을 결정하기 위해 사용되는 4개의 Sample이라고 생각하면 된다.

도대체 어떻게 Sampling을 하며, 최종 색상은 어떻게 결정하는지 정리가 필요하다. 여기서부터는 조금 추측이 포함되어 있다. 정확한 방법을 찾으면 수정하도록 하겠다. MSAA는 No-AA (No Anti-Aliased)와 같이 Vertex Shader 연산을 수행하고 다양한 중간과정을 거쳐서 Rasterization 연산을 수행하게 된다. MSAA와 No-AA는 Rasterization 연산에서 차이가 발생한다. No-AA의 경우 그림 3의 노랑색 하나의 좌표만 사용하여 해당 Fragment가 Object에 포함되어서 Fragment Shader 연산이 필요한지 결정하게 된다. 그래서 Rasterization은 노랑색 포인트를 기준으로 해당 Pixel이 Fragment Shader 연산이 필요한지 파악한다. 하지만, MSAA의 경우 4개의 빨간색이 특정 Object에 포함되어서 Fragment Shader 연산이 필요한지 파악한다. 출처 3, 5에 따르면 4개의 Bit를 사용하여 빨간색 좌표가 Object에 포함되어서 Fragment Shader 연산이 필요한지 저장한다고 한다. MSAA x8의 경우 8개의 Bit를 사용하여 해당 정보를 저장한다고 생각하면 될 것 같다. 만약 1개의 포인트라도 Fragment Shader 연산이 필요하다고 판단되면 Fragment Shader 연산을 수행하게 된다.

출처 3 설명에 따르면 4개 중 1개의 포인트라도 Object에 포함이 되어서 Fragment Shader 연산이 필요하면 1번의 Fragment Shader 연산을 수행한다고 한다. 만약 4개중 4개의 포인트가 모두 Object에 포함된다고 하여도 오직 한번의 Fragment Shader연산을 수행한다. 바로 여기서 SSAA와의 차이가 발생한다. SSAA의 경우 4개의 Fragment Shader 연산을 수행해야 한다. 하지만, MSAA의 경우 1번의 연산만을 수행하게 된다. 솔직히 여기서 약간의 궁금증이 생긴다. MSAA의 경우 1번의 연산으로 색상을 결정하게 되는데 도대체 어떤 좌표를 기준으로 Fragment Shader 연산을 수행하여 최종 Pixel 색상을 결정하는지 모르겠다. 예를 들어 그림 3의 0, 2번 위치가 특정 Object에 포함이 되어서 Fragment Shader 연산이 필요하다가 가정하자. 만약 Fragment Shader에서 Texture을 사용하여서 색상을 결정하게 되면 0 또는 2번 좌표를 사용해야지 색상을 결정할 수 있다. 하지만, 1 또는 3번 위치를 사용하여 Texture 값을 Loading 하면 정확한 색상을 알지 못하게 된다. 정확한 방법은 모르겠지만 MSAA는 오직 1번의 Fragment Shader 연산을 수행하게 된다. 1번의 Fragment Shader 연산을 수행하여 Pixel, Depth, Stencil 값을 결정하면 Object에 포함되어 있다고 판단된 빨간색 포인트에 Pixel, Depth, Stencil 값을 저장한다. 예를 들어 그림 3의 0, 2번 위치가 포함되었다면 1번의 Fragment Shader 연산을 수행하여 동일한(아마도 동일한 값을 사용할 것으로 판단됨) 값을 0, 2번 위치의 Color, Depth, Stencil Buffer에 저장한다.

그림 4: SSAA vs. MSAA 성능 차이 (출처 4)

MSAA 기법을 사용하여 모든 Drawcall 연산을 수행하면 특정 Fragment의 빨간색 4개 포인트는 모두 각기 다른 Color, Depth, Stencil 값을 가지게 된다. 아마 특정 Fragment에 위치한 4개의 빨간색 포인트는 동일한 Color, Depth, Stencil 값을 가질 수도 있다. 제일 마지막 단계에서 4개의 색상을 Interpolation 하여 최종적으로 Display 되어야 할 Fragment (Pixel) 값을 결정하게 된다. 결과적으로 MSAA는 SSAA와 비슷하게 더 많은 Color, Depth, Stencil Buffer가 필요하다. 하지만, SSAA와 달리 Fragment Shader 연산을 한 번만 수행하게 된다. 보통 Graphic 연산에서 가장 큰 Overhead를 차지하는 부분이 Fragment Shader 연산이다. 그래서 MSAA와 SSAA의 성능 차이가 발생한다. 그림 4는 MSAA와 SSAA의 성능 차이를 보여준다.

설명을 길게 작성했는데 솔직히 모르는 부분이 너무 많아서 MSAA에 대한 정확한 설명을 작성하지 못한 것 같다. 확실한 부분은 MSAA와 SSAA의 성능 차이는 Fragment Shader 연산량의 차이에서 발생한다는 것이다. 하지만, Color, Depth, Stencil Buffer의 크기는 같거나 오히려 MSAA가 더 클 것 같다. 추가로 더 좋은 정보를 찾거나 MSAA의 정확한 동작 방법을 알게 되면 다시 수정하여서 정리할 계획이다.

출처

  1. http://www.dei.isep.ipp.pt/~matos/cg/docs/OpenGL_PG/ch07.html
  2. https://kotaku.com/want-to-know-more-about-all-those-graphics-options-pc-g-1642819481
  3. https://mynameismjp.wordpress.com/2012/10/24/msaa-overview/
  4. https://sapphirenation.net/anti-aliasing-comparison-performance-quality/
  5. https://www.nvidia.com/object/coverage-sampled-aa.html
  6. https://sapphirenation.net/wp-content/themes/sapphire/Articles/slider-anti-aliasing/Splinter-Cell-Blacklist-MSAA-x4-vs-SSAA-x4.html

Leave a Comment