「ゲーム用テクスチャの活用 ~第1部:基本~」の記事で書いてあるのを、シェーダーの最適化にどう応用するか、こちらで説明させていただきます。
目次
テクスチャパッキングとは
テクスチャ パッキングとは、複数のグレイスケールマップ (AO、粗さ/滑らかさ、メタリック、高さなど) を 1 つのテクスチャに組み合わせることです。
テクスチャパッキングの理由
テクスチャをパッキングすることによって、下記の点でゲームを最適化することが出来ます。
- メモリ量の消費を減少できます(ただし、圧縮形式やカラーチャンネルの数をよく考える必要があります)
- テクスチャサンプラーの数を減少できます (これはシェーダの計算速度に大きな影響を与えます)
- ファイルの断片化を防げます (これはデータ交換速度に影響します)
最も一般的な3つ及び4つのチャネルのテクスチャパッキング方法例は以下になります。OSM(Unity)/ORM(Unreal Engine) 、MOHS(Unity)/MOHR(Unreal Engine)
場合によって、オリジナルのテクスチャパッキング方法を使っても構いません。
例えば、Metallic Mapを全く使うつもりが無い場合、場合によってはチャンネルの数を減らしても良いですし、Metallic Mapの代わりに違うマップ(Specular Occlusion Map、Tint Maskなど)を入れた方が良いです。
テクスチャの種類によっては、下記のような設定が必要になりますので注意してください。
・Normal MapのテクスチャをNormal Mapとしてマークする必要があります。
・Albedo、色有のEmissiveのテクスチャなどをsRGB色空間で利用する必要があります。
・それ以外のテクスチャをリニア色空間で利用する必要があります。(パッキングされたテクスチャやグレイスケールのEmissiveを含み)
・テクスチャ サンプラーは、サンプリングするテクスチャに応じて設定する必要があります。
Unity
テクスチャをエンジンにインポートするときは、テクスチャの種類に応じて各テクスチャを設定する必要があります。
シェーダを作成するとき、利用するテクスチャに応じてSample Texture 2Dのノードを設定する必要があります。
Unreal Engine
また、テクスチャをエンジンにインポートするときは、テクスチャの種類に応じて各テクスチャを設定する必要があります。
Texture SampleというノードのSample Typeは利用するテクスチャによって設定する必要があります。
高度なテクスチャパッキング(ノーマルマップのパッキング)
ゲーム エンジンは、ノーマル マップに2つのテクスチャ チャネルを使用しています。(詳しくは「ゲーム用テクスチャの活用 ~第1部:基本~」という記事でご確認ください)
そのノーマルマップには、もう2つのグレイスケールマップほどパッキングすることが出来ます。
ノーマルマップにパッキングすることにより、テクスチャサンプラーの数を減少させられ、シェーダが高速化され、ファイルの断片化が減少し、場合によってはメモリ消費量を減少させることが出来ます。
注意点:このようにパッキングされたノーマルマップをリニア色空間のテクスチャとして利用する必要があります。
Unity
テクスチャの設定は下記になります。
シェーダーは下記になります。(ノーマルマップの処理を画像内のコメントで説明しています)
結果を一般的なDXTnmのノーマルマップを利用した結果と比較してみましょう。
DXTnm圧縮形式のアルゴリズムはDXT5|BC3の圧縮形式のアルゴリズムと似ているため、品質はほとんど同じです。カメラをモデルから離すと、違いは全く分からないと思います。
最適化のヒント:上記のグラフでは、ビットマップのベクターの[0,1]の範囲を、ノーマル用の[-1,1]の範囲にコンバートすることを分かりやすくするためにRemapノードを使用しています。 ただ、もしシェーダーのパフォーマンスを増加させたいのであれば、Remapノードの代わりに下記のノードのチェインを利用することをおすすめします。なぜならRemapノードは幅広い用途がありますので、下記のノードのチェインよりパフォーマンスが少し低いですから。
Unreal Engine
もしUnreal Engineはパッキングされたノーマルマップを自動でNormal Map(BC5)とマークしていたら、Compression Settingsを手でDXT5|BC3に変更し、sRGBのチェックボックスを外す必要があります。
テクスチャの設定は下記になります。
シェーダーは下記になります。(ノーマルマップの処理を画像内のコメントで説明しています)
結果を一般的なBC5のノーマルマップを利用した結果と比較してみましょう。
品質はほとんど同じですが、前のテストより圧縮のアーティファクトの違いが見えやすいです。その理由は、BC5のノーマルマップの圧縮形式がDXTnmより品質が高いためです。しかし、カメラをモデルから離すと、圧縮のアーティファクトの影響はあまり分からないと思います。
最適化のヒント:先程のUnityと同じですが。上記のグラフでは、ビットマップのベクターの[0,1]の範囲を、ノーマル用の[-1,1]の範囲にコンバートすることを分かりやすくするためにRemapノードを使用しています。 ただ、もしシェーダーのパフォーマンスを増加させたいのであれば、Remapノードの代わりに下記のノードのチェインを利用することをおすすめします。なぜならRemapノードは幅広い用途がありますので、下記のノードのチェインよりパフォーマンスが少し低いですから。
まとめ
ノーマルマップのパッキングはよく考えて利用してください。出来る限り圧縮のアーティファクトないノーマルマップを使う必要がある場合は、ノーマルマップのパッキングを使用しない方が良いです。
ノーマルマップパッキングの使用が適している場合:
- テクスチャのノイズが多い場合 (石、岩、植物など)
- カメラから離れた背景オブジェクトの場合
- Terrainのマテリアルの場合
ノーマルマップパッキングを使用しない方が良い場合:
- きれいで滑らかな表面を作りたい場合 (Sci-Fiモデリングやハードサーフェスなど)
- キャラクターのモデリングの場合
- 最高品質のノーマルマップが必要な場合
パフォーマンスの比較
OSM/ORMを使うシェーダーのスペックとパッキングしたノーマルマップを使うシェーダーのスペックを比べましょう。
シェーダー | Pixel Shader Texture Lookups 数 | 全てのメモリ量 | Shader Instructions数 |
OSM/ARM | 3個(DXT1|BC1x2,DXTnm/BC5x1) | 683Kb | 241個 |
パッキングしたノーマルマップ | 2個(DXT1|BC1x1,DXTnm/BC5x1) | 511Kb | 238個 |
Texture Samplerの数やShader Instructionsの数によると、パッキングしたノーマルマップを使うシェーダーは、ORM/OSMのパイプラインよりも、データ交換速度も高くて、シェーダの計算速度も高いです。それに、メモリ量の消費も25%で少ないです。
参考サイト
Polycount Wiki – Normal Map Compression
Get Learnt w/ Chunck – What Is Channel Packing? | Channel Packing Series