【Unity】初めて『シェーダーグラフ』でシェーダーを学んでみる 実践編.炎①
こんにちは。
シェーダーを一度も触った事のないデザイナーが、Unityの「シェーダーグラフ」を使ってシェーダーを学んでみるシリーズ。
この実践編は、僕が作ったシェーダーをゼロから完成まで、実際にノードを組み立てながら、どのように作っているかを思考の流れと合わせて解説していきます。
シェーダー学習初という事もあり、たくさんの方のブログを拝見し、勉強させていただきました。
このシリーズには僕が参考にさせていただいたブログのリンクを貼らせていただいておりますので、そちらも併せて読んでいただくとより一層理解が深まるかと思います。
(※これ何だろう??というノードやワードがあれば、サイドメニューからブログ内検索をオススメします。詳細が書かれていない箇所は、過去記事で詳しく説明している可能性があります。)
今回は「実践編.炎①」という事で、上の画像のトゥーン調の炎がどのように作られているかを解説します。
まだ公式から配布されているサンプルのプロジェクト内でないとシェーダーグラフが使えないようですので、こちらのページから「ShaderGraphExamples-master」プロジェクトをダウンロードして下さい。
ダウンロードが出来たらプロジェクトを開き、下記の場所から僕が作ったシェーダーのパッケージファイル「Fire_01_Sample.unitypackage」を落として、今開いているプロジェクトへインポートして下さい。
(※こちらはVersion 2018.1 で作成したシェーダーになります)
インポートをしたら「Fire_01_Sample」というシーンを開いて下さい。
シーンは開けましたか?
それではシェーダーの解説に入ります。
シェーダーを開く
サンプルのシーンを開いたら、「Fire_01_Sample」というシェーダーファイルをダブルクリックして下さい。
するとシェーダーグラフのエディターが立ち上がります。
全体の流れ
実際にノードを組み立てて行く前に、このシェーダーがどのように作られているのか、全体の流れを軽く把握していきましょう。
まず大雑把に説明すると、初めに2パターンのUVスクロールするノイズを掛け合わせ、炎のちぎれを表現する素を作ります。
それをマスクで抜いてシルエットを整えた後、3段階の範囲違いの炎をつくり、それぞれに色を設定して合成し、炎が出来上がります。
ノードの構成自体は下記のようになります。
[A]
2パターンのUVスクロールをするノイズを作成
[B]
炎のシルエットの下の方がちぎれないよう重みを設定
[C]
炎のシルエットを設定
[D]
SubtractノードとStepノードを使い、3段階の範囲の違う炎を作成
[E]
それぞれの炎に色を設定し、加算合成して完成
これらの組み合わせで、下図のような炎のシェーダーが完成します。
実際に作ってみる
では実際に、ノードが何もない状態から完成まで、実際の作成手順に沿って解説していきたいと思います。
① Unlit Masterノードの作成
これがなくては始まらない。マスターノードを作成しましょう。
今回はエフェクトですので「Unlit Masterノード」を作ります。
任意の場所で右クリック→Create→Shader→Unlit Graphで作成できます。
今回の炎はアルファを使用しますので半透明の設定にします。
Surfaceを「Transparent」にしましょう。
② 2パターンのノイズを作る
次に、いきなり来ましたね今回のキモ!
炎が炎たりえるために欠かせない表現!
炎が上昇し、ちぎれていく表現の素となる2パターンのスクロールするノイズを作っていきます。
2つの「Gradient Noiseノード」をつくり、それぞれが+Y方向にスクロールするようにノードを繋げていきます。
UVスクロールは基礎編で何度も出てきているので詳しい説明は省かせてもらいますね。
ここではノイズのスクロール速度を調整しやすくするために、「Vectorノード」ではなく「Sliderノード」を使用しています。
僕はいちいち数値入力するのが面倒なので、数値を頻繁に調整するところは「Sliderノード」をよく使います。
「Gradient Noiseノード」のスクロール速度と細かさは今のところ適当で構いません。
細かいノイズのスクロール速度だけ、少し早めにしておきましょう。
ノードを組み上げていく過程でここの値はゴリゴリいじりますのでちゃちゃっと入力して次へGO!
③ 2パターンのノイズを合成してみる
②で作った2パターンのノイズを「Multiplyノード」で乗算合成してみましょう。
↓こんな感じです。
んーーー、、、まだこれがどう炎になっていくのか分からないですね。
次はこれを「Stepノード」に繋げて2値化してみましょう。
↓こんな感じです。しきい値は「0.15」で。
おおっ!なんかちょっと炎のゆらぎっぽく見えませんか!?
黒い模様がくっついたり離れたりして「もにゃもにゃ」した面白い動きになってますね。
2パターンの粗さ、速度の違うノイズが重なる事で、このように複雑な表情をした模様を作る事が出来ます。
次のステップでは、ここからもう少し手を加えて炎のシルエットに近づけていきましょう。
④ 炎のシルエットを作る
今のままだとパーティクルに貼った時に四角形がパッキリ出てしまうので、周囲を消し込みつつ炎のシルエットを作っていきます。
新たに「Sample Texture 2Dノード」を作り、「Mask_01_Sampleテクスチャ」をアサインします。
この「Sample Texture 2Dノード」と2つのノイズを合成した「Multiplyノード」をさらに「Multiplyノードで」乗算して、さきほどの「Stepノード」に繋いでみます。
テクスチャで周囲を消し込む事で四角形のフチがなくなりましたね。
さらに、このテクスチャは下から上にかけて徐々に暗くなるグラデーションテクスチャですので、ノイズと合成する事で上にいくほど暗くなるノイズが作られます。
それを「Stepノード」に繋げる事で、上にいくほど徐々に黒い面積が大きくなり、ちぎれて消えていくような表現を作る事ができます。
ここらへんで色をのせてみましょうか。
新たに「Colorノード」を作りマスターノードのColorに繋ぎ、「Stepノード」をマスターノードのAlphaに繋いで下さい。
この時、「Colorノード」はHDRモードにして、色は炎っぽい色にして、Intensityの値も上げてください。
(下図では、カラーピッカーのHexadecimalをBF4200、Intensityを2にしています。)
次に、Projectタブで、今回自作したシェーダーを「Fire_01_Sample.mat」にドラッグ&ドロップして、シェーダーをアサインして下さい。
この状態でGameビューを見てみてみましょう。
下図のようなエフェクトが映っているかと思います。
(もし映っていない方はGameビューのDisplayが「Display1」になっているか確認してみてください。僕の用意したシーンではDisplay1で炎シェーダーをアサインした板ポリゴンを正面から映すようにしています。)
どうでしょう!?
炎っぽく見えませんか!!??
僕は思わず「おぉ~~~っ」てなっちゃいましたよw
ここまで出来たら、あとはより炎らしく見せるための調整作業みたないなもんです。
ちなみに、この炎の周囲がグロウがかっているのはポストエフェクトのBloom効果によるものです。
Bloomは輝度が高い箇所を発光させるような効果で、「Colorノード」のIntensityの値を高くするほど強く発光するようになります。
ポストエフェクトの説明を始めると長くなるのでここでの説明は割愛しますが、いずれ記事にしようかと思っています。
Unityでのポストエフェクトについて知りたい方は下記のサイトがとても参考になると思います。
⑤ 炎の下の方に重みをつける
今回の炎は地面などが燃えているようなイメージで作ったので、シルエットもそのように調整していきます。
現状だと炎の下の方も「ちぎれ」の表現がされていて、1つの塊としては不安定ですよね。
実際の炎だと、下の方はつながっていて上の方にいくと徐々にちぎれて消えていくはずです。
ですので、下の方はちぎれが無く、上の方にいくほどちぎれていくというシルエットを作っていきたいと思います。
僕がどのように考えたかというと、ちぎれがあるのはその部分が黒いから。
という事は、下の方はちぎれたくないから黒くなければいい(つまり白)。上の方はちぎれていいから黒。
つまり、下が白、上が黒のグラデーションをノイズに加算合成してやればイイッ!
というふうに考えました。
で、組み上げたノードがこれです。
(グラデーションにバンディングが出ちゃってますね・・・。gifだとファイルサイズ抑えるためにどうしても出ちゃう・・・。)
下から上へ値が変化しているノードといえば「UVノード」のY軸です。
「Splitノード」でUVのY軸の値だけ抜き出します。
このままだと下が0、上が1の値なので、「Invert Colorノード」で値を反転し、下が1、上が0に変換します。
最後にノイズと「Addノード」で加算合成すると、下から上へ徐々に暗くなるノイズの完成です!
(※乗算だとノイズの黒い部分は黒いままなのでダメですよ!)
これをさらにマスクテクスチャと合成すると・・・、
周囲のフチが抜けて下に重みがあるグラデーションノイズが出来ます。
これを「Stepノード」に繋げると・・・、
あれ?
なんか塊みたいになっちゃいましたね。
「Stepノード」のしきい値を上げて、黒い面積を増やしてみましょう。
試しに「0.8」としてみます。
黒い面積が増えた事で、炎らしい「ちぎれ」が復活しました!
下の方もちゃんと繋がって重みが出ていますね!
この状態でGameビューを見ると下図のようになっていると思います。
これもう炎じゃないですか???
完璧に炎ですよね!?
という事で、炎の基本的な作り方はこれで完了です!
ここまで出来たら、マスクテクスチャを変えて少しパラメーター調整をするだけで、下図のような人魂っぽいシルエットの炎も作れちゃったりします。
今回は「Gradient Noise」でノイズを作りましたが、ノイズ用のテクスチャを自作して様々な「ちぎれ」具合を表現する事もできます。
「Gradient Noise」だと丸っこいちぎれになってしまうので、もっとギザギザしたちぎれにしたい場合はテクスチャを描く必要がありますね。
とにかく、ここまでのノード群だけでも様々な表情を持った炎が作れるという事です。
では、ここから先は、より炎らしさを表現するためのアプローチの解説へ入っていこうと思います。
⑥ 炎の色に段階を付ける
炎の色は燃やす物質や温度によって変化しますが、熱そうに見えるオーソドックスな炎というのは、中心が白か黄色で、中心から離れるにつれてオレンジや赤になっていく炎だと思います。
なんとなく、中心が白いとリアル寄り、黄色いとアニメ寄りなイメージがあります。
今回のシェーダーはトゥーン調(アニメ的)の炎をイメージしていますので、中心は黄色でいきます。
どのように色の変化を表現するかというと、中心、中間、外側の3段階の範囲違いのシルエットを作り、それぞれに別の色を設定した後、加算合成で1つにまとめるという方法で表現します。
では、実際にノードを組んでいきましょう。
新たに「Subtractノード」を3つ作り、下図のように繋ぎます。
「Subtractノード」はA入力からB入力の値を減算するノードです。
B入力の値を3段階設ける事で、このように同じ絵から範囲違いの絵を3つ作る事ができます。
それぞれに繋ぐための「Stepノード」も3つ用意して、下図のように繋ぎます。
(範囲はSubtractノードで調整するので、Stepノードのしきい値は1つのSliderノードで制御しちゃいます。)
これで3段階の範囲違いのシルエットが出来ました。
ここにそれぞれ「Colorノード」で色をのせ、「Addノードで」加算合成していきます。
(設定したカラーの値はサンプルのシェーダーと同じ値です)
これでこのように3段階の色を持った炎ができました。
現状だとマスターノードのAlphaが外れてしまってますので、一番範囲の広い「Stepノード」をAlphaに繋いで下さい。
Gameビューで見てみましょう。
こんな感じになってると思います。
3段階の色を持った炎が出来ましたね!
1色だけだった炎よりも「らしさ」が増したと思います。
ちなみにColorのIntensityの値は、一番範囲の広いColorだけ高めにしています。
これは何故かというと、中心部分も高くすると白く発光してしまい、単純に僕が表現したいイメージと違ったからです。
値高くしたらダメってわけじゃないですよ。
みなさんも是非、今回組み上げた「Subtractノード」「Stepノード」「Colorノード」の値を色々いじってみて、お好みの形、色具合を探ってみて下さい。
さて、ここで炎の表現はいったん置いておいて、マスターノードのAlphaに繋いでいる「Stepノード」を見てみましょう。
このようになっていると思います。
このシェーダーをパーティクル等に貼った場合、ここに表示されている絵がそのままゲームシーンで描画されます。
となると、下図の赤い部分は何も映っていない範囲という事になります。
この何も映っていない範囲、何もないように見えても実はアルファ値0で描画するという計算が行われているのです。
ですので、何も映ってないのに計算するという無駄な処理が行われています。
エフェクトは、半透明という描画負荷が重いブレンド方法を使う事が多いため、1枚のパーティクルに何も映らない部分が極力無いように作るのが鉄則です。
次のステップでは、上方向にシルエットの範囲を広げて、出来るだけ描画の無駄を省いていく方法を解説していきます。
⑦ シルエットを広げて描画の無駄を減らす
上方向にシルエットを広げる方法は、使用しているマスクテクスチャのグラデーションを調整するという方法もありますが、今回はシェーダー内だけで調整してみます。
僕が最初に思いついた方法は、「Stepノード」のしきい値で範囲をコントールしているから、しきい値を下げればいいんじゃないか?という方法です。
しきい値を「0.1」と極端に下げてみると下図のようになりました。
確かに範囲は広がりましたけど「ちぎれ」がなく塊になっちゃいましたね…
この方法はやめて、次はしきい値はそのまま、もっとノードの下流で明るさを上げて範囲の拡大を試してみました。
こんな感じです。
この場所に「Lerpノード」 を挟み、Bの値を「4」にすると、0~1の範囲だったグラデーションが0~4の範囲に変換されます。
(※Remapノードでも同様の事ができます)
「Stepノード」を見てみると下図のように少し範囲が広がりました。
左が拡大以前、右が拡大後です。
だいぶ無駄な領域が減りましたね。
今回の方法ではちぎれを維持しつつ広げられる範囲はこれが限界でした。
他にも色々な手法があるんじゃないかなーと思うんで、今後も模索していきたいと思います。
さて、ここまででだいぶサンプルの炎に近づいたと思いますが、後2つ、表現力アップを図りたいと思います。
⑧ 「ちぎれ」の形を縦長にする
炎って、「ぬら~っ」と縦に伸びたようにちぎれていくと思うんですよね。
なのでちぎれを縦長にしたいと思います。
これは簡単です。
2つのノイズに繋げている「Tiling And Offsetノード」のTilingのXY値を調整します。
縦長にしたいのでYの値をXよりも小さくします。
2つのノイズで縦長具合を少し変えると、より複雑なちぎれ形状になって「らしさ」が増します。
この状態でGameビューで見たのが下図です。
どうでしょう?
以前より炎らしくなったんじゃないでしょうか。
次は表現力アップの最後の1つ、ノイズの流れに「ゆがみ」を加えて、炎のゆらめき感のアップを図ります。
⑨ ノイズを歪ませて炎のゆらめき感をアップ
今のままでも十分炎らしいですが、もう少し手を加えます。
先に今回の対応で求まる結果をお見せしますね。
左が対応前、右が対応後です。
少しだけ右の方が斜めに歪んでいるんですが解りますかね?
微妙な違いですが僕はこちらの方が揺らいでる感じが強くて好きです。
では実際にノードを組んでいきましょう。
現在の2つのノイズを合成した状態は下図の状態だと思います。
ノイズがまっすぐ上にスクロールしてる状態ですね。
これを下図のように歪ませます。
これはどうやっているかというと、「Radial Shear」というノードを使ってUVを歪ませています。
「Radial Shearノード」は、下図のように魚眼レンズの効果に回転を加えたように歪ませるノードになります。
このノードを2つのノイズの「Tiling And Offsetノード」の下流に繋ぎます。
下図のように繋いでみて下さい。
CenterとStrengthの2つの値を調整してこのような動きにしています。
Centerは歪みの中心点、Strengthは歪みの強さです。
これにより、下図の右側のような少し斜め方向へゆらめいた炎を作り出しています。
完成!
これで今回のシェーダーは完成です!
スクロール速度、しきい値、ノイズの大きさ、色、などなど、今回組み上げたノードのパラメーターを調整すれば様々な表情を持った炎が出来ますので、お好みの炎になるまでいじってみて下さい。
今の僕のベストが、サンプルで用意した状態になります。
~ちょっと補足~
ちなみに、今回は「Stepノード」を使いましたが、これを「Smoothstepノード」に置き換えるとリアル寄りの炎が作れます。
↑こんな感じです。
少し輪郭がボケて自然な炎の表現になりましたね。
これは「Smoothstepノード」に置き換えた以外、他のノードは一切いじっていません。
少し全体のシルエットの四角っぽさが目立つので調整したいところですが、「Smoothstepノードを使うとこういう表現もできるよ!」という一例でした。
まとめ
今回は実践編という事で、サンプルシェーダーの解説ではなく、僕が実際に作ったシェーダーの解説となりました。
いかがだったでしょうか?
炎は誰もが作ってみたいエフェクトの1つだと思うので、誰でも再現できるように、なるべく詳しく解説してみました。
回りくどかったらごめんなさいw
このシェーダーを作りながら他の作り方も考えたりしてたんですが、いろんな方法がありますね。
ノイズの模様をこんな感じにしたらどうか?
ゆがみをフローマップで試してみたらどうか?
どういうシルエットが炎らしく見えるか?
など、試したい事は沢山ありました。
それだけ炎には色んな表情、作り方があるんだなと改めて思いましたね~。
みなさんが作った炎も見せてもらえたら嬉しいです!
では最後に、炎のシェーダーを作るうえで抑えておきたいポイントを書いて、今回の解説は終わりにしたいと思います。
- 2つのスクロールノイズを合わせて炎のゆらぎを表現
- マスクテクスチャで炎のシルエットを自由自在に
- Subtractノードで範囲の段階をつくり色の違いを表現
それでは、次回のシェーダーグラフ解説をお楽しみに!