てんちょーの技術日誌

自分がつまづいたこととかメモ

【UE4】水彩風マテリアルを作ってみた

はじめに

Unreal Engine 4 (UE4) Advent Calendar 2016 8日目です。

qiita.com

我らがおぎまふさんやcom04さんなど、いろいろな方がトゥーン表現に挑戦されています。

最近ではMarketPlaceでもトゥーンなアセットが出てきたりしています。

いろいろ参考サイトをまとめていたのですが、com04さんの記事を見たほうが早いですね…

qiita.com

ですがやっぱり自分好みのものを作りたい…と思って探していたら参考になりそうなところを見つけました。

d.hatena.ne.jp

こちらのサイトではsoftImageですが、なんとかそれっぽくなりました。

マテリアルダメダメなので、練習ついでに作ってみました。

できたもの

つくりかた

ここから先は、先程の参考サイトを見た前提でお話を進めていきますね。(さらっとでおkです)

考え方、手順は参考サイトと同じなので それをどうやって再現したか という感じで進めます。

とりあえず今回は

  • BaseColor
  • Specular
  • Shadow

に当たる部分のマスクを愚直に作って色指定、その境目にNoiseを入れてなじませていきます。コンパイルが重いかも。

ついでに、各色をテクスチャではなく直で色指定しています。モデルによっては細かくマテリアルが分けてる 方が相性がいいです。処理負荷は気にしないことにします。

BaseColorのマスクを作る

f:id:shop_0761:20161202014231p:plain

BaseTextureを指定できますが、結局Desaturationしてしまうのでそのままでもいいかも。

Blurをかけます。

Blurノード

float4 output = 0;
float weightSum = 0;
float currentWeight = 1.0;

for (int i = 0; i < 10; ++i) {
    output += Texture2DSample(Material.Texture2D_0, Material.Texture2D_0Sampler, texCoord + i * stride) * currentWeight;
    weightSum += currentWeight;
}

output /= weightSum;

return output;

参考:

qiita.com

そして、ちょっとNoiseをのせたり。 もちろん、すでにあるNoiseノードでもいいかと思いますが今回はstylized RenderingのT_TexturedPaperを使用しています。

Specularのマスクを作る

f:id:shop_0761:20161202014328p:plain

こちらはおぎまふさんを見習ってLightVectorを擬似的に用意して、Specularの計算をします。

f:id:shop_0761:20161202014445p:plain

こうして出来た2つをDesaturationしてそれぞれマスクとしました。

ここまででとりあえず2色分塗ることが出来ました。

ShadowとEdgeのマスクを作る

f:id:shop_0761:20161202014544p:plain

f:id:shop_0761:20161202014646p:plain

ここで影色も塗らなきゃなりませんが、このままでは塗れないのでゴリ押しで解決します。

影色になる部分を一旦求めて、今の絵に黒で塗り直します。(いい方法が知りたい)

こうすると後からaddして色を乗せられます。

f:id:shop_0761:20161202014834p:plain

こちらはおぎまふさんのRimLightの実装をそのまま使ってますので関数の中身は割愛します。

水彩の参考サイトの完成絵を見るとエッジが2段あるように見える(実際は歪めたり、ノイズかけたり)のでRimLight_Rangeにちょっとaddして作ります。

NoiseありとなしのEdgeがあったほうがよかったので、分けて作ってます。

f:id:shop_0761:20161202015259p:plain

f:id:shop_0761:20161202015632p:plain

ついでにさっきのshadowマスクを使って、明るい方のEdgeと暗い方のEdgeを作り分けてます。

ここで影色も作ってます。

まとめる

f:id:shop_0761:20161202015742p:plain

最後にshadow側とBaseColor側をaddして完成です。 お好みでNormalMapも使えるようにswitchをつけたりしました。

こうすることでGraymanの胸のUnrealロゴが出せます。

余談

最初はこのマテリアルをPostProcessで実装していたのですが、マスクできるようになったけど色指定できない…ってなったのでやめました。

その時、最初に作っていた影色を少し淡くするPostProcessが出来たのでついでに載せておきます。

alweiさんのお手軽Toonのマテリアルにちょっと足すだけです。

全体

f:id:shop_0761:20161202021012p:plain

追加した部分

f:id:shop_0761:20161202021032p:plain

CustomDepthでマスクを作った結果を使ってなんかごにょごにょしました。

でこの結果を最後にaddして出力してます。

余談2

blurのコードを書いてるときに思いついたのですが、blurベースでedgeが取れそうだったのでやってみました。

f:id:shop_0761:20161207225818p:plain

このぐらいいけます。

ノードはこれだけ 面倒なSobelフィルタを作らなくても使えるかも。

f:id:shop_0761:20161207225837p:plain

Blurのコード

float3 output = 0;
float ScreenMult_X = GetPostProcessInputSize(0).zw.x;
float ScreenMult_Y = GetPostProcessInputSize(0).zw.y;

output += 1.0 * SceneTextureLookup(TexCoord + float2(ScreenMult_X * BlurAmount * -1.0, 0.0), SceneTextureID, true);
output += 1.0 * SceneTextureLookup(TexCoord + float2(ScreenMult_X * BlurAmount * 1.0, 0.0), SceneTextureID, true);

output += 1.0 * SceneTextureLookup(TexCoord + float2(ScreenMult_X * BlurAmount, 0.0), SceneTextureID, true);
output += 1.0 * SceneTextureLookup(TexCoord + float2(0.0, ScreenMult_Y * BlurAmount), SceneTextureID, true);

output += 1.0 * SceneTextureLookup(TexCoord + float2(0.0, ScreenMult_Y * BlurAmount * -1.0), SceneTextureID, true);
output += 1.0 * SceneTextureLookup(TexCoord + float2(0.0, ScreenMult_Y * BlurAmount * 1.0), SceneTextureID, true);

output /= 6.0;

return output;

SceneTextureIDを引数にすることで、任意のSceneTextureを指定できます。 outputは加算した分だけ割ります。

f:id:shop_0761:20161207230113p:plain

多分この順番通りでIDが振られてると思います 分からなかったらコードを読めばいいんだよ うん。

まとめ

なんとなくMaterialの演算が分かった気がします。もろもろの計算は試行錯誤の結果なので、「なんでこうなるの?」というより「色々やったらこれがよかった」って感じです。

作りたい絵を目指して、実際に手を動かしてみるとこの意味がわかるかと思います。すごい人になれば頭のなかで完成形をイメージしながらノードを組めるかと思いますが()

まずは作りたい絵を探しましょう!!

わからないこととかあれば、可能な限りお答えするのでTwitter(@shop_0761)等でご連絡ください。

明日は Epic社のおかずさん(@pafuhana1213)の「帰ってきたUE4のマーケットプレイスで購入したアセットを片っ端からレビューするマン」です。

鹿のレビューが懐かしい…