SNN(Symmetric Nearest Neighbor)は Photoshop のフィルタの「面を刻む」のような効果で、絵画調になるというか単純化するというかのっぺりするというか‥そんなフィルタです。
下記のもんしょさんのブログにてSNNの概要やコードでの実装方法が紹介されており、こちらを頼りにポストプロセスマテリアルを作成してシーンに適用してみました(UE4.19)。
こちらがSNNフィルタ適用後です(2倍に拡大)。
前回の記事で軽くご紹介したように、おかずさんの輪郭検出の記事を試していれば、隣接するピクセルの情報を拾う方法が分かるかと思います。
ここからは、ピクセルの情報を拾う方法を知っている前提で話を進めます。
また、いきなりSNNフィルタを試すより、先に平均化フィルタやガウスブラーフィルタを試した方が、理解するのに丁度良いステップアップになると思います。
その際には下記のサイトの記事がとても参考になりました。
平滑化(移動平均、ガウシアン)フィルタ 画像処理ソリューション
ひとまず3x3の平均化フィルタからお試し中。 pic.twitter.com/9Tr7quwpM4
— moko (@moko_03_25) 2018年3月31日
5x5の平均化フィルタも試してみた。 pic.twitter.com/HMAh6Vbvzz
— moko (@moko_03_25) 2018年3月31日
3x3と5x5のガウスフィルタ。 pic.twitter.com/pR7K7mYI8W
— moko (@moko_03_25) 2018年3月31日
その後にSNNフィルタを試しました。
SNNフィルタもいけた‥!
— moko (@moko_03_25) 2018年3月31日
対象の色の比較部分は計算はもんしょさん(@monsho1977)のブログ記事のコードを参考にさせて頂きました!https://t.co/7S4dWmxu8S pic.twitter.com/YdFX6i7EWX
こちらのツイートの小さい画像では分かりにくいと思うので、順を追って解説していきます。
* * * * *
まずはUV座標をズラすマテリアル関数を作成します。
内容はおかずさんの輪郭検出の記事そのままです。
Scene TextureノードでScene Colorを選択、InvSizeポートからは画面サイズの逆数‥つまり1ピクセル分のUVの幅を取得できるので、Inputノードで入力してきた値を掛けると「何ピクセル分の幅か」が決まります。それをUV座標に足すと、そのピクセル分ズレた座標が出力結果になります。
もう1つマテリアル関数を作成します。
こちらはズラしたUV座標の色を拾うような構成にします。
先ほどのマテリアル関数を Scene Texture ノード(PostProcessInput0 を選択)に接続して、シーンの色を出力するだけの構成です。
こちらもおかずさんの輪郭検出の記事と流れは一緒です。
たったこれだけなのになぜ先ほどのマテリアル関数と分けているのかと言うと、PostProcessInput0 を Scene Depth にして深度を出力したりと用途に応じて構成が変わるため、流用可能なよう分けているのかと思います。
さらにマテリアル関数を作成します。
もんしょさんのSNNフィルタの記事の図入りでの説明をご覧になっている前提で進んでいますが、SNNフィルタの肝となる、範囲(今回は5x5)内の対称(対角線上)となる2つのピクセルの色のうち、自身(中央のピクセル)の色に近い方を出力する構成にします。
ここはもんしょさんの記事をそのまま参考にさせて頂きました。
基本的な構成の意味はもんしょさんの記事で解説されています。
ただ、DotProductノードが使われていて「どゆこと?」と思う方もいらっしゃるかと思います。
私は中学以降の数学を全く勉強しなかったため、DotProductは2つのベクトルの向きがどれくらい違うかを-1~1で取得してくれる程度にしか理解できていないのですが、どうもAとBのベクトルを掛けた後に全てのチャンネルを足すようです。
この点については、RGBのグレースケール変換に便利というもんしょさんの解説がおかずさんのブログ記事で紹介されていたおかげで知りました。
なのでここでのDotノードは「3チャンネルあるRGB値を比較するために1チャンネルに値を統合するのに利用しているのだな」程度には理解できました。。
さて、さらにもう1つマテリアル関数を作成します。
いよいよSNNフィルタを5x5ピクセルの範囲で行う構成を組みます。
最終的にこんな感じになりました。
ノードが多くて線が散らばっているので一瞬ぎょっとするかと思いますが、同じことを繰り返しているだけで、行っていることはとてもシンプルだったりします。
まず左半分は、5x5のピクセルの色を拾う構成になっています。
ここは輪郭検出の構成を試したことがあれば大丈夫ですよね。
右半分では、下図のように対称(対角線上)の2つのピクセルと中央のピクセルを比較して色を決める関数に繋ぎますが、これが全部で12通りの組み合わせになります。
そして12通りの出力結果を順に足していきます。
最後に、25ピクセル分の色が足された値を25で割って平均の色を出力します。
こちらが最終的に決定する色になります。
この構成を少し変えれば、結果も変わって面白いかと思います。
最後にマテリアルを作成します。
Material Domain を Post Process に変更して、最後に作成したSNNフィルタのマテリアル関数をエミッシブカラーに繋いで、PostProcessVolume アクターに登録するだけです。
さて、別のシーンで結果を見てみます。SNNフィルタ適用前はこちら。
(画像クリックで倍サイズになります)
SNNフィルタを適用するとこちらのような結果になりました。
(画像クリックで倍サイズになります)
以上になります。
面白い効果ではありますが、アーティスト的には綺麗な見た目にするためにもう一工夫入れたいところ。。そうするとどんどん描画負荷が重くなっていきそうですが‥
という訳で参考になれば幸いです!