SDF vs. MSDF vs. Slug: GPU 텍스트 렌더링 비교
요약
본 기사는 GPU 환경에서 텍스트를 렌더링하는 다양한 기술(비트맵 아틀라스, SDF, MSDF, Slug)을 비교 분석합니다. 각 방식의 장단점과 글리프 렌더링이 어려운 근본적인 이유를 설명하며, 최적의 방법은 사용 환경에 따라 달라진다고 강조합니다.
핵심 포인트
- 텍스트 렌더링은 단순한 비트맵 아틀라스보다 복잡함.
- SDF는 확대 품질을 높이나 모서리가 둥글어지는 단점이 있음.
- MSDF는 다중 채널을 사용해 모서리 보존에 유리함.
- Slug는 베지어 윤곽선과 가속 구조로 크기 변화에 대응하는 장점을 가짐.
GPU 텍스트 렌더링은 미리 생성한 텍스처를 샘플링하는 방식과 윤곽선을 직접 처리하는 방식 사이에서 화질, 메모리, 연산 비용을 절충함
SDF는 픽셀 대신 경계까지의 거리를 저장해 확대 품질을 높이지만 모서리가 둥글어지고, MSDF는 RGB 세 채널로 모서리를 보존하되 고정 해상도 아틀라스의 한계는 남음
Slug는 베지어 윤곽선과 글리프별 가속 구조를 사용해 프래그먼트 셰이더에서 픽셀 커버리지를 계산하며, 비트맵 아틀라스와 매 프레임 테셀레이션 없이 크기와 원근 변화에 대응함
대문자 R의 극단적 확대 비교에서는 Slughorn과 Rive만 깨끗한 경계를 유지함. 다만 텍스처 기반 방식은 동일 해상도, 거리장은 기본 생성 설정을 사용했고, Rive는 매 프레임 다시 테셀레이션함
단일한 최적 방식은 없음. 고정 크기 UI, 제한된 하드웨어, 사전 생성 가능한 확장형 UI, 예측하기 어려운 3D 시점, 움직이는 벡터 아트에 따라 적합한 방식이 달라짐
글리프 렌더링이 어려운 이유
확장형 폰트의 글리프는 픽셀이 아니라 닫힌 윤곽선으로 저장됨
TrueType은 2차 베지어 곡선, CFF 기반 OpenType은 3차 베지어 곡선을 사용하며 직선 구간도 함께 포함함
보통 비영 와인딩 규칙(nonzero winding rule) 으로 내부를 결정함. 픽셀에서 광선을 쏘아 윤곽선 교차를 세고, 와인딩 수가 0이 아니면 내부로 판단함
렌더러는 내부 채우기, 안티앨리어싱, 크기와 시점 변화에 따른 선명도를 동시에 처리해야 함
메뉴의 8픽셀 글자부터 원근 회전된 화면 크기의 글자까지 대응해야 함
CPU에서는 목표 크기로 한 번 래스터화할 수 있지만, GPU에서는 매 프레임 수천 개의 글리프를 임의 배율로 그리면서 재래스터화를 피하는 것이 목표임
비트맵 아틀라스: 단순하고 빠르지만 크기에 종속됨
텍스처 아틀라스는 글리프를 특정 크기로 래스터화해 공유 텍스처에 저장하고, 각 문자를 해당 영역을 샘플링하는 사각형으로 그림
텍스처 유닛만 있으면 동작하므로 속도와 이식성이 뛰어나고 구현도 단순함
저장한 크기를 넘겨 확대하면 흐려지거나 픽셀이 드러나며, 축소 시에는 밉 레벨을 준비하지 않으면 깜박임과 획 손실이 발생함
선명하게 표시할 크기마다 별도 아틀라스가 필요함
중국어/일본어/한국어처럼 수만 개 글리프를 여러 크기로 저장하면 메모리 부담이 커짐
원근 투영된 3D 표면에서도 고정 픽셀 격자를 늘려 쓰기 때문에 글자가 부드럽게 흐려짐
SDF: 거리 저장으로 확대 품질 개선
부호 있는 거리장(SDF) 은 각 텍셀에 가장 가까운 경계까지의 거리를 저장하며, 내부는 양수, 외부는 음수로 표현함
Valve의 Chris Green이 SIGGRAPH 2007에서 발표한 「Improved Alpha-Tested Magnification for Vector Textures and Special Effects」를 통해 도입됨
셰이더는 거리 0을 경계로 삼고, 임계값 주변을 부드럽게 처리해 저렴하게 안티앨리어싱을 구현함
거리가 매끄럽게 보간되므로 작은 텍스처를 크게 확대해도 깨끗한 경계를 얻을 수 있으며, 작은 텍스처와 저렴한 셰이더로 UI와 게임 HUD에 활용하기 좋음
다만 고정 해상도로 생성한 텍스처라는 한계가 있고, 쌍선형 보간이 날카로운 모서리를 둥글게 만듦
A의 꼭짓점이나 K의 꺾이는 부분이 부드러워짐
과도하게 확대하거나 글리프에 할당된 텍셀이 너무 적으면 가는 획이 끊기고 세부 형태가 뭉개짐
MSDF: 세 채널로 모서리 보존
다중 채널 부호 있는 거리장(MSDF) 은 서로 다른 경계 부분집합까지의 거리를 RGB 세 채널에 저장하고, 셰이더에서 세 값의 중앙값을 취해 모서리를 복원함
Viktor Chlumsky의 2015년 학위논문 「Shape Decomposition for Multi-Channel Distance Fields」와 2018년 논문 「Improved Corners with Multi-Channel Signed Distance Fields」에 기반함
msdfgen은 MIT 라이선스로 널리 사용됨
SDF보다 확대 시 모서리를 잘 유지하므로, 아틀라스를 미리 생성할 수 있는 확장형 텍스트에 좋은 선택임
아틀라스 생성과 메모리 예산은 여전히 필요하며, 동적 텍스트나 사용자 입력, 대규모 CJK 글리프 집합에서도 이 부담이 남음
일반 SDF보다 생성 비용이 높고, 세 채널 조회로 메모리 대역폭을 더 사용함
매우 작은 크기에서는 세부 형태를 담을 텍셀이 부족하고, 극단적 축소에서는 앨리어싱을 피하기 어려움
테셀레이션과 커버리지: 윤곽선을 GPU 기하로 변환
테셀레이션 계열은 글리프 텍스처 대신 윤곽선을 GPU가 래스터화할 기하로 변환함
Loop-Blinn은 곡선 삼각형과 픽셀별 폐기 셰이더로 2차 베지어 내부를 남기고, 스텐실 버퍼로 와인딩을 처리함. 안정적인 안티앨리어싱은 품질을 희생하는 기법 없이 구현하기 어려움
NV_path_rendering의 스텐실 후 커버 방식은 첫 패스에서 경로를 스텐실에 그리고 두 번째 패스에서 덮음. 품질은 높지만 NVIDIA 확장과 특정 하드웨어 경로에 의존함
Pathfinder는 경계를 미세 삼각형으로 분할하고, 픽셀별 부호 있는 사다리꼴 면적을 계산해 컴퓨트 패스에서 커버리지를 누적함. 타일 기반으로 최신 GPU에서 빠르게 동작함
Rive는 안티앨리어싱된 벡터 경로를 고유한 삼각형 패치로 바꾸고, 픽셀 로컬 스토리지를 사용하는 대규모 병렬 파이프라인으로 래스터화함. 2024년 오픈소스로 공개됐으며 움직이는 벡터 아트에서 120fps를 달성함
이 계열은 해상도 독립적이며, 특히 움직이는 벡터 그래픽에 적합함
기하가 바뀌면 테셀레이션을 다시 수행해야 하고, 복잡한 글리프에서는 기하 데이터가 크게 늘어남
깨끗한 해석적 안티앨리어싱 구현이 어렵고, 일부 방식은 특정 하드웨어 기능이나 확장에 의존함
Slug: 프래그먼트 셰이더에서 윤곽선 직접 계산
Slug는 Eric Lengyel이 2017년 Journal of Computer Graphics Techniques에 발표한 알고리듬임
논문 제목은 「GPU-Centered Font Rendering Directly from Glyph Outlines」임
2019년 특허를 받았으며, 2026년 3월 17일 특허를 퍼블릭 도메인에 헌납해 누구나 구현할 수 있게 됨
글리프를 2차 베지어 곡선과 직선 구간으로 작은 GPU 버퍼에 저장하고, 수평 밴드로 나눈 글리프별 가속 구조를 함께 만듦
Slug 특허가 퍼블릭 도메인으로 공개됐을 때, Claude에 상당한 공을 들여 작업을 위임하면서 Zig 기반 Slug 구현체 Snail을 만들었음. 일부 글꼴은 작은 크기에서 보기 좋게 렌더링하기 어려웠는데, TrueType의 힌팅 바이트코드가 크기별로 곡선 제어점을 픽셀 격자에 맞추는 반면 Slug는 크기별 글리프 전처리를 없애는 대신 힌팅을 하지 않기 때문임.
화면의 픽셀 밀도가 높아지면서 주요 글꼴 렌더러도 바이트코드 힌팅 대신 윤곽선을 분석하는 자동 힌팅이나 힌팅 없는 렌더링으로 이동하고 있음. Snail에는 크기별 전처리 없이 힌팅할 수 있도록 GPU 자동 힌팅을 넣었음. 글리프의 주요 특징 위치에 매듭점을 미리 계산하고, 셰이더에서 이를 이동시켜 글리프 일부를 늘리거나 압축함.
완벽하지는 않고 특히 세리프 글꼴은 어려우며, 글꼴별 조정이 도움이 됨. 라틴 문자 위주라 현재 셰이더로 처리하기 복잡한 CJK 글리프는 거의 항상 힌팅 없이 그림. 힌팅이 필요하면서도 미리 만든 비트맵을 잘라 그리는 것보다 이 방식이 나은 상황은 흔치 않음. 크기별 비용을 감수할 수 있을 때를 위해 TrueType 가상 머신도 넣었으며, DejaVu Mono는 자동 힌팅도 괜찮지만 자체 힌팅 바이트코드가 특히 뛰어남.
만드는 과정이 재미있었고 많이 배웠으며, 내가 아는 한 Slug 구현체 중 이런 자동 힌팅은 유일함. Snail 저장소에는 Snail로 직접 만든 도해로 Slug의 글리프 전처리와 렌더링 과정 대부분을 설명해 둠.
MSDF는 중간 크기부터 큰 글자까지 확대하는 데는 좋았지만, 일반적인 작은 글자 크기에서는 MSDF 해상도를 높여도 보기 좋지 않았음. 일반 GUI 앱에서는 GPU 부하도 이점을 상쇄했음. 글꼴 아틀라스는 작고 GUI 글꼴은 대부분 크기가 고정돼 있기 때문임.
반면 복잡한 SVG 경로 렌더링에는 아주 유용함. 64×64 SVG 별 모양도 감수할 만한 품질 손실로 전체 화면까지 확대할 수 있고, 128×128이면 거의 손실이 없음. 내 GUI 라이브러리에서는 아이콘 등의 복잡한 SVG 경로를 이 방식으로 렌더링하고 GPU에서 개별 경로를 합성함.
Lottie 애니메이션도 상당 부분 구현 가능함. 직접 해본 것은 기초 데모 수준이지만, MSDF 확대·축소, 그림자, 가장자리 흐림, 외곽선 등을 100FPS 이상으로 처리할 수 있음.
관련 설명: https://forum.nim-lang.org/t/14062#85314.
렌더링 예제: https://github.com/elcritch/figdraw#msdf-bitmap-based-sdf-re....
Lottie 작업: https://github.com/elcritch/lotty.
SDF 텍스트 렌더링을 구현했을 때 가장 좋았던 것은 효과를 쉽게 추가할 수 있다는 점임. 셰이더 몇 줄로 외곽선과 가장자리 부드럽게 처리하기, 즉 안티앨리어싱을 구현했음. 확대했을 때 모서리가 덜 날카로워도 괜찮아서 MSDF는 시도하지 않았고, 이런 효과가 MSDF에서도 잘 되는지는 모르겠음.
Slug는 주어진 점이 안인지 밖인지만 판단하고 거리는 알려주지 않는 것처럼 보임. 그렇다면 고해상도 화면이나 큰 크기에는 좋겠지만, SDF식 효과를 구현하기 어렵고 안티앨리어싱도 별도로 처리해야 할 듯함.
Slug도 픽셀 피복률을 추정한 0~1 값으로 안티앨리어싱을 제공함. 다만 베지어 곡선까지의 수평·수직 거리에 기반한 선형 추정이라, 거리가 멀어지면 정확하지 않아 (M)SDF처럼 외곽선이나 그림자 효과에 쓰기는 부적합함. 1픽셀 외곽선은 괜찮지만 현대 화면에서는 너무 가늘어 잘 보이지 않음.
이차 베지어 곡선과 점 사이의 최소 거리를 구하려면 삼차방정식을 풀어야 하지만, Slug는 픽셀마다 이차방정식만 풀면 되므로 성능 차이가 큼.
현재 하드웨어의 픽셀 밀도를 생각하면 픽셀 단위 최적화에만 집중하는 기법도 꽤 합리적으로 보임. 회색조 처리가 필요하면 다른 기법을 쓸 수 있음.
다만 회색조 안티앨리어싱 외에도 유용한 효과가 있음. 어떤 배경에서도 읽히도록 영상 자막에 넣는 외곽선 같은 효과를 Slug가 얼마나 잘 처리할지 궁금함.
LLM이 생성한 글을 읽는 일이 갈수록 지겨워짐.
사람이 직접 쓰는 글을 지지하지만, 적어도 내게는 대부분 LLM이 작성한 글을 읽는 일이 일상이 돼 가고 있음. 이 글의 작성 방식에는 동의하지 않더라도 더 알고 싶었던 주제라 끝까지 읽었음.
저자에 대한 인상은 나빠지고, 글쓰기에 더 많은 생각과 노력을 쏟지 않는 선택이 안타깝기도 함. 하지만 이 추세는 계속될 것이고, 크게 항의해도 별 효과가 없었으며 앞으로도 달라질 것 같지 않음.
어떤 특징을 보고 LLM이 쓴 글이라고 판단했는지 궁금함. 나는 구체적인 징후를 잘 알아보지 못함.
읽지 않으면 됨. 이런 불평을 보는 것도 지겨움. 이제는 이런 모델과 평생 상호작용하며 살아갈 세상임. 모델의 문체나 성격에 구체적인 피드백을 주는 것은 좋지만, 지금 같은 불평은 건설적이지 않아 원문보다도 나쁘다고 봄.
나도 GPU 곡선 렌더러 Windfoil을 개발 중임. 방향을 정해 탐색하는 과정에서 Fable 5가 처음 제안한 수식을 기반으로 함. Slug와 비슷한 부분이 있고 항상 더 빠르지는 않지만, 밴드를 두 개 대신 하나만 사용해 셰이더 저장 공간을 덜 쓰며 박스 필터 기준값에 더 가까운 안티앨리어싱을 제공함.
게임·그래픽 개발자라면 관심이 있을 수 있음. https://github.com/texel-org/windfoil-algorithm.
이 알고리즘의 안티앨리어싱은 어떻게 동작하는지 궁금함. Slug가 밴드 두 개를 쓰는 이유가 바로 안티앨리어싱이므로, 하나만 쓴다면 방식이 다르거나 주 방향을 벗어난 탐색의 효율이 낮을 듯함.
Slug 셰이더는 수평·수직으로 광선을 하나씩 쏴 픽셀이 글리프 안인지 밖인지 판별함. 안팎 판별만 한다면 하나로 충분하지만, 안티앨리어싱에는 가까운 경계까지의 거리도 유용함. 수평 광선이 수평 글리프 경계와 평행하면 교차점이 나오지 않으므로, 두 방향의 결과를 혼합해 양쪽 모두에서 동작하게 함. 여기서 밴드는 각 셀에 어떤 글리프 부분이 들어 있는지 기록한 가속 자료구조임.
그래도 근사치임. 진짜 기준값을 얻으려면 슈퍼샘플링으로 실제 픽셀 내부의 여러 점에 대해 글리프 안팎을 검사해야 함. 경계 픽셀에서는 일부 점만 내부에 들어가므로 글리프 모양에 따른 부드러운 값을 얻을 수 있음.
이 글에는 부정확한 정보가 있음. MSDF 아틀라스를 정적으로 미리 생성할 필요는 없음. 글리프를 비동기로 아틀라스에 업로드할 수 있다면 CJK 때문에 아틀라스가 거대해진다는 문제는 피할 수 있음. C 라이브러리에서는 윤곽선 추출을 비동기로 처리하기가 더 어려워, 비동기 파이프라인이 주로 아틀라스 업로드에 한정됨.
MSDF 렌더링 자체는 저렴하고 GPU 셰이더 없이 CPU에서도 쉽게 가능함. 비싼 부분은 아틀라스 생성과 업로드지만, 일반 비트맵 텍스처이므로 글꼴별·문자별로 한 번만 부담하면 됨. 대부분의 확대 배율에서 가장자리도 선명하며, 작은 글자는 어차피 단순한 CPU 래스터화가 더 적합함. MSDF와 작은 글꼴용 래스터화 대체 경로에 비해 Slug의 이점이 큰지는 모르겠음. 더 정확한 것은 맞지만, 약간의 해상도 개선을 위해 훨씬 복잡하고 GPU에 의존적인 렌더링을 택할 가치가 있는지는 의문임. 명백히 AI가 쓴 글을 믿기보다는 시간이 날 때 직접 시험해 보고 싶음.
MSDF에는 거대한 아틀라스, 동적 재생성, 제한된 글리프 집합 같은 선택지 중 적어도 하나의 단점은 따름. 하지만 글에서는 모든 단점이 동시에 적용되는 것처럼 써서 오해를 부름. Slug는 멋지지만 내 렌더링 프로젝트에도 맞지 않음.
취미로 Slug처럼 픽셀 단위로 정확한 안티앨리어싱을 제공하는 글꼴 래스터화 알고리즘 두 개를 작성했음. Slug의 베지어 곡선 근 분류 기법 대신 곡선을 단조 구간으로 분할함. 그러면 감김 수(winding number) 계산이 단순해지고 여러 픽셀을 병렬 처리할 수 있어, GPU의 워프·웨이브·서브그룹 연산과 CPU의 SIMD에서 잘 동작함.
곡선을 나누면 처리할 베지어 곡선이 늘어 불리해 보이지만 병렬화 기회가 생김. 글꼴의 베지어 곡선은 원래 단조인 경우가 대부분이라 증가량도 작음. Sebastian Lague의 재미있고 엄밀함에만 집중하지는 않는 영상에서 영감을 받았음.
첫 번째 최적화는 곡선의 경계 상자와 UV 공간의 직사각형 픽셀 영역을 비교해 곡선 계산 자체가 필요한지 빠르게 판별하는 것으로, GPU 워프 단위로 가능함. 두 번째는 회전·기울임·원근이 없는 축 정렬 변환에만 적용됨. 이차방정식 풀이에서 계산량의 30% 이상을 차지하는 제곱근과 나눗셈을 픽셀별이 아니라 행·열별로 계산해 횟수를 n²에서 2n으로 줄임.
두 최적화 모두 베지어 곡선의 도함수가 0이 아니어야 한다는 단조성의 수학적 불변 조건을 이용함. 모든 베지어 곡선은 de Casteljau 알고리즘으로 안정적으로 단조 구간으로 나눌 수 있음. 간단한 벤치마크에서는 GPU의 Slug나 CPU의 빠른 래스터화 알고리즘과 비교해 경쟁력 있는 결과가 나옴. 이런 빠른 CPU 알고리즘은 힌팅 등을 갖춘 정교한 알고리즘보다 대략 한 자릿수 배율 이상 빠름.
아직은 개별 문자를 그리는 정리되지 않은 셰이더와 CPU 코드, 몇 가지 벤치마크뿐임. 완전한 텍스트 렌더링 시스템으로 만들려면 일이 많고, 코드 예제를 곁들인 자세한 글도 쓰고 싶지만 시간을 내지 못함. 격려나 래스터화 알고리즘에 관한 깊은 논의 모두 환영함.
글리프 렌더링이 어려운 또 다른 이유는 힌팅임. FreeType 같은 전통적인 렌더러는 픽셀 격자를 고려해 곡선을 조금씩 조정하고 격자에 맞춤. 글에서 다룬 기법 모두 GPU에서는 이를 구현하기가 꽤 어려움.
CJK의 수만 개 글리프를 여러 크기로 미리 생성하면 메모리가 크게 늘어난다는 문제는 현재 보이는 글리프만 CPU에서 동적 아틀라스로 생성해 해결할 수 있음.
거리장 샘플 사이의 보간이 실제 곡선과 어긋나 생기는 패임은 SDF의 화면 공간 미분값을 셰이더에서 사용하면 해결되지 않을까? 이론적으로 픽셀 중심의 SDF 값과 그 값의 화면 공간 기울기 벡터만 있으면 경계 픽셀의 부분 피복률을 계산할 정보가 충분할 듯함.
화면 위에 정보를 표시하는 OSD 정도만 필요하다면 운영체제 라이브러리로 텍스처에 렌더링하는 편이 좋은 이유이기도 함.
Slug 시스템을 직접 구현한 것인지 궁금함. 글에서는 근의 적격성 판정을 언급만 하는데, 이 판정의 역할은 무엇인지? 감김 수만 계산한다면 Slug가 그렇게 마법 같은 기법으로 보이지는 않음.
글에서 테셀레이션과 Rive를 혼동하는 듯한 부분이 있어 헷갈림. 둘이 같은 것인지? 비전문가인 내가 이해하기로 Rive는 테셀레이션 방식의 렌더러 구현체임. Rive가 지원하지 않더라도 일반적인 테셀레이션 방식은 임의의 변환을 완벽하게 지원할 수 있지 않은지 궁금함.
비교표에서도 Slug가 이기거나 비긴 항목만 초록색으로 강조함. 공정하려면 항목별 승자를 모두 강조해야 하지 않을까? 메모리 사용량이 낮은 Rive가 보통인 Slug보다 나은 것 아닌지?
특허를 퍼블릭 도메인으로 공개한 것은 좋지만, 이미 공개한 알고리즘을 2년 뒤에 특허 출원할 수는 없음. 실제로는 공개 전에 출원했고, 글에는 2019년에 특허가 등록됐다고 써야 했을 듯함.
AI가 작성한 글이라 이 정도의 정확성까지 기대하기는 어려운 것일지도 모르겠음.
Slug 알고리즘의 가출원일은 2017년 3월 27일이며, 최종 특허의 설명 부분 첫머리에도 포함돼 있음. 가출원은 공개한 내용 전체에 우선일을 설정하고, 발명자에게 정식 출원을 마칠 1년을 줌. 여기서는 알고리즘 전체가 해당함.
JCGT 논문은 우선일 이후인 2017년 6월 14일에 공개됐고, 정식 출원은 1년 기한 안인 2018년 2월 1일에 제출됐음. 미국 특허청은 약 1년 반 뒤인 2019년 8월 6일에 특허를 등록함.
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기