20200715のUnityに関する記事は12件です。

VCIよわよわ初心者開発者に贈る「最初の一歩の前」

対象

VCIを作ってみたいバーチャルキャストよわよわ初心者
※よわよわ初心者の定義は下記を参照してね!
image.png

言っておきたいことお品書き

  • unityは最新版を入れてはいけない
  • unityはモデリングソフトではない
  • マテリアルってなんぞ?
  • 画像の取り扱いは注意
  • discord入っておいたほうが良い
  • luaのスクリプトのお手本が欲しいと思ったらembeddedscriptworkspaceフォルダを漁るべし

unityは最新版を入れてはいけない

Unityには、バージョンによって必ずしも互換性がありません!かなり内部は複雑なようで、細かなバージョン違いでもエラーが出たりするので、導入するバージョンは注意しましょう。もちろん最新版なら良いというものではありません。必ず推奨バージョンを調べて導入すること!

というか公式の情報も修正が追いついてないようで、いろいろなことが書いてありますが、現在(2020/07/15)ではUnity2019.3(筆者はvci0.27発表当時の最新版の2019.3.14fを使用)が推奨環境のようです。(参照元:https://github.com/virtual-cast/VCI/releases )2018系でも動くみたいですが、今後どうなるかわからないので開発側のバージョンに合わせておいたほうが良いでしょう。ちなみに私の環境ではeffekseer(エフェクトを入れる際に使われるもの)がバージョンをまたぐと表示が変になりました。

unityはモデリングソフトではない

私も最初勘違いしていたのですが、unityで何でもやるのかな、と思ってたらそうではないです。モデリングはblenderなどの別のソフトを使って行います。unityだけでも大変なのにblenderやeffekseerや他にも色んなものを覚えないといけません、、、大変だね!

とはいえ、球や板などのよくあるモデルは最初から用意されているので、それらを使えば試作研究には用は足ります。私はだいたい球だけとかを使って大まかなイメージを決めてからモデリングを始めます。

マテリアルってなんぞ?

私自身があんまり詳しい人でないので言うのがはばかられるのですが、事バーチャルキャストだけで言うなら、「見た目を規定するもの」と思って良いと思います。一色だけだったり、テクスチャと言われる画像をモデルに貼り付けたり、します。基本的に画像はマテリアルとして読み込まれるらしく、文字(バーチャルキャストではtextmeshproという文字を入れるオブジェクト)もマテリアルです。文字がマテリアル(物質)ってよくわかりませんが、見栄えに関係するものにはマテリアル(かパーティクル)が関係するようです。気持ち悪いですが慣れましょう。

似たものでシェーダーというのもありますがこれは影などの付き方を決めるものです(が、いろんな事ができて私は理解しきれてない、、、)。

画像の取り扱いは注意

画像は読み込むと基本いじられてしまいます
まず形状が正方形にされます(これは処理を早くするためみたいです)。更にサイズも、大きいものを入れても2048*2048くらいに小さくされてしまいます。大きくしようとしても8kまでしか無理です。更に、圧縮されたりもします。あと、色も照明をきちんと設定してあげないとうまく合いません。そこら辺こだわりがある方は特に注意してください!

discordは入っておいたほうが良い

公式wikiには大体のことが書いてあるのですが、常識扱いされて書いてない仕様が結構あったり、古いまま残ってたりすることがあるので、人に聞かないとわからないことが結構あります。そういう人のためにdiscord(チャットのようなものです)チャンネルがあります。VCI開発者チャンネルというのが公式wikiにリンクがあるので、ぜひ入りましょう。

luaのスクリプトのお手本が欲しいと思ったらembeddedscriptworkspaceフォルダを漁るべし

準備もできてチュートリアルもこなして、早速luaでスクリプトを組もう!と思ったときにお手本が欲しくなると思います。インターネット上を漁っても今のところあまりないので、ここは正直に公式のサンプルを見ましょう。The seed onlineで公式1のアイテム(https://seed.online/users/2 )は単機能ごとに用意されているので参考にしやすいです。
この子達を取り込んで、バーチャルキャストの中で呼び出すと、C:/Users/_USER_NAME_/AppData/LocalLow/infiniteloop Co,Ltd/VirtualCast/EmbeddedScriptWorkspace
のフォルダの中に対応されるスクリプトが保存されます。

ちなみに、チュートリアルはこっち(https://virtualcast.jp/wiki/doku.php?id=vci:script:tutorial )の方をおすすめします。youtube動画のやつ(スイカ)もありますが、こちらはよわよわ初心者にはちょっとレベルが高いので、自信のある方以外は、wikiの方にしましょう。cubeだけでも結構難儀するのでまずは小さく始めることをおすすめします。

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

VCIが消失その5

結論

小さすぎる

背景

VR配信・SNSサービスのバーチャルキャストでは、他のVRSNSに見られない仕組みとしてVCIというものがあります。VCIはバーチャル空間で取り扱えるアイテムであり、好きな場所で好きなタイミングで取り出すことができ、その挙動はスクリプトを用いて制御することでインタラクティブな効果を奏することができます。

そのVCIを制作するにあたって、制作の最初の段階で(特に初心者に多い)結構な割合で作ったはずのVCIを呼び出しても出てこない(見つけられない)事が起きます。

本記事ではこの現象に対する事例分けをし、それぞれの対処方法を紹介し、、、たいのですが、結構多岐にわたるので、そのうちの一つをご説明します。

対象

主に初心者

症状

なにか出てきたようなのだけど見当たらない
部分的に判定だけある

どういうことか

これはblenderから初めて取り込んだ際に起こりがちです。
初期設定では、blenderから読み込んだモデル(fbx等)はscaleが100のときに意図した大きさになるように設定されています。
几帳面な方は特に、scale100を見つけると「あれ?こんな数値にしたっけ?直さなきゃ」となって1にしてしまうことが結構あります。これをやってしまうと大きさが1/100になるため、10cmくらいで設計していると1mmとかもっと小さくなってしまい、見えなくなってしまいます。

解決策

解決策というか回避策は2つあります。
一つは、100のまま放っておく方法です。ただし、これはあまりおすすめしません。なぜなら、scalable(アイテムを拡縮できるようにする設定)を設定した際に、大きさ外としない形で変形してしまう恐れがあるからです。(逆に言えばscalableにしないのであればそのままでも良い)

2つ目は、読み込みのセッティングを変える方法です。
下図の「asprin」を例にとって説明します。
image.png

3dモデルを選択すると、インスペクタにモデルの情報が表示されます。ここの"Convert Units"にチェックを外してあげてください。
image.png
チェックを確認したら、下の方にスクロールして"Apply"ボタンを押します。
image.png
すると、エディター上では大きくなったように見えます。
scaleの値を1に設定してやれば、意図した通りになっています。

ちなみに

scalableの仕様の関係で、モデルの拡縮をunity editor上で行うのはあまり推奨されませんが、こうなるとモデルの大きさを調整したいと思ったときにblenderに戻らなければなりません。しかし、解決策の2つ目の方法を使うことで横着することができます。
解決策の2つ目と同様にモデルを選択し、"Scale Factor"を好きな値(例えば0.1とか)に設定して、Applyを押せば、アイテムのtransform のscale 1のときの大きさが変わっています(例のように0.1にしていれば、10分の1になります)
image.png

image.png
image.png

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

VCIが消失その4

結論

非表示にしてる

背景

VR配信・SNSサービスのバーチャルキャストでは、他のVRSNSに見られない仕組みとしてVCIというものがあります。VCIはバーチャル空間で取り扱えるアイテムであり、好きな場所で好きなタイミングで取り出すことができ、その挙動はスクリプトを用いて制御することでインタラクティブな効果を奏することができます。

そのVCIを制作するにあたって、制作の最初の段階で(特に初心者に多い)結構な割合で作ったはずのVCIを呼び出しても出てこない(見つけられない)事が起きます。

本記事ではこの現象に対する事例分けをし、それぞれの対処方法を紹介し、、、たいのですが、結構多岐にわたるので、そのうちの一つをご説明します。

作者のモチベーションが続けばもっと書きます

対象

主に初心者

症状

目の前に出てくることを期待していたものが見当たらない
その上、そもそも出てきた雰囲気がない

どういうことか

image.png
インスペクターの一番左上にチェックボックスがありますが、これはオブジェクトの表示・非表示を切り替えるものです。これで非表示になっていると出力されません。

解決策

チェック入れよう。それで出力すればもとに戻るよ。
image.png
GameObjectだけでなく、他の階層(サブアイテムなど)でも起きるので、他の海藻もチェックしよう。下図のように、非表示になっているアイテムはhierarchyでも色が薄くなるので、そこでチェックできます。
image.png

ちなみに

今でもたまにやらかします。いくつか数をこなしていくと、同一シーンでアイテムが重なってきてしまうので非表示の機能は良く使います。初心者というよりかは初級者の領域に入った際にぶつかるミスでしょうか。

また、単純に複雑なアイテムを作ったりするときも見にくいので非表示にしてみたりしつつ作っているとたまに忘れたまま出力してしまいます。気をつけよう。
(これできればエラー吐いて欲しいなぁ、、GameObject階層は特に)

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

Androidでポストエフェクトをつけると画面が真っ暗になる問題

問題

CameraにOnRenderImageをoverrideしたスクリプトをアタッチする形式でポストエフェクトを作成したところUnityEditor上では意図通りの動作を確認できるのに、Androidの実機で確認すると画面が真っ黒になるという問題に遭遇しました。

解決方法

.shaderZTest Alwaysを使うようにすると、意図通り動くようになりました。

Shader "Custom/HorizontalScaleShader"
{
      Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        [PowerSlider(1)]_HorizontalScale ("HorizontalScale", Range(0.01,1.00)) = 1
    }

    SubShader {
        Pass {
            Tags { "RenderType"="Opaque" }
            ZTest Always

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float _HorizontalScale;

            struct v2f {
                half4 pos : POSITION;
                half2 uv : TEXCOORD0;
            };

            float4 _MainTex_ST;

            v2f vert(appdata_base v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            half4 frag(v2f i) : COLOR {
                half2 uv = half2(i.uv);
                float horizontalScale = _HorizontalScale;
                uv.x = horizontalScale*uv.x+(1.0-horizontalScale)*0.5;
                fixed4 col = tex2D(_MainTex, uv);
                return col;
            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

結果

実機で動いた

posteffect.gif

参考文献

https://forum.unity.com/threads/graphics-blit-not-working-for-android.534835/

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

AndroidでPost Effectをつけると画面が真っ暗になる問題

問題

CameraにOnRenderImageをoverrideしたスクリプトをアタッチする形式でポストエフェクトを作成したところUnityEditor上では意図通りの動作を確認できるのに、Androidの実機で確認すると画面が真っ黒になるという問題に遭遇しました。

解決方法

.shaderZTest Alwaysを使うようにすると、意図通り動くようになりました。

Shader "Custom/HorizontalScaleShader"
{
      Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        [PowerSlider(1)]_HorizontalScale ("HorizontalScale", Range(0.01,1.00)) = 1
    }

    SubShader {
        Pass {
            Tags { "RenderType"="Opaque" }
            ZTest Always

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float _HorizontalScale;

            struct v2f {
                half4 pos : POSITION;
                half2 uv : TEXCOORD0;
            };

            float4 _MainTex_ST;

            v2f vert(appdata_base v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            half4 frag(v2f i) : COLOR {
                half2 uv = half2(i.uv);
                float horizontalScale = _HorizontalScale;
                uv.x = horizontalScale*uv.x+(1.0-horizontalScale)*0.5;
                fixed4 col = tex2D(_MainTex, uv);
                return col;
            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

結果

実機で動いた

posteffect.gif

参考文献

https://forum.unity.com/threads/graphics-blit-not-working-for-android.534835/

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

VCIが消失その3

結論

位置を入れる階層をチェックしよう。
位置はサブアイテムより下の階層だけに入れよう。

背景

VR配信・SNSサービスのバーチャルキャストでは、他のVRSNSに見られない仕組みとしてVCIというものがあります。VCIはバーチャル空間で取り扱えるアイテムであり、好きな場所で好きなタイミングで取り出すことができ、その挙動はスクリプトを用いて制御することでインタラクティブな効果を奏することができます。

そのVCIを制作するにあたって、制作の最初の段階で(特に初心者に多い)結構な割合で作ったはずのVCIを呼び出しても出てこない(見つけられない)事が起きます。

本記事ではこの現象に対する事例分けをし、それぞれの対処方法を紹介し、、、たいのですが、結構多岐にわたるので、そのうちの一つをご説明します。

作者のモチベーションが続けばもっと書きます

対象

all user(主に初心者)

症状

  • 目の前に出てくることを期待していたものが出現しない
  • そもそも出てこない

- VCIが変なところに出現する

原因

位置を入れる階層を間違えてる

どういうことか

VCIを出力する際に、GameObjectの階層(サブアイテムの階層)に入れられているTransformの値はすべて無視されます。
そのため、VCIが消失その2にで紹介したY=1に設定しようとして、GameObjectの階層にY=1を入れてしまうと、unity上では位置が動いているのに、バーチャルキャストで取り出した際にやはり地面に出てしまっていることがあります。
image.png

解決策

単純にGameObjectのtransformの値を確認しましょう。ここに値が入っていて良いことはあまりないので、(0,0,0)になっているか確認する癖をつけるといいです。
image.png
そして、上図のように値を入れて再度確認しましょう。

ちなみに

わけも分からず矢印(unityで出てくる三色の矢印)を弄って位置を設定していると、はるか上に行ってしまうパターンもあります(1敗)
後ろに出てくることもあります(1敗)
床の下にめり込むこともあります(2敗くらい、、、たぶん、、、)
やはり透明な背景で落ち着いて探すのが望ましいです。

そして、こんなときもやはり適当なcubeが入っているとロストしにくいです。

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

VCIが消失その2

結論

実は地面にある

背景

VR配信・SNSサービスのバーチャルキャストでは、他のVRSNSに見られない仕組みとしてVCIというものがあります。VCIはバーチャル空間で取り扱えるアイテムであり、好きな場所で好きなタイミングで取り出すことができ、その挙動はスクリプトを用いて制御することでインタラクティブな効果を奏することができます。

そのVCIを制作するにあたって、制作の最初の段階で(特に初心者に多い)結構な割合で作ったはずのVCIを呼び出しても出てこない(見つけられない)事が起きます。

本記事ではこの現象に対する事例分けをし、それぞれの対処方法を紹介し、、、たいのですが、結構多岐にわたるので、そのうちの一つをご説明します。

作者のモチベーションが続けばもっと書きます

対象

主に初心者

症状

目の前に出てくることを期待していたものが見当たらない

どういうことか

image.png

実はというかよくよく考えると当たり前なのですが、何も設定しないと、アイテムは座標(0,0,0)に配置されます。これは一体何処なのかと言うと、目の前の地面です。最初に作るものは小物か何かでしょうから、場合によってはサイズが小さすぎるのと複合して何処にあるのかわからなくなります。出てこないと思ったら、焦らずまず地面の上を探してみましょう。

解決策

サブアイテムの"Transform"のPositionのYを1(m)に設定すること。
image.png
ただここで注意!座標はサブアイテム(かそれ以下)に入れること!GameObject(一番上の階層)に入れてもunity上では意図した場所にいるように見えるけど、実際は変わっていないなんてことが起きてしまいますので気をつけて。

ちなみに

アバターやアイテムによっては、取れなくなって、場合によっては触ることすらできなくなったりします。
昔はこうなると手の施しようがなくて、修正しても更新できないので一旦ログイン画面まで戻る必要がありました。

今はメニューボタン長押しでハンドレーザーを出して更新もできるし、引き寄せもできるようになったので、その場で修正できるようになりました。便利♪開発陣のみなさまありがとうです。

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

AssetDatabaseメモ

ちょこちょこメモ

TextMeshProの付いているPrefabを探してフォント名・マテリアル名が欲しい

全ゲームオブジェクトを取得

 フィルタをObjectにするとなんでもかんでも取ってくるので
 GameObjectにしておく、Prefabにしかないなら"t: Prefab"でも良さそう

AssetDatabase.FindAssets("t: GameObject");

GUIDからパスに変換
var path = AssetDatabase.GUIDToAssetPath(guid);

パスからGameObjectに変換
GameObject currentObject = AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject> (path);

子オブジェクトも含めてどこかにTextMeshProUGUIを持ってないか検索(複数形ありえるのでGetComponents側を使う)
currentObject.GetComponentsInChildren<TextMeshProUGUI>()

ラベルを追加する

 ラベル付けしてEditor拡張から何かに使えないか実験

string[] labels = new string[] { "TextMesh" };
AssetDatabase.SetLabels(currentObject,labels);

using TMPro;
using UnityEditor;
using UnityEngine;

public class TextMeshProTest : MonoBehaviour
{
    public void OnClickCheckAsset()
    {
        AssetDatabase.Refresh();

        int targetCount = 0;
        var allObjects = AssetDatabase.FindAssets("t: GameObject");
        List<string> setFontsList = new List<string>();

        string setFontAppendString = string.Empty;

        foreach (var guid in allObjects)
        {
            var path = AssetDatabase.GUIDToAssetPath(guid);

            GameObject currentObject = AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(path);

            var textMeshProUGUIArray = currentObject.GetComponentsInChildren<TextMeshProUGUI>();

            Debug.LogWarning($"basePath={ path}");

            foreach (var textMeshProUGUI in textMeshProUGUIArray)
            {
                if (textMeshProUGUI != null)
                {
                    Debug.Log($"**********");
                    Debug.Log($"path={ path}");

                    if (textMeshProUGUI.font == null)
                    {
                        Debug.LogError($"textMeshProUGUI.gameObject.name={ textMeshProUGUI.gameObject.name}");
                    }
                    else
                    {
                        Debug.Log($"textMeshProUGUI.font.name={textMeshProUGUI.font.name}");
                        if (textMeshProUGUI.fontSharedMaterial == null)
                        {
                            Debug.LogError($"textMeshProUGUI.gameObject.name={ textMeshProUGUI.gameObject.name}");
                        }
                        else
                        {
                            Debug.Log($"textMeshProUGUI.fontMaterial.name={textMeshProUGUI.fontMaterial.name}");

                            // フォント名とマテリアル名をセットで追加
                            setFontsList.Add(textMeshProUGUI.font.name + "," + textMeshProUGUI.fontMaterial.name);

                            setFontAppendString = setFontAppendString + textMeshProUGUI.font.name + "," + textMeshProUGUI.fontMaterial.name + ",";

                            targetCount++;
                            Debug.Log($"targetCount={targetCount}");

                            // ラベルの追加
                            string[] labels = new string[] { "TextMesh" };
                            AssetDatabase.SetLabels(currentObject, labels);


                        }
                    }
                }
            }
        }

        Debug.Log($"setFontAppendString={setFontAppendString}");

        Debug.Log("***** END *****");

        Debug.Log($"targetCount={targetCount}");
    }
}

GetAssetPathを使えばObjectであればパスを取得できる(空文字で返って来る時もあるけど)

string targetPath = AssetDatabase.GetAssetPath(textMeshProUGUI.fontMaterial.mainTexture);
Debug.Log($"targetPath={targetPath}");
string targetPath2 = AssetDatabase.GetAssetPath(textMeshProUGUI);
Debug.Log($"targetPath2={targetPath2}");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AR顔認証打刻アプリを作る

はじめに

皆さん、仕事の日は毎日打刻していると思いますが。
正直めんどくさいですよね。
それを解決するには、楽にできる方法を考えるのが普通だと思っていましたが。
この記事を読んで、気分を上げる打刻という解決策があるということを教わり、便乗してVRがあるならARもありだろうということで、作ろうと思いました。

作ったもの

動画はこちらです。

追記
ビルドすることができました。

https://twitter.com/akomekagome/status/1283239374348406785

あれARは?と思ったと思いますが
Macが不調でスマホにビルドすることができませんでした...
後日、ビルド出来たら上げ直したいと思います。

コードをあげておきます。(github)

https://github.com/akomekagome/ARAttendanceManagementScript

使用ライブラリ

FreeeForUnity
Unirx
Unitask
Zenject
Dotween
AWS for .net
OVRLipsync
OpenCV + Unity

システムの流れ

1.カメラの画像からOpenCV + Unityで顔を検出
2.先ほどの画像をAWS Rekognitionで顔を照合
3.取得した顔情報から、Freeeの従業員データーと紐づけし、打刻を行う

Freeeへの接続

接続は先ほどの記事を書いたtoofusanさんのコードを使わせていただきました。

https://github.com/toofusan/FreeeForUnity

使用するには、client_id, client_secret,code, access token, authorizationCode, employeeIdが必要になります。
ただ、employeeIdは初期のセットアップで取得できないので、個別で取得する必要があるのですが、一番簡単な方法として
Free人事労務フリー/勤怠/従業員名
でアクセスしたurlが

https://p.secure.freee.co.jp/employees#/employeeId/年/月/

となっているので、ここから取得できます。

カメラの画像をOpenCV + Unityで顔を検出

AWS Rekognitionも無料ではないので、できるだけ費用を抑えるためカメラに顔が映ったときだけ顔照合しています。
OpenCVでデフォルトで入っているカスケード型分類器を使って、顔認識を行うのですが、Unityに持っていかないと使えないので、StreamingAssetsに持っていく必要があります。

OpenCvGateway.cs
public class OpenCvGateway
{
    public bool DetectFaceInImage(Mat mat, string cascadePath)
    {
        var cascade = new CascadeClassifier();
    // ここでカスケード型分類器のパスを指定
        cascade.Load(cascadePath);

        var faces = cascade.DetectMultiScale(mat, 1.1, 3, 0, new Size(20, 20));

        Debug.Log(faces.Length);
        return faces.Length > 0;
    }
}

AWS Rekognitionで顔認証 

通常UnityからAWSを使う場合は、AWS for Untiyから使用するのが普通だと思いますが、今回使うAWS Rekognitionが非対応だったため、やむを得ずaws for .netのnugetをunityに持ってきました。
ただ今思えば、AWS Lambaは使えるので、そちらの方でAWS Rekognitionは使ったほうがいい気がします。(未検証)
方法としては、nugetの拡張子をzipに変更し、中身から自分が欲しい.netのバージョンのdllを探し、UnityのPluginフォルダーに突っ込めば、使用できます。nugetは以下から取得できます。

https://www.nuget.org/packages/AWSSDK.Rekognition/3.5.0-beta

使い方は以下が参考になります。

https://docs.aws.amazon.com/ja_jp/sdk-for-net/v3/developer-guide/quick-start.html

AmazonRekognitionへの接続は以下のような形です。

AmazonRekognitionGateway.cs
    public class AmazonRekognitionGateway
    {
        AmazonRekognitionClient rekoClient;

        public AmazonRekognitionGateway(CognitoAWSCredentials credentials)
        {
            rekoClient = new AmazonRekognitionClient(credentials);
        }

        public AmazonRekognitionGateway(string keyId , string secretKey)
        {
            rekoClient = new AmazonRekognitionClient(keyId, secretKey, AwsSettings.Region);
        }

        public AmazonRekognitionGateway(AWSAuthData awsAuthData)
        {
            rekoClient = new AmazonRekognitionClient(awsAuthData.keyId, awsAuthData.secretKey);
        }

        public async UniTask<List<string>> AuthenticateFaceAsync(MemoryStream stream, string collectionId, CancellationToken token = default)
        {
            var image = new Image()
            {
                Bytes = stream
            };
            return await AuthenticateFaceAsync(image, collectionId, token);
        }

        private async UniTask<List<string>> AuthenticateFaceAsync(Image image, string collectionId, CancellationToken token = default)
        {
            var searchFacesByImageRequest = new SearchFacesByImageRequest()
            {
                CollectionId = collectionId,
                Image = image,
                FaceMatchThreshold = 90f,
                MaxFaces = 1
            };

            try
            {
                var searchFacesByImageResponse = await rekoClient.SearchFacesByImageAsync(searchFacesByImageRequest, token);

                var faceIds = searchFacesByImageResponse.FaceMatches
                    .Select(x => x.Face.FaceId)
                    .ToList();

                DebugExtensions.DebugShowList(faceIds);

                return faceIds;
            }
            catch (Exception e)
            {
                Debug.Log(e);
                return default;
            }
        }
    }

AWS for .netでAmazonRekognitionを紹介している記事が日本だとないので、知見を共有すると、カメラから取得したTexture2Dをjpgのbyte配列に変換し、それをMemoryStreamに変換することによって、Amazon S3を介することなく顔照合が行えます。(料金が浮く)

FaceRecognitionProvider.cs
       private async UniTask<List<string>> AuthenticateFace(Mat mat, CancellationToken token = default)
        {
            var tex = webCamClient.GetTexture2D();
            var jpgBytes = tex.EncodeToJPG();
            Destroy(tex);
            var stream = new MemoryStream(jpgBytes);

            return await rekoGate.AuthenticateFaceAsync(
                stream,
                AmazonRekognitionSettings.FaceCollectionId,
                token);
        }

FreeeのAPIから打刻を行う

AmazonRekognitionは顔がFaceId(文字列)と結びついているので、employeeIdを紐づけしておき、打刻を行います。

以下のコードでまず、打刻可能なタイプを取得します

FreeeEventHandler.cs
public void GetTimeClocksAvailableTypes(int employeeId, Client.Callback callback = default) 
{
    var endpoint = "https://api.freee.co.jp/hr/api/v1/employees/" + employeeId +
                   "/time_clocks/available_types";
    var parameter = new Dictionary<string, string>
    {
        {"company_id", company_id.ToString()}
    };
    StartCoroutine(client.Get(endpoint, parameter, callback));
}

それをもとに打刻を行います。

FreeeEventHandler.cs
        public void PostTimeClocks(string type, int employeeId)
        {
            var endpoint = "https://api.freee.co.jp/hr/api/v1/employees/" + employeeId + "/time_clocks";
            var p = TimeClockRequestJson(type, DateTime.Now);
            StartCoroutine(client.Post(endpoint, p, OnPostTimeClocks));
        }

        void OnPostTimeClocks(bool success, string response)
        {
            if (!success)
            {
                var msg = JsonUtility.FromJson<Message>(response).message;
                return;
            }

            var tc = JsonUtility.FromJson<PostTimeClocksResponse>(response).employee_time_clock;
            _postTimeClockSubject.OnNext((FreeeType)Enum.Parse(typeof(FreeeType), tc.type));
        }

OVRLipsyncでリップシンクする

Oculusが作成したOVRLipsyncを使用します。Vtuberとかがよく使ってる口パクできるやつです。
といっても使い方は、とても簡単で使用したいキャラクターにAudioSource, OVRLipSyncContext, OVRLipContextMorphTargetをアタッチするだけです。
BlendShapesがあるSkinned Mesh Renderが必要になります。

それぞれ
aa -> あ
E -> え
ih -> い
oh -> お
ou -> う
という感じに、紐ずいています

2020-07-15 (2).png

まとめ

中途半端な形になってしまい申し訳ないです。
原因を見つけ次第更新したいと思います。
作業中、Unityちゃんの「進捗どうですか?」がぐさぐさ刺さりました。

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

UnityでHDRPのプロジェクトを作り、フォトリアルなリアルタイムレンダリングを行う

HDRPとは

HDRPとは、High Definition Render Pipeline(高解像度レンダリングパイプライン)の略です。高品質のグラフィックスを実現するUnityの計算処理方法を指します。
PipeLineとは、3DCGの計算処理方法の1つで、3Dモデルのデータから2次元画像のイメージを作り出す多段階の過程全体と、それらの計算を高速化する手法を指します。

HDRPが使えるUnityのバージョン

HDRPはUnityの新しい技術のため、UnityはUnity 2019.3を強く推奨しています。
https://forpro.unity3d.jp/unity_pro_tips/2020/05/14/1221/

UnityはLTSのバージョンをダウンロードします。LTSとはLong-Term Support(長期サポート)のことを指しています。
UnityとHDRPのパッケージには互換性があため、UnityのバージョンにあったHDRPパッケージをインストールします。

前提

新規でプロジェクトを作成するケースを記載しています。既存のプロジェクトをHDRPに変換する方法は記載していません。

全体の流れ

  1. 新規シーンを作成
  2. 既存のポストプロセッシングを削除
  3. 色空間を切り替え
  4. HDRPパッケージをダウンロード
  5. High Definition Render Pipeline Assetを作成
  6. High Definition Render Pipeline Assetをプロジェクトに割当
  7. 3Dオブジェクトを取込
  8. マテリアルを取込
  9. テクスチャを取込
  10. ノーマルマップのテクスチャタイプを変更
  11. オブジェクトにテクスチャを割当
  12. シーンにオブジェクトを配置

詳細

新規プロジェクトを作成

Unity Hubを起動します。新規作成の右の三角ボタンを押して、2019.4のバージョンになっていることを確認し、新規作成ボタンを押します。
image.png

High Definition RPを選択し、作成ボタンを押します。
image.png

HD Render Pipeline Wizardのポップアップボックスが出てきた場合、Install Configuration Editable Packageを押して、新しいレンダリングパッケージをインストールします。インストールが終わったら、ポップアップボックスを閉じます。
image.png

新規シーンを作成

立ち上がるとデモシーンが開きます。デモシーンを使わず、新規シーンを作成するので、File -> New Sceneを選びます。
image.png

既存のポストプロセッシングを削除

HDRP には独自のポストプロセッシングがあるため、現在使用しているポストプロセッシングがあれば削除します。これを行うには、Window -> Package Manager -> Post Processingと入力して検索し、Removeをクリックします。
image.png

色空間を切り替え

Unity エディターでは、リニアワークフローとガンマワークフローの2つあります。リニア色空間を使用すると、ガンマ色空間よりも正確なレンダリングが可能です。これを行うには、Edit -> Project Settings -> Playerカテゴリで、Color Space を Linear にチェックを入れます。
image.png

HDRPパッケージをダウンロード

Window -> Package Manager -> Post ProcessingHigh Definition RPと入力して検索し、Installをクリックします。前段でインストール済みの場合は、Removeボタンを押せるようになっています。
image.png

3Dオブジェクトを取込

ProjectウィンドウのAssetフォルダ内で右クリックして、Create -> Folderを選択します。名称をObjectsに変更します。Objectsフォルダ内に移動し、右クリックして、Import New Assetsを選択します。fbxデータを取り込みます。

マテリアルを取込

取り込んだオブジェクトをクリックし、InspectorウィンドウのMaterialsタブのLocationをUse External Materials (Legacy)、NamingをFrom Model's Materialにして、Applyボタンをクリックします。自動でMaterialsフォルダが作成され、中にマテリアルが保存されます。
image.png

ProjectウィンドウのObjectsフォルダ内で右クリックして、Create -> Folderを選択します。名称をImagesに変更します。

テクスチャを取込

Imagesフォルダ内に移動し、右クリックして、テクスチャをドラッグ&ドロップします。

ノーマルマップのテクスチャタイプを変更

取り込んだノーマルマップをクリックし、InspetorウィンドウのTexture TypeをNormal Mapに変更し、Craete from Grayscaleをオフにし、Applyボタンをクリックします。複数を選択すれば、複数を一度にNormal Mapに変えることもできます。
image.png

オブジェクトにテクスチャを割当

Materialsフォルダの中のマテリアルをクリックし、InspectorウィンドウのBase Map、Metallic、Normal Mapの左の丸ボタンをクリックして、さきほど取り込んだ画像を割り当てます。HDRPでは、Metallic Map、AP Mapは使用しないようです(おそらく)。
image.png

シーンにオブジェクトを配置

Projectウィンドウのオブジェクトをシーンにドラッグ&ドロップして、シーンにオブジェクトを配置します。この状態だと、真っ黒になっています。
image.png

環境光の設定

空や影等の環境光の設定をします。

露出を設定

Unity HDRPではハイダイナミックレンジ(HDR)でデータが扱われているため、シーンではEXPOSURE(露出)のコントロールが必須の要素になります。HDRとは、RGB各8ビットの1677万色にとらわれず,幅広い表現域を用いてレンダリング工程を行う技法のことです。HDRを使うことで、黒つぶれしたり白飛びを抑え、明るい部分と暗い部分どちらの階調も犠牲にすることなく、よりリアルな描写が可能になります。
HierarchyウィンドウのSky and Fog Volumeをクリックして、InspectorウィンドウのAdd Override -> Exposureを選び、チェックを入れ、ModeをFixed、Fixed Exposureを8にします。

【Exposure値の目安】
晴天の屋外:14
明るい室内:9
街燈のある夜景:4
image.png
image.png

空からの環境光による拡散反射表現を設定

色の要素を持つはずの拡散反射成分がないため、全体的にグレーの見た目になっています。空からの環境光が拡散反射成分に影響するようライティングの設定していきます。空からの拡散反射光表現にはBake(各3Dオブジェクトの表面のテクスチャに光や影を焼き付けること)を使用します。

3DオブジェクトをStaticに変更

3DオブジェクトごとにStaticかDynamicのいずれかに分けます。地形、建物、固定の家具などの動かない形状はStatic(静的)とし、草、木、動くキャラクタなどの動く形状はDynamic(動的)とします。Staticの方が綺麗な影の表現ができますが、処理時間がかかります。
StaticにはBake処理を行い、DynamicのオブジェクトにはBake処理せずリアルタイムに光や影の計算処理を行うことで、処理速度とフォトリアル表現を実現しています。
Hierarchyウィンドウで3Dオブジェクトをクリックし、InspetorウィンドウのStaticにチェックを入れます。
image.png

Lighting Setting画面の表示

メインメニューのWindow -> Rendering -> Lighting -> Settingsを選択し、Lightingポップアップウィンドウを表示します。LightingポップアップウィンドウをInspetorタブの横にドラッグ&ドロップします。

3DオブジェクトのStatic Lightning SkyをHDRISkyに変更

image.png

Bakeの各種設定を変更してBake処理

Hierarchyウィンドウの3Dオブジェクトをクリックし、LightingウィンドウでBakeの各種設定を変更し、Generate Lightinigをクリックします。
Baked Global Illumination:ON
Lighting Mode:ShadowMask (遮蔽をベイク)
Lightmapper:ProgressiveGPU (ProgressiveCPUと比べ高速)
Multiple Importance Sample:ON (ノイズ軽減)
Filtering:Auto (ノイズ軽減)
Lightmapsize:32 (大きいと品質が上がるが計算時間が増加)
Lightmap Size:2048
Lightmap Parameters:Defoult-HighResolution
image.png

Generate Lightingでベイクすると静的オブジェクトに空の環境光が反映されて、物体の色が見えてきました。これで、静的オブジェクトの鏡面反射、拡散反射のそれぞれの成分に空からの環境光が反映された状態となりました。
image.png

直接光を設定

太陽からの直接光の反射表現を設定します。

太陽光の照度を設定

DirectionalLightは太陽光を想定し、Intensityの単位はLuxを使用します。
HierarchyウィンドウのDirectional Lightをクリックし、InspectotウィンドウのEmissionのTemperature、Intensityに値を入力します。
Luxは面積に対して照らされた照度で、その場所の明るさを表します。太陽光のような広範囲の場所に対して全体的に照らすライトには、分かり易いLuxを用います。色温度は正午の太陽光の5,000K近辺に設定します。

晴天の日の日向:100,000Lux 5,000~6500K
晴天の日の野外の日陰:10,000Lux
晴天の日の屋内窓際(北):1,000~2,000Lux
晴天の日の屋内中央:100~200Lux
薄曇:50,000Lux 6,500K
暗い雨天:10,000~15,000Lux
日没:500Lux 2,000~3,000K
満月:0.2Lux 4,000K
晴天、月のない夜:0.0003Lux
image.png

影を設定

HierarchyウィンドウのDirectional Lightをクリックし、InspectotウィンドウのShadowのEnableにチェックを入れ、Resolutionに2048と入力します。これで影の解像度が細かくなりました。
image.png

さらに影の解像度を上げます。HierarchyウィンドウのSky and Fog Volumeをクリックし、InspectotウィンドウのAdd Overrideをクリックし、Shadowsで検索し選択します。
Cascade Count、Split 1,2,3にチェックを入れ、値を入力します。
Cascade CountはShadowマップが持つ枚数の事で数が多い方が、よりシーン内の影の解像度が高くなり距離に合わせて細かなコントロールが可能になります。数が多いとよりメモリ容量を圧迫します。
Splitは、各Shadow Mapの使用する範囲を設定します。数値はそれぞれのSplitの適用距離となっていて数値を下げると手前から適用される範囲が狭まり解像度は上がりますが、奥の解像度が荒くなります。
image.png

間接光表現を設定

直接光があたり、物体に反射した光の表現が入っていないので、とても単調で暗いシーンになっています。反射光を表現するGlobal Illuminationの効果を加えます。
Global Illuminationとは、物体に跳ね返った光が相互反射し伝播していくことを表現するレンダリング手法で、省略してをGIと呼ばれる事もあります。

GIを加えるためLighting SettingでLight mapを再度Bakeします。Bake後、反射光が反映され画面全体が明るくなります。

image.png

参考記事
Unity Pro Tips _ HD レンダーパイプライン(HDRP)を使ったゲーム制作
KlabGames _ Unity HDRPでのハイエンドグラフィック制作アプローチ
積木製作所 _ Unity Japan Office メイキング4 ~テクスチャ&マテリアル~
のっぴの備忘録 _ HDRPを使ってみる
Unity Blog _ HD レンダーパイプライン:アーティストのためのクイックスタートガイド

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

Unity ML-Agentsで強化学習を行う「環境構築」「サンプル実行」

0.前提

この記事はUnityを触ったことがあり、強化学習を行ってみたいという人向けになります。(自分用の備忘録でもある)
間違いなどありましたら教えて頂けるとありがたいです。

1.実行環境

Unity 2019.3.0f6
Anaconda 2019.03 for Windows 64bit Python3.7
ml-agents-release_3

2.環境構築

Unityのダウンロード

https://unity3d.com/get-unity/download/archive
上記URLから好きなバージョンのUnityをダウンロードします。

ML-Agentsのダウンロード

https://github.com/Unity-Technologies/ml-agents/tree/release_3
ここからzipファイルでダウンロード、またはgitでクローンします。

Anacondaのダウンロード

https://www.anaconda.com/products/individual
Windows 64bit Python3.7をダウンロードします。

Anaconda Promptを起動します。

環境作成
conda create -n [名前] Python=3.7

環境を作成します。
[名前]は各自好きな名前をつけて構いません。

環境切り替え
conda activate [名前]

先ほど作成した環境に切り替えます。
[名前]は先ほどつけた名前になります。
スクリーンショット (17).png

先頭の()内が現在の環境の名前になっていて、環境が切り替わっているのがわかると思います。

フォルダ切り替え
cd [ダウンロードしたML-Agentsのフォルダまでのパス]

先ほどダウンロード(クローン)したフォルダに移動します。

ML-Agents用の環境インストール
pip install mlagents

ML-Agents用の環境をインストールします。
少し時間がかかるので気長に待ちましょう
学習時にもターミナルは使用するので、閉じずに残しておいて下さい。

以上で、環境の準備は完了です。

3.サンプル実行

ここでは、サンプルを使って実際に強化学習を行います。
今回は「3DBall」を用いて説明していきます。

学習の開始

mlagents-learn config/ppo/3DBall.yaml --run-id=[名前]

他のサンプルでやるときは3DBallのところは選んだサンプルの名前に変えてください。
[名前]に関しては自由です。保存される時のフォルダ名にもなるので、各自わかりやすくしてください。
スクリーンショット (14).png
Unityマークとこのような文が出てきたらシーンを実行して下さい。
スクリーンショット (24).png

規定のステップ数が終わると学習が完了します。
完了すると上のようなログが流れます。
※途中でやめたい時はCTRL+Cで途中で切りあげることもできます。

モデルを実行

学習が完了したモデルはresultsフォルダの中に実行時に決めた名前フォルダごとに入ります。
フォルダの中に入っているNNファイルをプロジェクト内にコピーしてください。
スクリーンショット (24).png
持ってきたモデルをBehavior Parametersの中のModelに貼ってください。
これで、モデルを切り替えることができました。

では、実際にシーンを実行してみましょう。
学習ができていることがわかると思います。

4.おわりに

今回は、詳しい説明は省いて、実際に学習を行うまでのやり方を説明しました。
次は、もう少し詳しいところまで記事にする予定です。

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

【Unity】Virtual Cameraの視点操作をスクリプトで行う 【Cinemachine】

alt
この記事はユニティちゃんライセンス条項の元に提供されています

ユニティちゃん1.2.1
Unity2019.3.1

やりたいこと

ゲームパッドの左アナログスティックでキャラクターを動かし、右アナログスティックでカメラの視点(映し出す方向)を操作したい(アクションゲームとかでありがちなやつ)。
movie_Trim_1 (1).gif

大まかな流れ

追従:Virtual CameraのFollowとLookAtに対象のオブジェクトをアタッチすれば勝手に追従してくれる。
視点の操作:Virtual CameraのBiasとFollow Offsetの値をスクリプトで変化させることで実現。
※視点を操作することによってプレイヤーが意識する座標系とワールド座標系が異なるため補正をしなければならない。

Virtual Cameraの設定

1.Virtual CameraをHierarchyに追加

Window>Package ManagerからCinemachineをインポートした後、Cinemachine>Create Virtual Cameraを選択。

2.Virtual Cameraがキャラクターを追従するように設定

Follow(追いかける対象)とLookAt(注視する対象)にオブジェクトをアタッチ。
image.png

3.Bodyを設定

image.png
Input Axis Nameを空にすることでマウスで勝手に視点操作されないようにする。

4.視点の角度などを良い感じに設定

image.png

5.BiasとFollow Offset

BodyのBiasとFollow Offsetをいじるとカメラの視点がキャラクターを中心に変化していることが分かります。
[Bias]
bandicam-2020-07-15-00-41-22-967_Trim.gif
[Follow Offset]
bandicam-2020-07-15-00-46-59-687_Trim.gif

このBiasとFollow Offsetをスクリプトから操作します。

スクリプトからVirtual Cameraを操作して視点を回転させる

using Cinemachine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using UnityEngine;

[RequireComponent(typeof(CharacterController))]

public class test : MonoBehaviour
{
    [SerializeField] private Animator _animator;
    private float moveSpeed = 7f;

    private CharacterController _characterController;
    [SerializeField] private CinemachineVirtualCamera _camera; //エディタでVirtual Cameraをアタッチ
    private CinemachineOrbitalTransposer _transposer;
    private Transform _transform;

    private Vector3 _moveVelocity;
    private Vector3 world_moveVelocity;
    private Vector3 _cameraRotation;

    void Start()
    {
        _characterController = GetComponent<CharacterController>();
        _transposer = _camera.GetCinemachineComponent<CinemachineOrbitalTransposer>();
        _transform = transform;

        _moveVelocity = Vector3.zero;
        world_moveVelocity = Vector3.zero;
        _cameraRotation = Vector3.zero;
    }

    void Update()
    {
        world_moveVelocity = Vector3.zero;
        _moveVelocity = Vector3.zero;

        _cameraRotation.x = Input.GetAxisRaw("Horizontal2");
        _cameraRotation.z = Input.GetAxisRaw("Vertical2");

        if (_cameraRotation.magnitude >= 0.1)
        {
            _transposer.m_Heading.m_Bias += _cameraRotation.x * 3f; //Biasを操作
            _transposer.m_FollowOffset.y -= _cameraRotation.z / 8f; //Follow Offsetを操作

        }

        _moveVelocity.x = Input.GetAxisRaw("Horizontal1") * moveSpeed;
        _moveVelocity.z = Input.GetAxisRaw("Vertical1") * moveSpeed;

        if (_moveVelocity.magnitude >= 0.01)
        {
            //座標系の補正
            world_moveVelocity = Quaternion.AngleAxis(_transposer.m_Heading.m_Bias, Vector3.up) * _moveVelocity;

            Vector3 targetPositon = _transform.position + world_moveVelocity;
            //向かせたい方向
            Quaternion targetRotation = Quaternion.LookRotation(targetPositon - _transform.position);

            _transform.rotation = Quaternion.Slerp(_transform.rotation, targetRotation, 0.2f);
        }

        _characterController.Move(world_moveVelocity * Time.deltaTime);

        _animator.SetFloat("Speed", new Vector3(_moveVelocity.x, 0, _moveVelocity.z).magnitude);

    }
}

Horizontal1とVertical1は左アナログスティック(移動)の軸で、Horizontal2とVertical2は右アナログスティック(視点)の軸。

1.BiasとFollow Offset

BiasとFollow Offsetは以下のように操作できます。

[SerializeField] private CinemachineVirtualCamera _camera;
_transposer = _camera.GetCinemachineComponent<CinemachineOrbitalTransposer>();
_transposer.m_Heading.m_Bias += _cameraRotation.x * 3f; //Biasを操作
_transposer.m_FollowOffset.y -= _cameraRotation.z / 8f; //Follow Offsetを操作

2.座標系の補正

Biasを操作して水平方向に視点を回転させると、プレイヤーが意識する座標系とワールド座標系にBias分の角度の差が生まれます。
image.png
プレイヤーが右移動を入力したとき、想定する挙動は赤色の矢印方向への移動なのに、
右上のワールド座標系のx軸(赤)方向への移動になってしまう。

移動入力ベクトルをy軸まわりにBias分だけ回転させることで解決できる。

//座標系の補正
world_moveVelocity = Quaternion.AngleAxis(_transposer.m_Heading.m_Bias, Vector3.up) * _moveVelocity;

参考リンク

Cinemachine公式リファレンス

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