UnityのスキャンラインシェーダーでAR空間認識の表現にチャレンジ
2021年8月2日

こんにちは。
エム・ソフトのエンジニアのasanoです。

エム・ソフトでは、ARやXRのアプリを日々開発しています。
開発に役立ちそうな情報やメモなどを不定期で公開していきたいと思います。

はじめに―AR平面検出のイメージを新しく実装

先日、弊社SlackのUnityチャンネルにて、エム・ソフト開発のアプリ「Pinspect」に関するこのような投稿がありました。



現在の面検出の演出をこれにアップデートしたいな…

https://github.com/thorikawa/ar-meshwave


ARアプリでは床面や壁面など、水平面の検出をよく行いますが、その際のビジュアルイメージを変えたいとのこと。

もちろんPinspectも平面検出を行っているので、ビジュアルイメージを改善することで、より使いやすいアプリとなりそうです。

Readmeの動画をみたところ、どうやらスキャンラインのシェーダーの様子。
せっかくなので、今回は勉強がてら実装をしてみようと思います。

※スキャンラインシェーダーとは?
光のラインが走っているような表現(スキャンライン)ができるように作成されたシェーダー(描画プログラム)のこと

Unityでの前準備

今回、使用する環境は以下の通りです。
Unity 2019.4.17

まずはシェーダを作成します。
Projectタブの+ボタンからShader→Unlit Shaderを選択します。

シェーダー名はScanlineとしました。

作成したらScanlineシェーダーを開きます。
UnlitShaderからシェーダーを作成するとShaderのグループがUnlitとなるため、わかりやすいようにCustom変更します。

【変更前】

↓↓

【変更後】

続いてMaterialを作成します。
プロジェクトタブの+ボタンからMaterialを選択します。

Material名はShaderと同様にScanlineにしました。

作成後、Scanline Materialを選択し、InspectorのShaderを先ほど作成したScanline Shaderに変更します。

 → 

最後にShaderの動作確認用にCubeをシーン上に置いてマテリアルをアタッチします。
Hierarchy タブの+ボタンから3D Object→Cubeと選択し、

確認がしやすいようにScaleを1, 1, 20としておきます。

最後にCubeに先ほど作成したScanlineマテリアルをアタッチします。

これで前準備は終了です。

スキャンライン表現を実装する

続いてスキャンラインの表現の実装をしていきます。
今回はモデルのZ座標が一定の範囲内にある時だけ色を塗るという実装をしていきます。

構造体の定義、関数の変更

その前に、今のShaderの実装だと、モデルの座標を使用できないので、構造体の定義とvert関数を以下のように変更します。

struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD2;
};

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}

これでモデルのZ座標の値を参照できるようになるため、frag関数を以下のように変更します。

fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);

float z = i.worldPos.z;
// Z座標が0~0.1以外は透過
col.a = step(0, z) * step(z, 0.1);
return col;
}

step関数では第2引数が第1引数以上であれば1を、第2引数の方が小さければ0を返すため、この場合Z座標が、0以上0.1未満の時のみ表示するようになっています。

ただし、このままだと透過されず、何も変化がないため、SubShaderブロックの先頭に以下のように修正します。

Tags { “RenderType” = “Transparent” “Queue” = “Transparent” }
Blend SrcAlpha OneMinusSrcAlpha

こうすることで下の画像のような結果になると思います。

グラデーションの作成

続いてグラデーションを作っていきます。

グラデーションの表現にはsmoothstep関数を使用します。
先ほど変更したfrag関数に以下の処理を追加します。

                col.a += step(z, 0) * smoothstep(-0.4, 0.0, z);

すると下の画像のようになると思います。

smoothstep関数は0から1までの値を返す関数なのですが、第1引数を0、第2引数を1として第3引数をその範囲内で緩やかに補間する関数です。
なので、追加した処理ではZが-0.4から0に向かって緩やかに透過度が上昇していくようになっています。

線の移動を実装

続いてこの線が移動するようにします。

移動自体は簡単です。
今回はだんだんと遠くに向かうようにしたいので、変数zに時間を減算していきます。

時間はUnityで定義されているシェーダー変数_Timeを使用します。
変数zの定義直後に以下の処理を追加します。

               z -= _Time.w;

最後に繰り返し表示されるようにします。

繰り返しではfmod関数を使用します。
fmod関数は剰余を計算する関数で、第1引数を第2引数で割ったあまりを返してくれます。

先ほど追加した処理に以下の処理を追加します。

                z = fmod(z, 3.3);

今回は第2引数に3.3(3.3メートル間隔で繰り返し)と入れていますが、実際に使う時は目的にあった値を入れてください。
以上でスキャンラインの実装が完了になります。

スキャンラインの色を変更する

続いて色を実装していきます。
単色だと面白くないのでカラフルにしていこうと思います。

変数が1軸なので、HSVの色相にZ座標を入れて、そこからRGBに変換する方法が良さそうです。
動作確認のため、スキャンラインの処理はコメントアウトしておきます。

HSVからRGBに変換するためには、Hの値が0~360の範囲内で、Hが0~60までの場合は…と場合分けして考えなければならないのですが、今回は以下のように実装しました。

               z -= _Time.w;
// 追記
col.rgb = clamp(abs(frac(z / 45. + (int3(0, 1, 2) / 3.)) * 6 – 3) – 1, 0, 1);
z = fmod(z, 3.3);

実装方法についてはこちらの記事を参考にしています。

[汎用関数]HSV2RGB 関数
https://qiita.com/keim_at_si/items/c2d1afd6443f3040e900

今回は z/ 45.としていますが、45.の部分を変更することで色が1周するまでの長さを変更することができます。

最後にスキャンラインを元に戻して、これで色の実装は以上になります。

スキャンラインシェーダの完成

最後にARで検出した面に今回作成したシェーダーを当ててみました。
スキャンラインシェーダーによって平面検出されているのがわかると思います。

これによって、水平面検出時のビジュアルイメージがわかりやすくなりそうですね!

ただ、個人的には、うーんなんかこれじゃない感が…
出来栄えはともかくとして、ARに限らず3DCGでの表現としてシェーダーが役に立つことは確かなので、今後も勉強していきたいです。

今後もAR、XRの開発についてのノウハウや解説など、随時発信してきますので、楽しみにしていただけると嬉しいです。

エム・ソフトでは、iOSのVR、ARアプリ製品の開発、また点群を使ったアプリ等の開発を行っています。
これらの技術を活用したアプリケーション開発のご相談や、各種デモ・事例紹介のご依頼など、お気軽にご相談下さい。

【参考文献まとめ】
ビルトインのシェーダー変数 – Unity マニュアル
https://docs.unity3d.com/ja/2019.4/Manual/SL-UnityShaderVariables.html

Unityを利用した空間スキャンライン表現 | STYLY
https://styly.cc/ja/tips/unity-shader-worldscanline/

Shader(HLSL), 手続き的にテクスチャ生成など行うとき使用頻度の高い関数
https://qiita.com/oishihiroaki/items/9d899cdcb9bee682531a

[汎用関数]HSV2RGB 関数
https://qiita.com/keim_at_si/items/c2d1afd6443f3040e900

詳しくは、エム・ソフトのウェブサイトで。

(Visited 1 times, 1 visits today)
関連記事
Translate »