Primer 02 — Textures & Mipmaps
遠くの面はなぜちらつく / Minification & Mipmapping

テクスチャと
ミップマップ

遠くの床や壁が、動くとジリジリと沸き立つように見えることがあります。あれは「1つの画面ピクセルにテクスチャの点(テクセル)が何個も詰まってしまう」ときに起きる現象です。ミップマップ(mipmap)は、この問題をあらかじめ縮小した画像を用意しておくという素朴なアイデアで解決します。実際に動かして、なぜ効くのかを見ていきましょう。

Texel Minification Mipmap Filtering
まず用語だけ — 読む前の30秒
テクセル texel
テクスチャ側の1マス。画面の1ピクセル(pixel)に対応する語。
サンプリング sampling
面のこの点に貼る色を、テクスチャから取り出すこと。
ミップ/ミップマップ mipmap
元テクスチャを半分ずつ縮小して束ねた画像列。遠い面用の「縮小済みの控え」。
エイリアシング aliasing
細かい模様を粗く拾って出る、ちらつき・モアレ。ミップが防ぐ相手。
LOD level of detail
面の遠さに応じて選ぶミップの段。遠いほど粗い段を使う。
フィルタリング filtering
色の取り出し方。最近傍(カクカク)/バイリニア(なめらか)など。
※ 各語の詳しい定義は、ページ末尾の用語集(クリックで展開)にもあります。

§1 — Texel & UV

まず「テクスチャ」とは

テクスチャ(texture)は、面に貼りつける画像です。その1マスを テクセル(texel = texture element)と呼びます ── 画面側の1マスが pixel なのに対し、テクスチャ側の1マスが texel です。面のどこにテクスチャのどこを貼るかは UV座標(0〜1)で決めます。GPU が実際に色を取り出すことを サンプリング(sampling)と言います。

テクスチャ空間 · UV
U=01 texelU=1
sampling
UVで
色を取り出す
画面 · 面に貼られた結果
面(mesh)
奥に傾くほど1pxに多くのtexelが入る →

§2 — The Problem

なぜ遠くの面はちらつくのか

面が奥に遠ざかると、画面の1ピクセルが覆うテクスチャの範囲がどんどん広がります。1pxに何十テクセルも入るのに、GPU がその中の1点だけを選んで色にすると、どのテクセルに当たるかは運任せ ── カメラが少し動くだけで当たるテクセルが入れ替わり、ちらつき(aliasing / モアレ)になります。下は同じ床を、左=素朴な1点サンプリング、右=ミップマップありで並べたものです。再生して、奥の方を見比べてください。

奥に向かって遠ざかる床(同じテクスチャ)
ミップなし point sampling
奥がジリジリ沸き立つ。カメラが動くたび当たるテクセルが変わるため。
ミップあり mipmapped
奥は事前に縮小・平均された色を使うので落ち着いて溶け込む。

「テクセル密度」を上げるほど1pxに入るテクセルが増え、左のちらつきが強くなります。右はどの距離でも安定 ── これがミップマップの効果です。

この現象は信号のサンプリング定理と同じ話です。高い周波数(細かい模様)を、粗い間隔(まばらなピクセル)で拾うと、元にない偽の模様が現れる。これが エイリアシング(aliasing)。答えは「拾う前に、細かさを間引いておく=縮小しておく」ことです。


§3 — The Chain

ミップマップ=縮小画像の連鎖

ミップマップは、元テクスチャを半分・その半分…と縮小した画像を、1×1になるまで事前に作って束ねたものです。各レベルは1つ上を平均して作るので、細かい模様はレベルが進むほど「ならされて」いきます。描画時は、その面の遠さ(1pxが覆う範囲)に応じてちょうどいい細かさのレベルを選んで貼ります。

ミップの連鎖(左が元テクスチャ / Level 0)
追加メモリ
+約33%
なぜ 1/3 で済む
各レベルは面積が 1/4 ずつ。1 + 1/4 + 1/16 + … = 4/3 に収束するので、全ミップを足しても元の約 33% 増えるだけです。この小さなコストで、遠景のちらつきとメモリ帯域の両方が改善します。

"mip" はラテン語 multum in parvo(小さな中にたくさん)の頭字。ちなみに縦横が2の累乗(power-of-two)だと半分に割り切れて連鎖がきれいに作れるため、ミップ前提のテクスチャは 1024² のようなサイズが好まれます。


§4 — Filtering

サンプリングの補間 ── フィルタリング

縮小(minification)の相棒が拡大(magnification)です。テクスチャに近づいて1テクセルが何ピクセルにも広がるとき、どう色を埋めるか。最近傍(nearest)は一番近い1テクセルをそのまま ── カクカクのドット絵に。バイリニア(bilinear)は隣り合う4テクセルを重み付き平均 ── なめらかに。下のトグルで見比べてください。

8×8 テクスチャを ×30 拡大
トライリニア(trilinear)は縮小側の相棒。上下2つのミップレベルからそれぞれバイリニアで取り、さらにその2つを混ぜてレベルの境目を目立たなくします。§2 の右の床がなめらかなのはこの補間のおかげです。
異方性フィルタリング / Anisotropic

斜めから見た床が「奥だけ」ぼやける問題

床を浅い角度で見ると、1pxが覆う範囲は奥行き方向に細長い長方形になります。ミップは正方形単位で縮小するので、細長さに合わせると横方向まで潰れ、地面の遠景が過剰にボケてしまう。異方性フィルタリング(AF)は、その細長い方向に沿って複数回サンプリングし、必要な方向だけ細かさを保ちます。UEのテクスチャ設定にある Anisor.MaxAnisotropy がこれ。2x〜16x と表記され、数字は方向に沿う最大サンプル数の目安です。


§5 — In UE

UEでの実際 ── テクスチャストリーミング

ミップは画質だけでなくメモリ管理の要でもあります。UEは「今カメラから遠いテクスチャは、細かい上位ミップを VRAM に載せない」という判断を毎フレーム行います ── これが テクスチャストリーミング。近づけば上位ミップを読み込み、遠ざかれば捨てる。読み込みが間に合わないと、一瞬ボケたテクスチャが表示される「ミップ落ち」が起きます。

Streaming Pool
帯域とVRAMの予算
載せられるミップの総量には上限(プール)がある。溢れると全体のミップが一段落ちて画面がボケる。r.Streaming.PoolSize で調整。
LOD Bias
わざと段をずらす
選ぶミップを人為的に上げ下げする値。+1で一段粗く(軽く・省メモリ)、−1で一段細かく(重く・くっきり)。品質と負荷のつまみ。
Virtual Texturing
必要な断片だけ
巨大テクスチャを小さなタイルに分け、実際に画面に映る部分のミップだけを都度読み込む仕組み。超高解像度をVRAM節約で扱える。

確認コマンド例:stat streaming でプールの使用量、Ctrl+Shift+,(GPU Visualizer)で描画コスト。ミップ落ちが気になるときは、まずプールサイズと LOD Bias を疑うのが定石です。


Glossary — 用語

よく出る言葉