遠くの床や壁が、動くとジリジリと沸き立つように見えることがあります。あれは「1つの画面ピクセルにテクスチャの点(テクセル)が何個も詰まってしまう」ときに起きる現象です。ミップマップ(mipmap)は、この問題をあらかじめ縮小した画像を用意しておくという素朴なアイデアで解決します。実際に動かして、なぜ効くのかを見ていきましょう。
テクスチャ(texture)は、面に貼りつける画像です。その1マスを テクセル(texel = texture element)と呼びます ── 画面側の1マスが pixel なのに対し、テクスチャ側の1マスが texel です。面のどこにテクスチャのどこを貼るかは UV座標(0〜1)で決めます。GPU が実際に色を取り出すことを サンプリング(sampling)と言います。
面が奥に遠ざかると、画面の1ピクセルが覆うテクスチャの範囲がどんどん広がります。1pxに何十テクセルも入るのに、GPU がその中の1点だけを選んで色にすると、どのテクセルに当たるかは運任せ ── カメラが少し動くだけで当たるテクセルが入れ替わり、ちらつき(aliasing / モアレ)になります。下は同じ床を、左=素朴な1点サンプリング、右=ミップマップありで並べたものです。再生して、奥の方を見比べてください。
「テクセル密度」を上げるほど1pxに入るテクセルが増え、左のちらつきが強くなります。右はどの距離でも安定 ── これがミップマップの効果です。
この現象は信号のサンプリング定理と同じ話です。高い周波数(細かい模様)を、粗い間隔(まばらなピクセル)で拾うと、元にない偽の模様が現れる。これが エイリアシング(aliasing)。答えは「拾う前に、細かさを間引いておく=縮小しておく」ことです。
ミップマップは、元テクスチャを半分・その半分…と縮小した画像を、1×1になるまで事前に作って束ねたものです。各レベルは1つ上を平均して作るので、細かい模様はレベルが進むほど「ならされて」いきます。描画時は、その面の遠さ(1pxが覆う範囲)に応じてちょうどいい細かさのレベルを選んで貼ります。
"mip" はラテン語 multum in parvo(小さな中にたくさん)の頭字。ちなみに縦横が2の累乗(power-of-two)だと半分に割り切れて連鎖がきれいに作れるため、ミップ前提のテクスチャは 1024² のようなサイズが好まれます。
縮小(minification)の相棒が拡大(magnification)です。テクスチャに近づいて1テクセルが何ピクセルにも広がるとき、どう色を埋めるか。最近傍(nearest)は一番近い1テクセルをそのまま ── カクカクのドット絵に。バイリニア(bilinear)は隣り合う4テクセルを重み付き平均 ── なめらかに。下のトグルで見比べてください。
床を浅い角度で見ると、1pxが覆う範囲は奥行き方向に細長い長方形になります。ミップは正方形単位で縮小するので、細長さに合わせると横方向まで潰れ、地面の遠景が過剰にボケてしまう。異方性フィルタリング(AF)は、その細長い方向に沿って複数回サンプリングし、必要な方向だけ細かさを保ちます。UEのテクスチャ設定にある Aniso / r.MaxAnisotropy がこれ。2x〜16x と表記され、数字は方向に沿う最大サンプル数の目安です。
ミップは画質だけでなくメモリ管理の要でもあります。UEは「今カメラから遠いテクスチャは、細かい上位ミップを VRAM に載せない」という判断を毎フレーム行います ── これが テクスチャストリーミング。近づけば上位ミップを読み込み、遠ざかれば捨てる。読み込みが間に合わないと、一瞬ボケたテクスチャが表示される「ミップ落ち」が起きます。
確認コマンド例:stat streaming でプールの使用量、Ctrl+Shift+,(GPU Visualizer)で描画コスト。ミップ落ちが気になるときは、まずプールサイズと LOD Bias を疑うのが定石です。