ノーマルマップ作成サイト · 2Dスプライト用の法線マップ
スプライトを開くと、ノーマルマップはもうできています。透明な縁から内側へ面取りを立ち上げ、明るさや手持ちの高さマップで細部を加え、ドラッグで動かせる点光源で照らします。ライティングはGodot 4の2Dの計算式そのままなので、プレビューがそのままエンジンの見た目です。
スプライトのノーマルマップ(法線マップ)は、スプライトと同じサイズのPNGで、各ピクセルの面がどちらを向いているかをRGBで持ちます。これで平らな絵も2Dライトの下で立体的に陰影がつきます。透過付きのスプライトをドロップすると、Studioのテクスチャ作業画面が透明な縁から内側へ立ち上がる面取りと明るさの細部ですぐにノーマルマップを作り、Godot 4の2Dの計算式そのままで照らして見せ、_n.png(OpenGL、Y+)、_n_dx.png(DirectX)、またはエンジンで確認済みのGodot 4・Unity 6 URP 2D用バンドルとして書き出します。

- Godot 4 2D lights検証済み· Godot 4.7.2
- Unity 6 URP 2D検証済み· Unity 6000.5.3f1 (URP 17.5)
- PNG set + JSON通常ファイル
スプライトのノーマルマップが持つ情報と、立体感の出どころ
接空間ノーマルマップは、ピクセルごとに単位ベクトルを1つ色として保存します。赤がX(画像の右)、緑がY(GodotとUnityが使うOpenGL方式では上)、青がZ(画面の手前)です。各成分は (n + 1) ÷ 2 × 255 で書かれるので、正面を向いたピクセルはどのノーマルマップにもある薄紫の (128, 128, 255)、右へ45°傾いた面は (218, 128, 218) になります。
絵には奥行きがないため、どの生成ツールも高さを推定してからその傾きを求めます。Nerulioは、透明な縁から立ち上がる面取り(シルエットまでの距離に丸・直線・なめらか・くぼみ・段の形状を適用)と、必要なら明るさからの細部で高さを作ります。塗りは形そのものではないので、明るさの細部は画面上で近似と明記されます。同じサイズの高さマップ(16ビットも可)を足すこともできます。
ノーマルはその高さの傾きです。行を下向きに数える画像座標で n ∝ (−強さ·∂h/∂x, 強さ·∂h/∂y, 1) を長さ1に正規化します。高さはピクセル単位なので、強さ1なら1ピクセルにつき1ピクセル上がる斜面が45°の面になります。カーネル(Pixel・Sobel・Scharr・Sobel 5×5)が決めるのは、傾きをどれだけなめらかにするかだけです。
2Dライトは各ピクセルをN·L、つまりノーマルと光の方向のなす角のコサインの分だけ明るくします。スプライトの平面からライトまでの高さが光の入る角度を決めるため、同じマップでも高い位置のライトではほぼ平らに、低くかすめるライトでは深く彫られたように見えます。
- 接空間
- スプライトの面を基準にした方向。Xが右、Yが上、Zが見る人の側。
- 平らなノーマル
- (128, 128, 255)。傾きがなく、マップがないのと同じように照らされます。
- 面取り(ベベル)
- 透明な縁から内側へ上がる高さ。細く丸い縁か、全体がふくらむクッション形。
- 強さ
- すべての傾きに掛ける値。凹凸の位置は変えずに面を急にします。
具体例:面取りした縁ひとつの数値
スプライトの左の縁、つまり右へ行くほど体の内側へ1ピクセルにつき1ピクセル高くなる所を、Pixelカーネルで計算すると:
傾き ∂h/∂x = 1 px/px n ∝ (−s·1, 0, 1)
強さ 0.5 → 傾き 26.6° RGB ( 70, 128, 242)
強さ 1 → 傾き 45.0° RGB ( 37, 128, 218)
強さ 2 → 傾き 63.4° RGB ( 13, 128, 185)
ライトが左64px・高さ64px → L = (−0.707, 0, 0.707)
強さ1: N·L = 0.707·0.707 + 0.707·0.707 = 1.00 → 最も明るい
ライトが右64px: N·L = max(0, −0.5 + 0.5) = 0 → 環境光のみ光の側の縁が輝き、反対側の縁が暗くなる。この対比が効果のすべてです。強さはすべての面を一度に急にするので、体を鋭くではなく丸く見せたいときは面取りの幅を広げてください。Studioは128px以下のフレームに1.5px、それより大きいスプライトには代表的な内接半径の約0.9倍を推奨します。
できること
- シルエットからの面取り:幅と高さ(px)、丸・直線・なめらか・くぼみ・段の形状。推奨幅は128px以下のドット絵で1.5px、大きなスプライトは全体がふくらむクッション形。
- 明るさからの細部(近似と明記。暗いマントを穴と読まないよう、広い陰影を無視する設定あり)、または同じサイズの高さマップ(16ビットPNGも可)。
- テクスチャ作業画面の高さブラシ(B):推定が外れた所を上げる・下げる・なめらかにする・平らにする・消す。1ストロークが1回の取り消し単位で、フレームの外へははみ出しません。
- 点光源は最大8個(色・強さ・高さ・半径・減衰)、アンビエント、スペキュラー。WebGL2のプレビューはCPUの基準と1/255以内で一致し、Godot 4.7.2も同じでした。
- ドット絵向けの法線は、全ピクセルを4・8・16・32方向×1〜4段階の傾きにそろえ、絵柄を崩しません。
使い方
- 透過付きのスプライトPNGをドロップ(シートは先にスプライト作業画面でフレームに切ります)。
- ノーマルマップパネルで推奨の面取りをそのまま使うか、幅と形状を変える。
- Lit表示で光をドラッグし、Cでフラットとライティングを比較。不自然な所は高さブラシで描く。
- Godot 4のシーン、Unity 6 URP 2Dのバンドル、またはOpenGL・DirectXのPNGで書き出し(Ctrl+E)。
エンジンでノーマルマップを使う
- Godot 4:スプライトのテクスチャにした
CanvasTextureのNormal Map Textureに_n.pngを入れ、PointLight2DのHeightを0より大きくします(既定値は0)。書き出した_lit.tscnシーンは接続済みです。詳しくはGodot 2Dノーマルマップのページへ。 - Unity 6 URP 2D:
_n.pngをスプライトのセカンダリテクスチャ_NormalMapとして追加し、マテリアルはSprite-Lit-Defaultのまま、Light 2DのNormal Map QualityをFastかAccurateにします(既定はDisabled)。テクスチャ側は同梱のインポーターが設定します。Unity 2Dノーマルマップのページを参照。 - DirectX方式の緑を読むエンジン:PNGセットの
_n_dx.pngを使います。_n.pngとの違いは緑(255 − g)だけです。 - アニメーションのシート:先にスプライト作業画面でフレームを切ると、フレームごとに立体が計算されます。スプライトシートのノーマルマップを参照。
結果の確認
- Lit表示でライトをスプライトの周りに一周ドラッグします。明るい縁がライトについてくるはずです。遠い側の縁が光るなら、別の規約で読まれています。
- Cでフラットとライティングを並べます。ライティング側がフラット側より暗い所は、どのライトにも背を向けた面です。影になるべき場所か確かめてください。
- 高さ表示に切り替えます。暗いマントや輪郭がくぼんで見えたら、明るさを形として読んでいます。
_n.pngを任意のビューアーで開きます。透明部分がアルファ最大の (128, 128, 255) なのは意図どおりで、縁のノーマルを2px外側へ複製しているので、シルエットでフィルタリングしても誤ったベクトルが混ざりません。
絵の種類ごとの出発点
Studioは透過の有無からスプライトか繰り返しテクスチャかを提案します。以下はよく使う出発点です。
| 絵の種類 | 最初の設定 | 確認すること |
|---|---|---|
| ドット絵のスプライト(フレーム128px以下) | 面取り1.5px、丸形、縁はClamp、Pixelカーネル、必要ならドット絵向けの法線 | Lit表示で光を回し、明るい縁が光についてくるか |
| 大きい・塗りのスプライト | 面取り ≈ 代表的な内接半径×0.9(クッション形)、広い陰影を無視した明るさの細部 | 暗い部分が穴に見えないか。高さブラシで修正 |
| 繰り返しテクスチャ(透過なし) | 縁はWrap、明るさの細部か高さマップ | Wrapテスト0=継ぎ目なし、3×3タイル表示(T) |
| 既存のノーマルマップ | 生成せずに元データとして使用 | チェックパネルの判定→「It is OpenGL / It is DirectX」。スプライトで赤の反転が出たらFlip red(X−) |
トラブルシューティング
| 症状 | 考えられる原因 | 確認方法 | 対処 |
|---|---|---|---|
| 暗い塗り(マント、輪郭)が穴に見える | 明るさの細部が暗い所を低いと読んだ | 高さ表示でその部分がくぼんでいる | 明るさの高さを下げる、「これより広い陰影は無視」の値を小さくする、または高さブラシ(B)で持ち上げる |
| スプライトが縁だけ光る平たい板に見える | 大きな塗りのスプライトにドット絵向けの1.5pxの縁が使われた | ノーマル表示で色があるのが1〜2pxの輪だけ | 面取りを内接半径の約0.9倍へ広げるか、丸形を選ぶ |
| エンジンで反対側が明るくなる | エンジンが別の緑の規約を読む、またはほかのツールが赤を反転して保存した | ライトを上に置くと上の縁が明るくなるはず | GodotとUnityには _n.png。よそで作ったマップはOpenGL・DirectXの判定へ |
| エンジンで何も変わらない | レンダラーが読む場所にマップがない、またはライトがノーマルマップを無視している | GodotはCanvasTextureのノーマル欄、Unityは _NormalMap とLight 2Dの品質 | 書き出したシーンやインポーターを使うか、エンジン別のページどおりに設定 |
| 手打ちのドット絵がライトで余計に不自然 | 様式化された陰影は色から計算できない | Cでフラット表示と比べる | 高さブラシで凹凸を描く、量子化したドット絵用ノーマルを使う、またはマップを手で描く |
ほかの方法
| 方法 | こんなときに向いています |
|---|---|
| Laigter(デスクトップ、GitHubに無料のGPL-3版) | プリセット、スペキュラー・オクルージョン・パララックスのマップ、コマンドラインがあるデスクトップアプリがほしいとき。3Dから描画したスプライトでは既定のドーム形が最も近い結果でした(18.3°、Studioは20.0°)。Laigterとの比較を参照。 |
| 3Dモデルからベイク | 3Dからレンダリングしたスプライトなら、ベイクしたノーマルが正解で、生成ツールはそれを近似するだけです。 |
| ノーマルマップを手で描く | 様式化したドット絵。手描きのCC0のたいまつでは、Nerulioを含むすべての生成ツールが作者のマップと37〜42°ずれました。 |
| 高さ → ノーマル生成(テクスチャラボ) | 高さマップやグレー画像がすでにあり、面取りもライトもなしで勾配だけがほしいとき。 |
エンジン別の書き出しと検証方法
「検証済み」は、実際のCC0アセットから書き出したファイルを本物のエンジン・ツールで読み込み、元データと比較したという意味です。読み込んでいない形式はそのように表示します。
| 書き出し先 | ファイル | 検証 |
|---|---|---|
| Godot 4 2D lights | _lit.tscn · CanvasTexture .tres · normal / specular PNG | 検証済み Godot 4.7.2実在のCC0ケース6件をGodot(gl_compatibility)で描画し、確認した全フレームがStudioのライティングプレビューと1/255以内で一致。緑を反転した同じ描画は3.8〜37段階ずれるため、規約の誤りを検出できます。 |
| Unity 6 URP 2D | PNG · nerulio-texture.json · Editor/NerulioNormalMapImporter.cs | 検証済み Unity 6000.5.3f1 (URP 17.5)12回中12回合格(6ケース×ガンマ・リニア色空間):URPが適用したライティングが書き出したノーマルマップと一致(平均誤差0.0037〜0.0085)。光の減衰はURP独自なので、明るさはプレビューではなくUnityの基準です。 |
| PNG set + JSON | albedo · _n (OpenGL) · _n_dx (DirectX) · 16-bit height · AO · specular · manifest | 通常ファイルどのエンジン・ツールでも使える通常ファイルで、エンジンでの実行検証はうたっていません。 |
検証の根拠
Godot 4.7.2で実在のCC0ケース6件(ドット絵、HDスプライト、繰り返しテクスチャ、読み込んだDirectXマップ)を描画し、確認した全フレームがStudioのプレビューと1/255以内。Unity 6000.5 URP 2D:12回中12回合格(6ケース×ガンマ・リニア)。規約が分かっている実在のマップでOpenGL/DirectX判定:1Kのマップ128枚で100%正解、1,562サンプル中で誤った「高」判定は0件(「緑が明るければOpenGL」という単純な規則は46%)。
制限
- 立体はシルエットと色から推定したもので、作者が考えた形ではありません。
- プレビューのリムライトは書き出しません。カスタムシェーダーなしで描くエンジンがないためです。
- Unityの明るさのカーブはURP独自の減衰で、検証したのはノーマルマップの項(N·L)です。
- 透明な縁がない絵(繰り返しテクスチャ)では、赤の反転とDirectXの緑を区別できません。チェックパネルがそれを伝え、「赤を反転」を用意しています。
- シートはスプライト作業画面でフレームを切るまで1枚の絵としてライティングされます。テクスチャ作業画面ではフレームを切りません。
- リムライトはプレビュー専用で、Unityにはスペキュラーマップを書き出しません(Godotには書き出し)。
- Unityは独自の光の減衰を使います。書き出したノーマルマップはUnityで検証済みですが、明るさの曲線はUnityのものです。
バージョンと出典
Nerulio:Godot 4用バンドルはGodot 4.7.2(Compatibilityレンダラー)が実在するCC0の6ケースを描画し、確認した全フレームがプレビューと1/255以内でした。Unity 6用バンドルはUnity 6000.5.3f1・URP 17.5で12回中12回合格(N·Lの平均誤差0.0037〜0.0085)。実際の正解ノーマルとの角度は2026-09-24の比較測定によるものです。バイトの符号化とY+の規約は下記のUnity・Godotのドキュメントに従います。
参照した公式ドキュメント
よくある質問
ドット絵にも使えますか?
はい。128px以下のフレームは既定で1.5pxの縁、Pixelカーネルはなめらかにせず、ドット絵向けの法線で方向をそろえられます。ただし手描きのドットの陰影はどの生成ツールでも難しく、CC0のたいまつではNerulio・Laigter・PBR Forgeのいずれも作者のマップと37〜42°ずれました。
OpenGLとDirectX、どちらが必要?
GodotとUnityはOpenGL式の緑(Y+)を読み、これが既定です。PNGセットには_n(OpenGL)と_n_dx(DirectX)の両方が入ります。既存のマップを読み込むとチェックパネルが規約を判定しますが、「It is OpenGL / It is DirectX」を押すまで何も変えません。
エンジン向けの書き出しには何が入っていますか?
Godot 4はSprite2D、CanvasTexture、アンビエント用のCanvasModulate、光ごとのPointLight2D、フレーム用のAnimationPlayerを含む_lit.tscn。Unity 6はPNG、JSON、そしてノーマルマップを_NormalMapのセカンダリテクスチャとして設定し、確認用ライトも作れるエディタースクリプトです。
無料ですか?アカウントは必要ですか?
このページの機能はすべて無料で、アカウントなしで使えます。透かしや制限された書き出しはありません。
スプライトはアップロードされますか?
いいえ。Studioはこのブラウザのタブ内でデコード・パック・書き出しを行います。プロジェクトはブラウザ(IndexedDB)に自動保存され、.nerulioファイルとして保存できます。
Asepriteやエンジンのプラグインは必要ですか?
いいえ。.asepriteファイルはブラウザで読み書きします。エンジン別のバンドルには必要なものが入っています(Godotはインポート設定とシーン、UnityとGodotのタイルセットは小さなエディタースクリプト)。