本文へスキップ

ラフネスをスムースネスに変換 · Unity向けチャンネル反転

Unityのメタリックワークフローはスムースネスを読みますが、多くのテクスチャツールはラフネスを書き出します。反転は単純な計算ですが、バイトが残ることが前提です。テクスチャラボはPNGを直接読むので、保存される値は正確に255 − ラフネスです。

Unityのメタリックワークフローはスムースネスを読みますが、glTF・Godotや多くのPBRテクスチャの素材はラフネスを提供します。Unityのシェーダーライブラリは知覚ラフネスを1 − スムースネスと定義しているので、変換は厳密な反転です。スムースネス = 1 − ラフネス、8ビットのPNGでは255 − 値です(ラフネス80 → スムースネス175)。リニアなデータのまま反転し、結果をUnityが読む場所、つまりURP・組み込みのメタリックマップのアルファか、HDRPのマスクマップのアルファに入れます。Nerulioはブラウザ上でチャンネルをバイト単位で正確に反転し(測定済み)、Unityへの読み込みは実行していません。

テクスチャマップをドロップ(PNGはチャンネルのバイトをそのまま保持)
テクスチャラボを開く
ブラウザ内で処理し、ファイルはアップロードされません。
テクスチャラボ:CC0テクスチャから生成したノーマルマップとOpenGL/DirectXの規約表示
CC0アセット(ambientCG)を開いた実際のテクスチャラボ画面です。
出力
  • Channel PNGs測定確認· numpy
  • Normal map測定確認· numpy
  • Packed mask / ORM測定確認· Pillow
  • Engine import未検証

ラフネス・スムースネスと、ガンマが問題になる理由

2つのマップは同じ性質を逆側から表します。ラフネス0が鏡、1が完全に粗い面で、スムースネスは1が鏡です。UnityのレンダーパイプラインのシェーダーライブラリではPerceptualSmoothnessToPerceptualRoughnessが1 − スムースネスを返し、その後で値を2乗します(PerceptualRoughnessToRoughness)。どちらのマップもアーティストが描く知覚値を保存しているので、反転するだけで、2乗や平方根はとりません。

反転が厳密なのはリニアなデータのときだけです。ラフネスマップのバイトは色ではなく数値です。カラーマネジメントをする編集ソフトやsRGBをオンにしたインポーターなど、どこかでsRGBとして扱われると値がずれます。スムースネスのバイト175はリニアなら0.686ですが、sRGBとしてデコードすると約0.429です。結果はsRGB (Color Texture)をオフにして読み込み、ガンマカーブで「補正」しないでください。

スムースネスの入れ先。URPのLitはMetallic Mapのアルファ(Smoothness › Source Metallic Alpha、既定)かベースマップのアルファから読みます。URPのチャンネルパックテクスチャと組み込みStandardシェーダーのメタリックマップはAに、HDRPのマスクマップもAに持ちます。グレーのスムースネスPNG単体は、カスタムシェーダーかパック用にしか使えません。

グロス(glossiness)マップはすでにスムースネスです。そのためNerulioの名前のルールはglossをスムースネスに分類し、反転するとラフネスに戻ってしまいます。

ラフネス
0 = 鏡のように滑らか、1(255)= 完全に粗い。glTF・Godot・Unrealの規約。
スムースネス
Unityの逆値:1 − ラフネス。
知覚ラフネス
描いて保存する値。エンジンはライティングの前に2乗します。
リニア(非カラー)データ
sRGBデコードせず保存値のまま使う値。

例:数値で見る

ラフネスのバイト4つを、ラボが書くとおりに反転した値です。

ラフネスのバイト  ラフネス    スムースネス = 1 − r   スムースネスのバイト (255 − v)
  0              0.000       1.000                255
 80              0.314       0.686                175
128              0.502       0.498                127
255              1.000       0.000                  0

同じバイト175をsRGB (Color Texture)オンで読み込むと:
175 / 255 = 0.686 → sRGBデコード → 0.429   (目に見えて粗い面)

2回反転すると元のバイトに戻るので、変換で何も失いません。ラボはダウンロードしたファイルで、保存した平面がすべてのテクセルで255 − ラフネスと一致することを測定しました。

ラフネスからUnityのスムースネスへ

手元にあるものNerulioでUnityで
グレーのラフネスPNGチャンネル:ラフネスと表示されたチャンネルを反転して保存グレーのスムースネスマップ(パックやカスタムシェーダー用)
ラフネス + メタリックのPNGマスクパッカー、「Unity メタリック+スムースネス」プリセット:R ← メタリック、A ← ラフネス反転URP・組み込み:Metallic Map、スムースネスはMetallic Alphaから
ラフネス + メタリック + AO(+ ディテール)マスクパッカー、「Unity HDRP マスク」プリセット:Rメタリック、G AO、Bディテールか0、Aラフネス反転HDRP Lit:Mask Map
ORMテクスチャ(Gにラフネス)チャンネル、ORM配置:Gを反転して保存し、Aにパックメタリックマップかマスクマップのアルファ
グロス・glossinessマップ反転不要:すでにスムースネスそのままAにパック

できること

  • 正確に255 − vを書くチャンネル反転(ダウンロードしたファイルをnumpyで確認)。
  • ブラウザのCanvasなら失う、完全に透明なテクセル下のバイトを保持。
  • Unity HDRP(マスクマップ)やURP(アルファにメタリック+スムースネス)での意味つきのチャンネルプレビュー。
  • パッカーへ続けて:スムースネスはURPメタリックマップかHDRPマスクマップのアルファに入ります。

使い方

  1. ラフネスマップ(PNG)をドロップ。
  2. チャンネルを反転。
  3. スムースネスマップを保存するか、マスクパッカーを開く。
  4. Unityプリセットでメタリック(HDRPならAOも)と一緒にパック。

出力されるファイル

ファイル用途
rock_roughness-g-inverted-roughness.png反転した平面の8ビットグレーPNG。名前は元の役割(roughness)の前にinverted-が付くので、中身はスムースネスです。
rock_metallic-mask.pngマスクパッカーの結果:Rにメタリック、Aに反転したラフネス(HDRPならAOとディテールも)。Unityのマテリアルにそのまま使えます。

Unityが読む場所にスムースネスを入れる

Unityの手順はUnity 6・HDRP 17.1のドキュメントに従ったもので、Nerulioは読み込みを実行していません。

  1. マスクパッカーを開き、メタリックマップを1枚目、ラフネスマップを2枚目に入れて、「Unity メタリック+スムースネス」(URP・組み込み)か「Unity HDRP マスク」を選びます(HDRPでディテールマスクがなければAOをGに、Bは0に)。
  2. Aチャンネルの「反転」を付け、パックしたPNGをダウンロードします。URPでアンビエントオクルージョンも使うなら手動割り当てに切り替え、AOのファイルをGにします。
  3. Unityでテクスチャを選び、sRGB (Color Texture)をオフにします。HDRPではTexture TypeもDefaultにします。
  4. URP Lit:Workflow Mode Metallic、テクスチャをMetallic Map(GにAOがあればOcclusion Mapにも)に割り当て、Smoothness › SourceはMetallic Alphaのままにします。
  5. HDRP Lit:Surface InputsのMask Mapに割り当てます。調整が必要ならSmoothness Remappingのスライダーで範囲を合わせ直します。

結果の確認

  • パックしたPNGをチャンネル分解で開きます。Aはどこでも255 − ラフネスのはずです(ラフネス80のテクセルは175)。
  • Unityで粗い部分(にぶく広いハイライト)と磨いた部分(鋭いハイライト)を比べます。逆なら反転していないか、2回反転しています。
  • テクスチャのインスペクターで、パックマップのsRGB (Color Texture)がオフになっているはずです。

トラブルシューティング

症状考えられる原因確認方法対処
全体が濡れたよう、鏡のように見えるラフネスを反転せずアルファに入れたAを分解してラフネスマップと比べると、反転されず同じAの「反転」を付けてパックし直す
テクスチャリングソフトより面が粗く見えるパックマップをsRGBで読み込んだ(175が約0.429になる)UnityのインスペクターでsRGB (Color Texture)がオンオフにする。ガンマカーブで補正しない
スムースネスマップが効かないSmoothness › SourceがAlbedo Alpha、またはどこかのツールがアルファなしでPNGを保存したマテリアルのインスペクターを確認し、PNGを分解してAが定数255でないか見るSourceをMetallic Alphaにし、パックマップはRGBAのPNGで保存
つやのあった部分が粗くなったグロスマップグロスマップはすでにスムースネスなのに反転した元のファイル名にglossやglossinessがある反転せずにパックする
URPのマテリアルが環境光で暗くなるパックテクスチャをOcclusionにも割り当てたが、Gが0で書かれているGを分解すると定数0手動割り当てでAOのファイルをGにするか、Occlusion Mapを空にする

ほかの方法

方法こんなときに向いています
画像編集ソフトで反転カラーマネジメントをしないソフトで8ビットのグレーのラフネスマップを扱うとき。反転結果は同じ255 − vですが、アルファへのパックは自分で行う必要があります。
Shader Graph(Unity)ラフネスのテクスチャのまま、シェーダー内で反転したいとき。UnityのURPのページは、既定と違うパックにはShader Graphを案内しています。
チャンネル分解ラフネスが別のパックテクスチャ(ORMのG)の中にあり、先に取り出す必要があるとき。

出力ファイルと検証方法

「測定確認」は、ダウンロードしたファイルをページ外(Pillow・numpy・独立したパーサー)で開き直して性質を測ったという意味です。ゲームエンジンでの読み込みは試していません。

書き出し先ファイル検証
Channel PNGs<name>-r/g/b/a.png (8-bit grey)測定確認 numpyチャンネルごとにバイト一致。完全に透明なテクセル下のRGBも保持。
Normal mapPNG · OpenGL (+Y) or DirectX (−Y)測定確認 numpyデコードしたベクトルは単位長(最大誤差0.0002)。OpenGLとDirectXは緑だけが異なります。
Packed mask / ORMUnity HDRP mask · URP metallic · ORM (glTF, Godot)測定確認 Pillowパック後のバイトを確認:ORMプリセット、AO 10 / ラフネス 80 / メタリック 220 → (10, 80, 220, 255)。
Engine importUnity · Unreal · Godot未検証どのエンジンでも読み込んでいません。

検証の根拠

チャンネル分解は4チャンネルすべてでバイト一致(最大差0)、透明テクセル2048個のRGBを保持。エッジブリードはアルファ0のテクセル448個だけを変更し、アルファは一切変更なし。OpenGL ↔ DirectXは緑(255 − g)だけが異なり、変換後の法線は単位ベクトル。

制限

  • チャンネルのバイトを正確に扱えるのはPNGだけです。JPEG・WebPはブラウザがデコードし「バイト一致ではない」と表示します。
  • インターレースPNGと16ビット出力は非対応。正確な経路は3,355万ピクセルまで。
  • ノーマルマップの規約はピクセルから判定できず、変換のみ可能です。
  • Unity・Unreal・Godotへの読み込みは行っていません。チャンネル順は各ドキュメントとエンジンのソースに基づきます。

バージョンと出典

テクスチャラボで測定(docs/TEXTURE-LAB.md):ラフネスマップのGを反転してダウンロードすると、平面は正確に255 − ラフネスで、ステージには元 → 結果が並んで表示されました。パッカーのR反転のチェックはAO 10から(245, 80, 220, 255)を出しました。1 − スムースネスの関係はUnityのレンダーパイプラインのソース、チャンネルの位置とインポート設定は下記のUnityのページに基づきます。Unityへの読み込みは実行していません。

参照した公式ドキュメント

よくある質問

Unityはスムースネスをどのチャンネルから読みますか?

URPメタリックはメタリックマップのアルファ、HDRPマスクマップもアルファです(Rメタリック・G AO・Bディテールマスク)。どちらの順序もUnityのドキュメントに基づき、ラボで引用しています。

Unityで読み込みましたか?

いいえ。バイトは確認しましたが、ここではUnityへの読み込みは行っていません。

無料ですか?アカウントは必要ですか?

このページの機能はすべて無料で、アカウントなしで使えます。透かしや制限された書き出しはありません。

スプライトはアップロードされますか?

いいえ。Studioはこのブラウザのタブ内でデコード・パック・書き出しを行います。プロジェクトはブラウザ(IndexedDB)に自動保存され、.nerulioファイルとして保存できます。

Asepriteやエンジンのプラグインは必要ですか?

いいえ。.asepriteファイルはブラウザで読み書きします。エンジン別のバンドルには必要なものが入っています(Godotはインポート設定とシーン、UnityとGodotのタイルセットは小さなエディタースクリプト)。