JKになりたい

何か書きたいことを書きます。主にWeb方面の技術系記事が多いかも。

レーダースキャンしてるっぽいシェーダーを作りたかった(失敗)

やりたいことができてないんだけど、未来への糧のために記録として残しておきます

今後は失敗の記録もしていく所存・・!

やりたかったこと

以前の記事で、遮蔽にキャラクターが隠れていても見えるようなレンダラを作成しました

sakataharumi.hatenablog.jp

ただ、上の記事の最後をみてもらうとわかるんですがOverrideしたマテリアルが微妙で、スキャンしてる感がありません・・。

そこで、スキャンしてる感があるシェーダーを作りたいな、と。

具体的には、「アウトラインが強調されて、アウトラインの中は適当な色で、ぐわんぐわんスキャンされてる感じ」をイメージしていました(語彙力がない)

できたもの(失敗)

youtu.be

スキャンしてるっぽい表現には成功していますが「アウトラインの表示」がうまくいってません。

ちなみに、シンプルなテクスチャであればよい感じにアウトラインだけが表示されて、スキャンされているっぽくなります。

youtu.be

試したアプローチ方法

今回作成したShaderGraphの全容を掲載します。

f:id:sakata_harumi:20201123203028p:plain

スキャンエフェクト部(赤の部分)とアウトライン抽出部(青の部分)から成ります。



スキャンエフェクト部の実装はシンプルで、Photoshopでグラデーションの画像を用意してUVアニメーションさせて、それをアルファにセットしてるだけです。

アウトライン抽出部ではCustomFunctionを利用しています。こちらを利用するとHLSLで書かれた関数を実行することができます。

docs.unity3d.com

今回は、微分フィルタによるエッジ抽出で、アウトラインを表示させるというアプローチを試しました。 微分フィルタには代表的なソーベルフィルタを採用しました。

ソーベルフィルタでは、縦用と横用で別のカーネルを用います。カーネルの具体的な値は以下のwikipediaを参照してみてください。

Sobel operator - Wikipedia

ソーベルフィルタの実装はこちらのブログを参考にさせて頂きました。

pond-comfat.hatenablog.com

今回実装したCustomFunction(sobel.hlsl)

#ifndef MYHLSLINCLUDE_INCLUDED
#define MYHLSLINCLUDE_INCLUDED

void sobel_float(float2 uv, Texture2D tex, SamplerState s, out float3 Out)
{
    float2 delta = float2(0.01, 0.01);

    float2 uv0 = uv + delta;
    float2 uv1 = uv + float2(0, -delta.y);
    float2 uv2 = uv + float2(delta.x, -delta.y);
    float2 uv3 = uv + float2(-delta.x, 0);
    float2 uv4 = uv + float2(0, 0);
    float2 uv5 = uv + float2(delta.x, 0);
    float2 uv6 = uv + float2(-delta.x, delta.y);
    float2 uv7 = uv + float2(0, delta.y);
    float2 uv8 = uv + float2(delta.x, delta.y);

    float3 col0 = tex.Sample(s, uv0);
    float3 col1 = tex.Sample(s, uv1);
    float3 col2 = tex.Sample(s, uv2);
    float3 col3 = tex.Sample(s, uv3);
    float3 col4 = tex.Sample(s, uv4);
    float3 col5 = tex.Sample(s, uv5);
    float3 col6 = tex.Sample(s, uv6);
    float3 col7 = tex.Sample(s, uv7);
    float3 col8 = tex.Sample(s, uv8);

    float cgx = col0 * -1 + col2 * 1 + col3 * -2 + col5 * 2 + col6 * -1 + col8 * 1;
    float cgy = col0 * -1 + col1 * -2 + col2 * -1 + col6 * 1 + col7 * 2 + col8 * 1;
    float cg = sqrt(cgx * cgx + cgy * cgy);
    Out = float3(1, 0, 0) * (cg - 0.2);
}
#endif

やっていることは、wikipediaに書かれていたソーベルフィルタのカーネルを着目するUV座標を軸として畳み込んでいるだけです。

縦のエッジと横のエッジでそれぞれ別のカーネルになっているので、それぞれについて計算した上で合成する必要があることにだけ気を付けないといけません。


最後に、スキャン部とアウトライン部をMultiply Nodeで合成してAlphaに設定してあげれば完成です。(完成じゃないんだけど)

おわりに

複雑なテクスチャに対して微分フィルタでアウトライン表示させてみるというアプローチは多分うまくいかないことがわかりました。

別の方法を探りたい。。。誰か何か良い方法を知ってたら教えてください( ;∀;)