- 投稿日:2019-10-21T19:14:48+09:00
Sharematerialの意味(URL参照)
sharematerialとは
同じマテリアルを使用しているものの設定を同時に変えることができる。
lineObj.AddComponent<MeshRenderer>().sharedMaterial = _parent._material;
こんな感じの使い方同じマテリアルって言ってるけど、これだと左のマテリアルが共通化されてるのかな
正直詳細がわからないので下のリンクを参考にしてみる
マテリアルのプロパティをスクリプトから変更【Unity】
- 投稿日:2019-10-21T19:02:37+09:00
【Unity】Noitom Hi5とPerception Neuron PROを使った全身トラッキング環境の構築(+モーション記録)
最初に
Hi5とPerception Neuron PROはVTuberハッカソン 全国ツアー2019に参加した際に借りた機材になります。
個人で所有していないため、本機材に関する質問には、返答できない場合があります。
ご了承ください。本記事の目標
Unity上でVRM形式のモデルをNeuron、Hi5で動かし、そのモーションを記録する
環境
【PC】Windows10 1903
【Unity】2019.2f1トラッカー
SDK
- Noitom Perception Neuron PRO
ダウンロードページお借りしたモデル
- 折岸みつ
モデルページお借りしたスクリプト
モデルインポート
- UniVRM
ソースコードモーショントラッキング関係
モーション記録関係
- EasyMotionRecorder
ソースコード設定
Noitom Perception Neuron PRO SDK
ダウンロードページからAXIS NEURON PROをダウンロード、インストールし起動する
[File]->[Settings]->[Output Format]のBVH Dataの項目のDisplacementの項目のチェックを外す
OKで設定を反映
Unity
モデルインポート
UniVRMリリースページから最新版の.unitypackageをダウンロード
UnityのプロジェクトにUnity Packageをインポート
VRM形式のモデルデータをAssetフォルダ直下にコピペする
UniVRMがUnity用にプレハブを作成する
Noitom Perception Neuron PRO
Perception Neuron PRO、Hi5設定ツールをダウンロードし、解凍する
解凍後のフォルダのAssetフォルダから、
・Neuronフォルダ
・NotionHi5フォルダ
・calibration.unity
をUnityプロジェクトのAsset直下にコピペする
動かしたいモデルにNeuronAnimator.csをアタッチする
設定完了
気になる人はUnityを再生して、AXIS NEURON PRO上の動きがモデルに反映されているか確認するNoitom Hi5
上記Noitom Perception Neuron PROの項目1.、2.を行っていない人は行う
モデルの右手首、左手首にHi5_InertiaInstance.csをアタッチ
それぞれを以下の画像のように設定する
設定が面倒な方はHi5_InertiaInstance.csの設定が面倒な人向けの項目を確認してください
左手の設定サンプル(右手はそれぞれを右手のモデルに読み替えてください)
Hi5_InertiaInstance.csの設定が面倒な人向け
自動設定機能を追加します。
- Hi5_InertiaInstance.csにhttps://gist.github.com/neon-izm/395709df5af70021490625e4c03e59bdより引用した以下のコードを加える
/// <summary> /// モデルを変えたときに HandBone をセットし直すのが面倒だったため、自動的にアタッチしてくれる関数 /// HandBones[0] に対象モデルのRoot(Animatorがアタッチされてるオブジェクト)を入れてから実行する。 /// </summary> [ContextMenu("Automatic Set HandBone")] void AutomaticSetHandBone() { if (HandBones[0] == null) { Debug.LogError("HandBones[0] にモデルのRoot(Animatorとかあるオブジェクト)を入れてください。"); return; } //Animator からボーン情報を持ってきたいので、Animator を取得 var animator = HandBones[0].GetComponent<Animator>(); if (animator == null) { Debug.LogError("Animator が見つかりません。"); return; } //念の為初期化 HandBones = new Transform[(int) Bones.NumOfHI5Bones]; //左手と右手で取得すべきボーンが違うから判定 switch (HandType) { case Hand.LEFT: HandBones[1] = animator.GetBoneTransform(HumanBodyBones.LeftHand); HandBones[2] = animator.GetBoneTransform(HumanBodyBones.LeftThumbProximal); HandBones[3] = animator.GetBoneTransform(HumanBodyBones.LeftThumbIntermediate); HandBones[4] = animator.GetBoneTransform(HumanBodyBones.LeftThumbDistal); HandBones[6] = animator.GetBoneTransform(HumanBodyBones.LeftIndexProximal); HandBones[7] = animator.GetBoneTransform(HumanBodyBones.LeftIndexIntermediate); HandBones[8] = animator.GetBoneTransform(HumanBodyBones.LeftIndexDistal); HandBones[10] = animator.GetBoneTransform(HumanBodyBones.LeftMiddleProximal); HandBones[11] = animator.GetBoneTransform(HumanBodyBones.LeftMiddleIntermediate); HandBones[12] = animator.GetBoneTransform(HumanBodyBones.LeftMiddleDistal); HandBones[14] = animator.GetBoneTransform(HumanBodyBones.LeftRingProximal); HandBones[15] = animator.GetBoneTransform(HumanBodyBones.LeftRingIntermediate); HandBones[16] = animator.GetBoneTransform(HumanBodyBones.LeftRingDistal); HandBones[18] = animator.GetBoneTransform(HumanBodyBones.LeftLittleProximal); HandBones[19] = animator.GetBoneTransform(HumanBodyBones.LeftLittleIntermediate); HandBones[20] = animator.GetBoneTransform(HumanBodyBones.LeftLittleDistal); break; case Hand.RIGHT: HandBones[1] = animator.GetBoneTransform(HumanBodyBones.RightHand); HandBones[2] = animator.GetBoneTransform(HumanBodyBones.RightThumbProximal); HandBones[3] = animator.GetBoneTransform(HumanBodyBones.RightThumbIntermediate); HandBones[4] = animator.GetBoneTransform(HumanBodyBones.RightThumbDistal); HandBones[6] = animator.GetBoneTransform(HumanBodyBones.RightIndexProximal); HandBones[7] = animator.GetBoneTransform(HumanBodyBones.RightIndexIntermediate); HandBones[8] = animator.GetBoneTransform(HumanBodyBones.RightIndexDistal); HandBones[10] = animator.GetBoneTransform(HumanBodyBones.RightMiddleProximal); HandBones[11] = animator.GetBoneTransform(HumanBodyBones.RightMiddleIntermediate); HandBones[12] = animator.GetBoneTransform(HumanBodyBones.RightMiddleDistal); HandBones[14] = animator.GetBoneTransform(HumanBodyBones.RightRingProximal); HandBones[15] = animator.GetBoneTransform(HumanBodyBones.RightRingIntermediate); HandBones[16] = animator.GetBoneTransform(HumanBodyBones.RightRingDistal); HandBones[18] = animator.GetBoneTransform(HumanBodyBones.RightLittleProximal); HandBones[19] = animator.GetBoneTransform(HumanBodyBones.RightLittleIntermediate); HandBones[20] = animator.GetBoneTransform(HumanBodyBones.RightLittleDistal); break; default: Debug.LogError("HandType が不正なものです。"); break; } }
下図の通り、
・Hand Baseの要素0にVRMのアニメーターがついているオブジェクトを設定
・自動設定を実行
・追加の設定
を行う
Asset直下のcalibration.unityを再生し、Hi5のキャリブレーションを行う(スペースキーで画面遷移します)
詳細な手順は以下の動画をご確認ください
https://youtu.be/FN1wvcpdOjk?t=157レコーディング環境
1.EasyMotionRecorderリリースページから最新版のunitypackageをダウンロード、プロジェクトにインポート
2.Asset -> EasyMotionRecorder -> Prefubs -> EasyMotionRecorderをシーンにドラッグアンドドロップ
3.オブジェクトEasyMotionRecorderのMotionDataRecoder.csのアニメーターにモデルをドラッグアンドドロップ
動作
上記設定の項目を行った状態で再生すると、Hi5とPerception Neuron PROを着用した人のモーションがモデルに反映されます。
デモ用に取ったモーションを再生したもの
レコーディング
記録
Unityを再生中にEasyMotionRecorderのMotionDataRecorder.csに設定したキーで録画開始、終了ができます。
記録されたモーションはAsset -> Resourcesに記録されます。アニメーションに変換
そのままでは、アニメーターで再生できないので、アニメーションに変換します。
Asset -> Resourcesに記録された.assetファイルを選択し、下図の通りHumanoid Animation Clipとしてエクスポートします。
あとは、アニメーションコントローラーに張り付けて使用してください。参考文献
Noitom Perception Neuron PROホームページ
https://www.aiuto-jp.co.jp/products/product_2459.php
Neuron PROの着方やSDKのダウンロードでお世話になりました유니티 라이브 모션 캡쳐 with 하이파이브 글러브 & 뉴런 프로
https://www.youtube.com/watch?v=FN1wvcpdOjk
Unity側のNeuron PRO、Hi5の設定、ツールのダウンロードでお世話になりましたizm_11's blog
Noitom Hi5をVTuber案件で投入したのでレビューしつつ便利スクリプトを書いた
http://izm-11.hatenablog.com/entry/2018/10/10/184502
Hi5の自動設定のコードをお借りしました。EasyMotionRecorder
http://github.com/duo-inc/EasyMotionRecorder
モーションの記録でお世話になりました。UniVRM
https://github.com/vrm-c/UniVRM
VRMモデルのインポートでお世話になりました。ニコニ立体 【モデル配布】折岸みつ【オリキャラ】
https://3d.nicovideo.jp/works/td35076
モデルをお借りしました
- 投稿日:2019-10-21T18:42:45+09:00
Unity メッセージのないエラーログ
エラーメッセージのないエラー発生
先日から突然Unityで意味不明なエラーログが発生しだしかなり苦労させられたので、備忘録に残しておきます。
環境
Unity2018.4.2f
Mac 10.14.5現象
Unityを触っていたらこのような意味不明なエラーログが4つほど発生。
試したこと
- 他のプロジェクトでも立ち上げると同じエラーが発生
- 新規プロジェクト作成するも同じエラー発生
どうやら、Unity自体に問題がありそう。。
ここの記事を参考にUnityをクリアインストールを試すが解決せず。。
発見その1
昔使っていた
Unity2018.2.5
では謎のエラーは発生せず正常運転!Unityのバージンで問題があるかもしれないと推測し、バージョンごとにテストした結果
Unity2018.2.xx以下ではOK
Unity2018.3.xx以上ではNGUnityのバージョンで問題があるのならば、もっと大きな問題になっているはずなので、根本的にはバージョンの問題ではない気がする。
発見その2
Bootcampで入れたWindows10側でUnity2018.4.11をインストールして試してみると、正常運転!
やはり、Unityのバージョンが問題ではなく、MacOSとUnityの組み合わせで問題が出ている気がする。
解決
結局、MacOSのバージョンをMojave(10.14.5)から最新のCatalina(10.15)へアップデートすることで謎のエラーは出なくなりました。
根本的な解決ではないような気もしますが、とりあえず結果オーライということで。あとがき
アップデートしてソフトウェアが動かなくなることは多々ありますが、アップデートしないことで動かないという現象は初めての経験でした。
OSのアップデートは、不具合も多いのでいつもは1ヶ月ほど様子見てからにするのですが、今回のようなこともあるので、また一つ勉強になりました。
ちなみに、最近調子悪かった
Adobe Creative Cloud
もアップデート後は正常運転するようになってくれて、いろいろと問題が解決できました!!
- 投稿日:2019-10-21T18:18:06+09:00
【Unity(C#)】Emptyなオブジェクトを可視化してEdit簡略化
Transformだけのオブジェクト
Create EmptyしたObjcetって文字通りメッシュも何もないので、
位置調整が必要なオブジェクトとして設定してしまった場合、微調整がけっこう大変ですよね。AudioSourceだけのオブジェクトで音源の位置を変化させた場合なんかも、
視覚情報だけでデバッグしたいところです。そこで、ギズモを描画する機能を使って編集、デバッグをしやすくします。
もうすでにきれいにまとめてくださっている方がいらっしゃったので、らくちんでした。
UnityのGizmosを使ってシーンビューで視覚的なデバッグの補助をするコード
#if UNITY_EDITOR using UnityEditor; using UnityEngine; /// <summary> /// ギズモちゃん /// </summary> public class DrowGizmo : MonoBehaviour { [SerializeField] enum MODE { CUBE, WIRECUBE, SPHERE, WIRESPHERE } [SerializeField,Header("描画モード選択")] MODE m_mode; [SerializeField, Range(0,5),Header("ギズモの大きさ")] float m_gizmoSize = 0.3f; [SerializeField, Header("ギズモの色")] Color m_gizmoColor = new Color(1f, 0, 0, 0.3f); void OnDrawGizmos() { Gizmos.color = m_gizmoColor; Vector3 thisObjPos = this.gameObject.transform.position; switch (m_mode) { case MODE.CUBE: Gizmos.DrawCube(thisObjPos,Vector3.one*m_gizmoSize); break; case MODE.SPHERE: Gizmos.DrawSphere(thisObjPos, m_gizmoSize); break; case MODE.WIRECUBE: Gizmos.DrawWireCube(thisObjPos, Vector3.one * m_gizmoSize); break; case MODE.WIRESPHERE: Gizmos.DrawWireSphere(thisObjPos, m_gizmoSize); break; } } [CustomEditor(typeof(DrowGizmo))] public class CustomWindow : Editor { public override void OnInspectorGUI() { EditorGUILayout.BeginVertical(GUI.skin.box); { EditorGUILayout.HelpBox("ギズモでEditサポート", MessageType.Info); } EditorGUILayout.EndVertical(); base.OnInspectorGUI(); } } } #endifOnDrawGizmos
OnDrawGizmos
内に書いた処理はオブジェクトを選択していなくても表示されます。
基本こちらしか使わないかな~?と思います。
- 投稿日:2019-10-21T08:04:21+09:00
ハードエッジでも縁崩れしない理想的なアウトラインシェーダを作ってみた
はじめに
仕事でUnityゲーム開発しているyoship1639です。
ハードエッジでもこの様に縁崩れせずにアウトラインを奇麗に出すシェーダを思いついたので公開します。縁取りをするシェーダは多々ありますが、特定のパターンで上手くいかない場合があります。
それぞれ代表的な既存のアウトラインシェーダの特徴と、上手くいかないパターンは以下の通りです。
モデルを拡大する手法
特徴:一般的な手法。1パス目で拡大したモデルをアウトライン色で描画し、2パス目で通常描画する
ダメなパターン:ハードエッジモデルの場合やモデルの中心(各頂点の中心)が0じゃない場合に破綻するステンシルバッファを使う手法
特徴:これも一般的な手法。1パス目で拡大したモデルをステンシルバッファに描画し、2パス目で通常描画する。ポストプロセス等でアウトラインに色を付ける
ダメなパターン:モデルを拡大する手法と同様法線を使う手法
特徴:カメラ視点から見てモデルの法線がほぼ垂直になる(内積がほぼ0になる)ピクセルをアウトライン色にする手法
ダメなパターン:アウトラインが超絶汚い深度バッファを使う手法
特徴:深度バッファを使って深度が急変する箇所をアウトラインとみなす手法。
ダメなパターン:シーン全体にアウトラインがかかってしまう各手法の細かい説明等はこちらを参考にしてください
【Unity】【シェーダ】4種のアウトライン描画方法とその特徴と、上記の様に一長一短があります。
理想的なのは、特定のハードエッジなモデルでもきれいなアウトラインを表示することです。
そこで、上記のダメなパターンを払拭したアウトラインシェーダを思いついたので解説したいと思います。アルゴリズム解説
今回思いついたのは、GrabPassを用いたアウトラインシェーダです。
GrabPassは簡単に説明すると、直前までのパスの描画結果を背景含めてテクスチャとして出力することです。Grabテクスチャはその描画結果のテクスチャを指します。アルゴリズム概要は以下の通りです。
- 【1パス目】モデルをシーン上で絶対使わないような色(ダミー色)でソリッド描画する
- 1の結果をGrabPassとして2パス目に渡す
- 【2パス目】ピクセルシェーダでGrabテクスチャのUV値付近をアウトライン幅で一定数サンプリングする
- サンプリングの結果ダミー色が含まれていなかった場合、アウトライン付近と判断しアウトライン色で描画する。それ以外は通常描画する。
上記のアルゴリズムでモデルを描画すると、ハードエッジであってもきれいにアウトラインを描画することができます。
それぞれ説明します。
1. 【1パス目】でモデルをシーン上で絶対使わないような色(ダミー色)でソリッド描画する
まず、1パス目でアウトライン描画したいモデルをシーン上で絶対使わないような色でモデルを膨らませずに塗りつぶし描画します。なぜシーン上で絶対使わないような色でないといけないかというと、Grabテクスチャの背景と被る色だとアウトラインが破綻する可能性があるからです。この色をダミー色と呼んでおきます。アウトライン色ではありませんのでご注意。
使いやすいダミー色は
rgb(255,0,255)
なので、この色を使います。Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { half4 vertex : POSITION; }; struct v2f { half4 vertex : SV_POSITION; }; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i) : SV_Target { return fixed4(1.0, 0.0, 1.0, 0); } ENDCG }2. 1の結果をGrabPassとして2パス目に渡す
これは特に何も考えずにシェーダコード内で
GrabPass {}
と書けば大丈夫です。これで1パス目の結果をテクスチャとして2パス目に渡すことができます。3. 【2パス目】ピクセルシェーダでGrabテクスチャのUV値付近をアウトライン幅で一定数サンプリングする
ここが今回の手法のミソです。
頂点シェーダはいつもの感じに行いますが、ピクセルシェーダは少し違います。まず、GrabPassで入手したGrabテクスチャのピクセルのUV値を算出します。これは頂点シェーダで
ComputeGrabScreenPos
を呼べばUnityが勝手に算出してくれます。このUV値のサンプリング結果はモデルのピクセル値と一致します。(つまりrgb(255,0,255)
です)UV値を算出したら、そのUV値の付近をアウトライン幅分ずらして一定数サンプリングします。一定数というのはアウトラインと判定するのに十分な数です。今回の手法は6方向分サンプリングすれば十分です。
#define SAMPLE_NUM 6 #define SAMPLE_INV 0.16666666 #define PI2 6.2831852 #define EPSILON 0.001 #define DUMMY_COLOR fixed3(1.0, 0.0, 1.0) sampler2D _GrabTexture; half _OutlineWidth; v2f vert(appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.grabPos = ComputeGrabScreenPos(o.pos); return o; } fixed4 frag(v2f i) : SV_Target { // サンプリングのオフセット(アウトラインの幅) half2 delta = (1 / _ScreenParams.xy) * _OutlineWidth; int edge = 0; [unroll] for (int j = 0; j < SAMPLE_NUM && edge == 0; j++) { // オフセット分ずらしてサンプリング fixed4 tex = tex2D(_GrabTexture, i.grabPos.xy / i.grabPos.w + half2(sin(SAMPLE_INV * j * PI2) * delta.x, cos(SAMPLE_INV * j * PI2) * delta.y)); // ダミー色と同でないならアウトラインであると判定 edge += distance(tex.rgb, DUMMY_COLOR) < EPSILON ? 0 : 1; } ...4. サンプリングの結果ダミー色が含まれていなかった場合、アウトライン付近と判断しアウトライン色で描画する。それ以外は通常描画する。
サンプリングした結果、ダミー色しか含まれていない場合はアウトライン付近ではないと判断し通常のモデル描画を行います。
ダミー色以外が1つ以上含まれていた場合は、アウトライン付近であると判断できるので、アウトライン色で塗りつぶします。
これを実現すると、ハードエッジであっても関係なく、かつ特定のモデルのみアウトラインを適用させることができます。シェーダコード
今回作成したアウトラインシェーダのシェーダコード全文です。コピペすれば動きます。
必要最小限のコードしか書いていないので、ライト処理やシャドー処理などは行いません。Shader "Custom/Outline" { Properties { _MainColor("Main Color", Color) = (1, 1, 1, 1) _MainTex("Texture", 2D) = "white" {} _OutlineColor("Outline Color", Color) = (0.0, 0.0, 0.0, 1) [Slider(0.1)] _OutlineWidth("Outline Width", Range(0.0, 10.0)) = 3 } SubShader { Tags { "RenderType" = "Opaque" } LOD 100 // 【1パス目】ダミー色で塗りつぶし Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { half4 vertex : POSITION; }; struct v2f { half4 vertex : SV_POSITION; }; v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag(v2f i) : SV_Target { return fixed4(1.0, 0.0, 1.0, 0); } ENDCG } GrabPass {} // 【2パス目】Grabテクスチャを使ってアウトライン+通常描画 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #define SAMPLE_NUM 6 #define SAMPLE_INV 0.16666666 #define PI2 6.2831852 #define EPSILON 0.001 #define DUMMY_COLOR fixed3(1.0, 0.0, 1.0) struct appdata { half4 vertex : POSITION; half2 uv : TEXCOORD0; }; struct v2f { half4 pos : SV_POSITION; half2 uv : TEXCOORD0; half4 grabPos : TEXCOORD1; }; sampler2D _GrabTexture; fixed4 _MainColor; sampler2D _MainTex; half4 _MainTex_ST; fixed4 _OutlineColor; half _OutlineWidth; v2f vert(appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.grabPos = ComputeGrabScreenPos(o.pos); return o; } fixed4 frag(v2f i) : SV_Target { half2 delta = (1 / _ScreenParams.xy) * _OutlineWidth; int edge = 0; [unroll] for (int j = 0; j < SAMPLE_NUM && edge == 0; j++) { fixed4 tex = tex2D(_GrabTexture, i.grabPos.xy / i.grabPos.w + half2(sin(SAMPLE_INV * j * PI2) * delta.x, cos(SAMPLE_INV * j * PI2) * delta.y)); edge += distance(tex.rgb, DUMMY_COLOR) < EPSILON ? 0 : 1; } fixed4 col = lerp(tex2D(_MainTex, i.uv) * _MainColor, _OutlineColor, edge); return col; } ENDCG } } }おわりに
上記のシェーダコードはアウトラインを表示するための必要最小限の機能しか実装していません。しかし、アウトライン部分以外はいくらでも追加実装することができるので汎用性は高いのではないかと思います。
また、良い感じに描画してくれるアウトラインシェーダですが弱点があります。それはアウトラインシェーダを適用させたモデルを重ねた時です。この場合、GrabPassがモデルと背景の境界を正しく判定できなくなるので、アウトラインが多少破綻してしまいます。そのため、ダミー色をマテリアルごとに変えたりといった工夫が必要になるかもしれません。それ以外の場面では現状問題なく描画できます。
その他不具合があったらご連絡ください。改良版を考えます。
良きUnityライフを。