20200817のUnityに関する記事は5件です。

【Unity】LiDAR+ARFoundation+ShaderGraphで地形メッシュのエフェクトを作る

概要

3/25に発売されたLiDAR搭載のiPad用に地形エフェクトアプリ( https://apps.apple.com/us/app/id1526438768 )の開発を行った際、
リファレンスが探し辛く一部詰まるところがあったので、その部分の解決方法を残しておきます。

今回のアプリは上記の写真のようにLiDARで取った地形メッシュと人物にエフェクトをかけていく形としました。

開発環境

ソフトウェア バージョン
macOS 10.15.6
Xcode 11.6
Unity 2020.1.0f1
ARFoundation 4.0.2
ARKit 3.5
Universal RP 8.2.0
iOS(iPad) 14(preview)

やりたいこと

①PeopleOcclusion、Meshingの機能を保ちながら背景の暗さを変更する。(エフェクトがかかっている部分だけを残して他部分の明度を下げる。)
②LiDARで取得したメッシュにShaderGraphで制作したシェーダでエフェクトやテクスチャを乗せる。

詰まった箇所と解決方法を順番に書いていきます。(ARFoundationでURPを使う方法は割愛します)

①PeopleOcclusion、Meshingの機能を保ちながら背景の暗さを変更する

スクリーンショット 2020-08-17 15.43.18.png
ARCameraBackgroundにはUseCustomMaterialというチェックボックスがありますが、これにチェックをいれてPublic変数にマテリアルを入れたところで背景にマテリアルは適用されません。

調べたところ、どうやらこのチェックボックスは残されたままで内部の機能だけが廃止されたようです。
ということで、ARCameraBackground.cs内のm_DefaultMaterialがどのマテリアルで初期化されているかを追っていきます。

ARCameraBackground.cs
//省略
void Awake () {
   m_Camera = GetComponent<Camera> ();
   m_CameraManager = GetComponent<ARCameraManager> ();
   m_OcclusionManager = GetComponent<AROcclusionManager> ();
   m_DefaultMaterial = m_CameraManager.cameraMaterial;
}

Awakeでの初期化でm_DefaultMaterialにm_CameraManager.cameraMaterialが代入されています。
ここでcameraMaterialがどのシェーダを使っているのかをTextなどでデバッグするとARKitBackground.shaderというシェーダで背景のメッシュ、人物オクルージョンを画面に描画しているのがわかりました。

このシェーダの中で色々処理をしているようですが、複雑なので必要な箇所だけをピックアップしてコピーを変更していきます。

CustomARKitBackground.shader
Properties
    {
        _textureY ("TextureY", 2D) = "white" {}
        _textureCbCr ("TextureCbCr", 2D) = "black" {}
        _HumanStencil ("HumanStencil", 2D) = "black" {}
        _HumanDepth ("HumanDepth", 2D) = "black" {}
        _Brightness("_Brightness", Float) = 1
    }

まずは明度プロパティを外部スクリプトから変更できるようにBritenessをPropertiesの中に記述します。

CustomARKitBackground.shader
//省略
                float _Brightness;
//省略
#if ARKIT_HUMAN_SEGMENTATION_ENABLED

                if (ARKIT_SAMPLE_TEXTURE2D(_HumanStencil, sampler_HumanStencil, i.texcoord).r > 0.5h)
                {
                    // 人物の処理はこのブロック内。
                    // Sample the human depth (in meters).
                    float humanDistance = ARKIT_SAMPLE_TEXTURE2D(_HumanDepth, sampler_HumanDepth, i.texcoord).r;

                    // Convert the distance to depth.
                    depthValue = ConvertDistanceToDepth(humanDistance);           

                }else{
                    //このブロックを追記。人物以外の処理はここ。
                    c.rgb = fixed3(c.r * _Brightness, c.g * _Brightness, c.b * _Brightness);
                }
#endif // ARKIT_HUMAN_SEGMENTATION_ENABLED

ずっと下にいくとフラグメントシェーダの記述部分がありますが、そこに上記のように追記をします。

HumanStencilが人物の場合は1、背景の場合は0が出力されるようになっており
ここで分岐させて処理を分けているようなので、elseを追記して背景部分を分岐させてあげます。

背景だけを暗くする処理としての追記は簡単で、カメラから拾ってきたRGBにBriteness変数を掛け合わせているだけです。
手書きのshaderに精通している訳ではなく未だ細かい芸はできませんがやりようによっては他にも色々できそうですね。

そしてこれを

     propId = Shader.PropertyToID ("_Brightness");

等で変更可能にしてやると背景の明度をスクリプトから変えたりする事ができます。

②LiDARで取得したメッシュにShaderGraphで制作したシェーダでエフェクトやテクスチャを乗せる

ARFoundationにはLiDARで取得したMeshにPrefabを適用することができます。
(PrefabのMeshFilterがNone以外だと表示がバグります。)

今回はこのMeshPrefabにShaderGraphで作ったシェーダを適用して地形エフェクトを実現しようという作戦です。

スクリーンショット 2020-08-17 15.28.55.png

ここで注意なのですが、現時点ではシェーダに必要なUV、頂点カラーは提供されていません。
(UVが提供されていない為、Editor上のプレビューでは正常に表示されていても実機では表示できないという仕様を仕様だと知らずにRP周りをごちゃごちゃ数日いじりまわして断念しかけていた)

ここのフォーラムでは、MeshPrefabNormalsConcurrent Queue Size以外はARKit3.5では提供されていないとのこと。
https://forum.unity.com/threads/using-arkit-meshing-with-ar-foundation-4-0.883981/

解決策

メッシュのUVではなく、オブジェクト座標を用いてテクスチャを貼る。
という方法で実現をしました。

PositionノードのSpaceのプルダウンタブが初期値はWorldですが、ここをObjectに変更してUVを欲しているノードの初期値につないでやります。
スクリーンショット 2020-08-17 17.09.18.png

Z軸の情報を切り捨てている為、XY平面に並行な面以外のテクスチャやエフェクトは少し伸びてしまいますが、UVノードを使わずに表示させることができました。
(SplitやNormal等を用いてUVを補正してあげたりすれば多少マシにはなりますが、伸びずにぴったり貼ることは難しいようです。どなたか情報を求む。。)

あとはShaderGraphで好きなものを作ってMeshPrefabにマテリアルを適用したPrefabを突っ込むだけで地形エフェクトが完成します。

これはシェーダの分岐部分で人物部分に手書きシェーダを適用させたものですが、
・人物オクルージョン(+人物エフェクト)
・地形メッシュにエフェクト
・背景だけ明暗調整
をしっかりと併用する事ができました。

今回開発したアプリはこちらのURLで公開されているのでぜひダウンロードして遊んでみてください。
https://apps.apple.com/us/app/id1526438768

それでは皆様良きARライフを。お疲れ様でした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】macOS向けビルドしたアプリにドラッグアンドドロップを可能にするUniDragAndDropForMacを作りました

UnityからmacOS向けのアプリをビルドして、そのアプリに対しドラッグアンドドロップを実装するという知見は、僕が調べた限り見つける事は出来なかったため、UniDragAndDropForMacというネイティブプラグインを作りました。
macDD.gif
GitHub:baobao/UniDragAndDropForMac

以下技術的な話になります。

そもそも出来るのか?

参考 : 【Unity】【C#】ランタイム時にファイルをドラッグ&ドロップして取得する(Windows のみ)
調査する中Windows版での実装方法は見つかりました。ソースをチラ見すると、どうやらWindowsのネイティブ機能を使って実装している事が分かったので、macOSでも同様にネイティブの機能を使えば実装できるだろうと思いました。

用意されていないネイティブ機能を使う場合はネイティブプラグインで解決させる

まずはCocoaフレームワークの中にドラッグアンドドロップのAPIを調査します。

Swiftはほぼほぼ未経験だったので、成果物を作り上げるスピードを優先させるため今回はObjective-C(以下:ObjC)で実装することにしました(後者の記事を参考にさせて頂きました)。

  • C#側からObjCに文字列(ファイルパス)を送ってもらう関数ポインタを渡す
  • Unityが作ったネイティブViewにアクセス
  • ObjC側でドラッグアンドドロップされたファイルパスをC#に送る

このような手順で実装していきます。

C#側からObjCに文字列(ファイルパス)を送ってもらう関数ポインタを渡す

UniDragAndDrop.cs
// コールバックのデリゲート
delegate void callback_delegate(string val);

// ネイティブプラグインの実行、関数ポインタをobjCにわたす
[DllImport("UniDragAndDrop")]
private static extern void Initialize(callback_delegate callback);

// objCから実行されるコールバック
[MonoPInvokeCallback(typeof(callback_delegate))]
private static void cs_callback(string dragAndDropPath)
{
    // Do Something...
}

C#側からObjCにアクセスする部分は上記のようなコードになっています。


UniDragAndDrop.mm
// ドラッグアンドドロップを実装したViewをアプリ全面に生成
GetDragAndDropFilePath *ddview = [[GetDragAndDropFilePath alloc] initWithFrame:view.frame];

// C#の関数ポインタを渡す
[ddview setCallback:callback];

GetDragAndDropFilePathはNSImageViewを継承したドラッグアンドドロップを実装するViewです。これをアプリ全面に表示することでドラッグアンドドロップが出来るようにしています。

Unityが作ったネイティブViewにアクセス

NSArray *ar = [NSApp orderedWindows];
NSWindow *window = [ar objectAtIndex:0];
NSView *view = [window contentView];

この3行の処理でUnityが実行しているネイティブのNSView(*view)を取得しています。
*viewに対してドラッグアンドドロップ用のView(前述のGetDragAndDropFilePath)を追加していきます。

ObjC側でドラッグアンドドロップされたファイルパスをC#に送る

GetDragAndDropFilePath.h
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

typedef void (*cs_callback)(const char*);

@interface GetDragAndDropFilePath : NSImageView {
    cs_callback _callback;
}
- (void)setCallback:(cs_callback) callback;
@end

NSImageViewを継承したクラスを作成してドラッグアンドドロップを実装しています。

GetDragAndDropFilePath.mm
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
    NSURL* fileURL = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
    NSString *url = fileURL!=NULL ? [fileURL path] : @"";
    // C#にファイルパスを送る
    _callback([url UTF8String]);
    return NO;
}
@end 

ドロップが完了したタイミングで予め渡されていたC#の関数に対してファイルパスを送るようにしています。

UniDragAndDropForMacとしてリリース

どうせ作るならOSSで作りたい欲求が有るので、以下のリポジトリにObjCソース含め全て公開しています。
UniDragAndDropForMac

インストール方法

UniDragAndDropForMac.unitypackageを用意しているのでインポートして使ってください。

最後に

欲しい物が無い、探しても見つからない、そんな時は今後もネイティブの力を使って作っていこうと思います。

環境

  • Unity2019.4.4f1
  • macOS Catalina 10.15.5
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityEditor拡張からTransportなどのプロパティ値を変更して反映する

概要

Unityのエディタ拡張などでオブジェクトの位置などを一括で設定したい!って場合のやり方を記載する。

やり方

void SetPosition(Vector3 pos)
{
    var obj = GameObject.Find("Player");
    obj.transform.position = pos;
}

これでPlayerゲームオブジェクトの位置を設定できた。…わけではない。

これをエディタ上で実行してもSceneビュー上だと確かに位置は動いているが、
変更された形跡もないし保存もできない、なぜだか変更した値が反映されていないというおかしな状態になってしまう。

正しいやり方

一旦、値を設定するtransformSerializedObjectに変換して値を変更する必要がある。

void SetPosition(Vector3 pos)
{
    var obj = GameObject.Find("Player");

    var serializedTransform = new SerializedObject(obj.transform);

    // オブジェクトの値を最新のものに更新
    serializedTransform.Update();

    // 位置を示すプロパティ名はm_LocalPosition
    // 型はVector3なので.vector3Valueに代入
    serializedTransform.FindProperty("m_LocalPosition").vector3Value = pos;

    // 変更した値を適用
    serializedTransform.ApplyModifiedProperties();
}

上記のようにして変更する。
すると、値はしっかりと変更され、Hierarchyビューでも変更を示すマークが付く。もちろんUndoも可能になる。

プロパティ名の調べ方

プロパティ名は、以下のようにすると一覧が取得できる。

var iterator = serializedObject.GetIterator();
while (iterator.NextVisible(true)){
    Debug.Log (iterator.propertyPath);
}

下記のリンク先の丸パクリですみません……。

参考リンク

以下の記事を参考にさせていただきました。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity2020からTransformの回転値インスペクタでクォータニオンのコピーが出来るようになっていた

Unity2020からはインスペクタ上でクォータニオンの値をコピーできるようになった、という地味な新機能紹介です。

CopyQuaternion.gif
Rotation文字部分で右クリックするとコンテキストメニューが表示されるようになりました。
オイラー角(50, 30, -10)をCopy Quaternionすると、以下のコードがペーストされます。

// クォータニオンのペースト
Quaternion(0.386220366,0.269255698,-0.185263887,0.862561643)

とても便利です。

上記の動画でも分かる通りオイラー角もVector3型でコピーできるようになりました。以下のようにペーストされます。

// オイラー角のペースト
Vector3(50.0000038,30.0000038,350)

同様にPosition、ScaleもVector3型でコピーできるようになっています。


今回紹介したTipsはUnityの安原さんの動画でさらっと紹介されています。
納涼クォータニオン夏祭り - Unityステーション

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MagicLeapでTextMeshProのSpriteAssetが片目が描画されないときに確認したいこと

とりあえず対症療法だけどメモ

ProjectSettings > XR Plug-in Management > MagicLeapSettings にあるForceMultipassにチェックを入れることでとりあえず描画できる

Settings.JPG

ただSinglepassStereoRendeeringのほうがパフォーマンス的にはよいのでできればTextMeshProで利用しているマテリアルをSinglepassに対応したものに差し替えたほうが良いかも

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む