今回はオブジェクトサーフェイスを彩るマテリアルを作成してみたいと思います。
ついでに、簡単ではありますがUE4のマテリアルの基礎についても説明しようと思います。
ただ、英語が読めるようでしたらこちらのドキュメントを参照していただく方がよいかと思います。
https://docs.unrealengine.com/latest/INT/Engine/Rendering/Materials/PhysicallyBased/index.html
また、より詳しい情報としてはこちらのブログが参考になるのではないかと思います。
http://d.hatena.ne.jp/hanecci/20130727
ただしこちらはシェーディングにある程度詳しくないとちんぷんかんぷんな内容かもしれません。
とは言え、わからないなりに頑張ってみたいという方は一度読んでみるとよいでしょう。
さて、今回作成するマテリアルはたぶん日本人なら誰でも最初に作ってみようと思うであろうToon Shaderです。
ライティング結果を2値化することでカートゥーンっぽいシェーディングを行うあれです。
まあ、今回のものは見栄えを良くするための工夫とかは行っていませんし、いろいろ制約も多い方法です。
もっと良い方法があるのかもしれませんが、ソースコードを書き替えずに対応する方法がこれしか思いつかなかったので仕方ない。
では、まずはいつも通りにBlankプロジェクトを作成しましょう。
名前は何でも構いません。
まずは邪魔なのでイスとテーブル、テーブルの上の像をかたしてしまいましょう。
CTRLキーを押しながらクリックしていき、全部選択したら削除します。
そして、形状的にわかりやすいので、コンテンツブラウザ内の /Game/Props/SM_MatPreviewMesh_02 を配置しましょう。
このStaticMeshは2つのマテリアルが割り当てられていますが、今回はわかりやすさ優先でElement0のマテリアルだけ変更することにします。
まあ、変更はあとで行いましょう。
次にマテリアルを新規作成します。
場所はどこでも構いませんが、わかりやすくするためにフォルダを作成しましょう。
コンテンツブラウザのGameフォルダを右クリックし、[New Folder]をクリックします。
フォルダ名はわかりやすくするために"Toon"とでもしておきましょうか。
このフォルダを右クリックし、[New Asset] -> [Material] で新しいマテリアルを作成します。
名前は…"ToonMaterial"としておきましょう。
このマテリアルをダブルクリックするとマテリアル編集ウィンドウが開き、以下の図のようなノードが出現します。
これがUE4の基本マテリアルの出力ノードです。
[Details]タブの[Material Domain]がSurfaceになっているものは物体表面のマテリアルとして利用されるものです。
UE4はディファードシェーディングを実装しているので、ここで設定されたパラメータは基本的にGBufferに格納されます。
ディファードシェーディングについて簡単に解説しましょう。
今まで基本となっていたシェーディング方法はフォワードシェーディングと呼ばれていて、モデルの描画命令が発行されたときにピクセルシェーダ(頂点ライティングの場合は頂点シェーダ)でライティング計算が行われ、モデルに張り付けられたテクスチャ等を利用してライティングを施されたオブジェクトサーフェイスが画面に表示されていました。
この方法はマテリアルの多様性に対応しやすいのですが、ライトの数が制限されやすい手法のため、平行光源1つと点光源2つ、などといった制約が描画エンジンごとに存在していました。
PS3、Xbox360時代から多光源シーンを取り扱うことが多くなり、フォワードシェーディングでは対応しづらくなってきました。
ディファードシェーディングではモデルの描画命令の時点ではライティング計算を行いません。
この段階ではGBufferと呼ばれる複数のバッファ(すべて画面サイズ分)にテクスチャの値やスペキュラの強さといったマテリアルの情報を書き込みます。
すべてのモデルが描画された段階で今度はライトの形状を(点光源なら球)を画面に書き、球が描画されたピクセル、つまり光源の範囲内にあるピクセルだけシェーディングを行います。
この際に先に格納されたGBufferの値を取り出し、ディフューズやスペキュラの計算を行います。
この方法を用いると、フォワードシェーディングのようなライトの個数制限がかなり緩和されます(完全になくなるわけではないです)。
しかし、半透明オブジェクトに対応できない、ライティング方程式を複数使い分けることが難しいなどの問題点もあります。
最近ではForward+と呼ばれる多光源に対応したフォワードシェーディングも存在しています。
次世代機のゲームではディファードシェーディングかForward+が使われていることが多いですね。
半透明オブジェクトの問題は置いておくとして、ライティング方程式の問題は実はかなり解決の道が見えています。
物理ベースシェーディングの一般化がまさにその道です。
物理ベースシェーディングはライティング方程式を物理的に理屈が通った手法を用いることで複数のライティング方程式を用いる必要をなくし、パラメータの調整も少なくすることができるという代物です。
最近話題のゲームエンジンや次世代機のゲームではほぼこのシェーディングを用いています。
Unityも次のバージョン5にて対応されるとのことです。
上の図で設定可能なパラメータも物理ベースシェーディングで使用されるパラメータとなります。
UE4では基本的なライティング方程式は固定です。
しかし、完全な発光体のようにライティングを行いたくないものや表面下散乱(サブサーフェイススキャッタリング)と呼ばれる肌の表現などは若干特別な方程式を用いる必要があります。
そのため、UE4では通常のライティングを含んだ4つのライティングモデルが設定可能になっています。
[Details]タブの[Lighting Model]の項目をチェックしてみてください。ここに、"Default Lit", "Unlit", "Subsurface", "Preintegrated Skin"の4つが選択可能となっています。
”Default Lit"が通常のライティングで、最もよく使われるであろうライティングモデルです。
"Unlit"はライティングをしないライティングモデルです。
残り2つは表面下散乱を表現するためのものですが、"Preintegrated Skin"は肌を表現するのに適している低コストのライティングモデルのようです。
氷やロウソクなどの表現は"Subsurface"を用いる方がよいようです。
Elemental Demoの氷の巨人は"Subsurface"を用いているようですね。
上の図は"Default Lit"の場合の最終出力ですが、下の図は残り3つの最終出力です。左から"Unlit", "Subsurface", "Preintegrated Skin"です。
比較的重要なパラメータについて解説しましょう。
Emissive Colorは自己発光のカラーです。蛍光灯のような自己発光を行うマテリアルはこの値を出力する必要があります。
Normalは法線方向で、法線マップを利用する場合は法線マップをここに入れてやります。入っていないとポリゴンの法線を用いることになります。
Base ColorとMetalicは金属、非金属の表現を行うのに最も必要なパラメータとなります。
Base Colorは基本的にその物体の表面の色です。ディフューズカラーとかアルベドとか呼ばれるものをここに入力します。
通常、物理ベースシェーディングでは拡散光であるディフューズの色の表現はアルベドを、反射光であるスペキュラの色の表現はリフレクタンスと呼ばれるパラメータを用います。
ただ、たいていの場合はアルベドとリフレクタンスの色は同じような色を用い、金属ならリフレクタンスを強く、非金属ならアルベドを強くするといった調整が行われます。
その表現のために2つの色を調整するのは意外と面倒です。
そこで用いられるのがMetalicパラメータです。
Metalicパラメータはそのサーフェイスが”どれくらい金属か?”ということを示します。
Metalicが0.0の場合は完全に非金属で、この場合はベースカラーが100%ディフューズカラーとして用いられます。
逆に1.0の場合は完全に金属とみなし、この場合はディフューズカラーは完全に黒となります。
金属でも拡散光は全く起こらないというわけではないらしいのですが、ほぼ無視しても問題ないという理由からこうなっているようです。
ではSpecularは何かというと、金属以外の反射光を表現するのに用いられます。
例えば漆塗りの漆器などのように、金属ではないけれどテカりがあるオブジェクトの表現に用いられます。
このようなオブジェクトはMetalicパラメータによってディフューズカラーの値を低くしてはいけないのです。
なお、シェーダコード内の計算式は以下のようになっています。
DiffuseColor = lerp(BaseColor, BlackColor, Metalic);
SpecularColor = lerp(Specular, BaseColor, Metaric);
となっています。
Roughnessはそのサーフェイスがどのくらいラフか、つまり粗いかどうかを示すパラメータです。
同じ金属でもピカピカに磨かれた金属と紙やすりをかけたようなザラザラな金属ではスペキュラの反射の仕方が違ってきますが、これを表現するのがこのパラメータです。
0.0~1.0の範囲で表現し、0.0なら完全に滑らかな表面を、1.0で極限まで粗い表面を表現できます。
UE4のマテリアルで最初に覚えるべきなのはこの程度でしょう。
では、ここでトゥーンシェーディングを行うにはどうすればよいでしょうか?
Base Colorなどに値を入れてしまうと普通にライティングが行われてしまうわけですが、ここまでのパラメータで1つだけライティング処理から切り離されたパラメータが存在します。
Emissive Colorです。これだけはライティング計算とは無関係です。
なので、トゥーンシェーディングをマテリアル内で計算し、結果をEmissive Colorに出力すればいい、ということになります。
この際に問題になるのは?それはライト情報です。
ディファードシェーディングではモデル描画時にライトの情報を必要としません。そのため、マテリアル内でライトの情報を取得できません。
一応"Light Vector"というノードがあるのですが、[Material Domain]が"Surface"の場合はこれを利用することができません。
そこで、外部からライトパラメータを取得するための入り口を作ってやることにします。
コンテンツブラウザのToonフォルダを右クリックし、[New Asset] -> [Materials & Textures] -> [Material Parameter Collection]を選択します。
作成されたコンテンツ名は"CustomLightMatParam"とでも名付けておきましょう。
これをダブルクリックするとスカラー、もしくはベクトルのパラメータを設定できる画面が出てきますので、以下のように3つのパラメータを追加しましょう。
ライト方向を指定する"LightDirection"、ライトカラーを指定する"LightColor"、影色を指定する"ShadowColor"の3つです。
もう1つ、事前に用意するものがあります。
トゥーンシェーディングの場合によく使われるルックアップテーブルです。
完全な2値化をするなら分岐を利用するのもありなのですが、テクスチャに必要な諧調を設定しておき、これをフェッチするのが一般的な実装でしょう。
なので、今回は下のようなテクスチャを用意しました。
完全な2値画像ではなく、変化する部分に少しだけグラデーションをつけています。
256*8のテクスチャで、これをコンテンツブラウザにドラッグ&ドロップしてインポートしましょう。
では、マテリアルを作成しましょう。
細かく説明するようなこともないので、ノードのスクリーンショットをご覧ください(クリックで拡大)。
かなり大きな画像なので注意してください。
真ん中あたりにTexture Sampleノードがありますが、このノードが前述のルックアップテーブルテクスチャです。
普通は表示されるものだと思うのですが、なぜかまっ黒でした。
また、今回はSpecularとRoughnessを外部から設定できるようにしました。
拡散光はトゥーンシェーディングされますが、反射光は割と普通に設定可能です。
また、DiffuseTextureも外部から指定できるようにしています。
これらの値を変更したい場合、ポストプロセスの際にも説明したMaterial Instanceを用いましょう。
さて、このマテリアル、もしくはこれをベースにしたMaterial Instanceを最初に置いたStaticModelに適用しましょう。
Level EditorでStaticMeshをクリックし、[Details]タブの[Materials] -> [Element 0]に作成したマテリアルをドラッグ&ドロップしましょう。
適用されると上方向からライトが当たっている状態でトゥーンシェーディングされるはずです。
しかしこれではシーンのライト情報を反映していません。
なので、レベル開始時のイベントを用いてマテリアルパラメータに必要な情報を書き込みましょう。
影色は割と適当です。好みの色に設定しましょう。
で、こうすると結果は下のようになります。
他の部分がトゥーンじゃないので違和感がありますが、影色付きのトゥーンシェーディングの完成です。
影色は真っ黒より少し青みがかった感じにしておくとよりそれっぽい気がします。
しかし、この方法には問題点があります。
最も目立つ問題点はやはりシャドウでしょう。
Emissive Colorに設定している関係でシャドウイングが行われていません。
本来であれば台座に球部分のシャドウが落ちるはずなのですが、落ちていません。
もちろん、他のオブジェクトを上の方に配置してもシャドウは落ちません。
もしも、シャドウイングも行いたいしシャドウの色も指定した影色にしたい、という要望があるようでしたらシェーダコードやエンジンのC++コードからいじる必要があるのではないかと思います。
UE4はC++コードもシェーダコードも公開されているため、必要であれば変更も可能ですが、バージョンアップのことを考えると簡単に修正ってわけにはいかないでしょう。
マテリアルの[Material Domain]には"Light Function"なるものも存在していますが、こちらはライティング方程式を変更する手段ではなく、ライティングの結果に追加の処理(マスクなど)を行う機能のようです。
ステンドグラスを通した光を表現するためにライティングの結果にステンドグラステクスチャを積算するような場合ですね。
今回の記事は片手落ちな気もしますが、もしシャドウも対応できるオリジナルシェーディングの方法を知っている方がいましたら教えてください。