MRが楽しい

MRやVRについて学習したことを書き残す

アンダーワールドの仕組みを探る その2(パーティクルシステム)

再び前回記事の続きとなります。
アンダーワールドの仕組みを探る その1(黒色の透過)
 http://bluebirdofoz.hatenablog.com/entry/2017/04/20/003204

今回はアンダーワールド出現時に発生する爆発エフェクトについて確認してみます。
MRとは直接関係ない技術ですが、表現方法の一つとして是非覚えておきたい事柄です。

ボールと折り紙が接触すると、こんな感じの爆発が発生します。
f:id:bluebirdofoz:20170421020407j:plain
一瞬なので撮影が難しい…。


まずはエフェクトの発生源について探ってみます。
f:id:bluebirdofoz:20170421020450j:plain
このExplosion_tmpというのが本体のようです。
一つのゲームオブジェクトかと思っていましたが、中々に複雑な構成をしています。

まずはExplosion_tmpにあるオブジェクトを読み解いてみました。
◇Explosion_tmp : 爆発エフェクトを作成
├◇StreamParents : 破片エフェクトを作成
│└◇SubEmitter : 破片エフェクトのサブエミッター
├◇SubEmitter : 爆発エフェクトのサブエミッター
└◇sparks : 火花のエフェクトを作成

ここで出てくるサブエミッターという用語ですが、Unityの下記機能を利用したエフェクトです。
・Sub Emitters モジュール
 https://docs.unity3d.com/ja/540/Manual/PartSysSubEmitModule.html

大元のエフェクトが発生した際に連動して発生させられる子エフェクトとも呼べる機能のようです。
Explosion_tmp直下のSubEmitterは爆発に光効果を、StreamParentsのSubEmitterは破片に煙効果を追加していました。

パーティクルシステムについて調べてみます。
・メインモジュール
 https://docs.unity3d.com/ja/540/Manual/PartSysMainModule.html
"ParticleSystem"のコンポーネントは非常に万能で、爆発エフェクトも、火花エフェクトも、煙エフェクトまでこのコンポーネントで実現しているようです。

例えば、爆発エフェクトを確認してみました。
マテリアルにシェーダが設定されており、そのテクスチャでアニメーションを定義していることが見て取れます。
f:id:bluebirdofoz:20170421020748j:plain

流石にここまで複雑なエフェクトはまだ作れないので、試しに一つ簡単なエフェクトを追加して動作を確認してみます。
GameObject->ParticleSystemで"ParticleSystem"がアタッチされたゲームオブジェクトを作成します。
f:id:bluebirdofoz:20170421020817j:plain
以下のページを参考に、ステータスを色々と変更してみました。
・Unityのパーティクル「Shuriken
 http://marupeke296.com/UNI_PT_No1_Shuriken.html

これで爆発時に時間経過によって色が変化するエフェクトが発生するはずです。
f:id:bluebirdofoz:20170421020847j:plain


アプリを起動して確認してみます。
f:id:bluebirdofoz:20170421021034j:plain
カラフルなエフェクトが発生しました。成功です。


今回パーティクルについて勉強しましたが、紹介ページの通り、"ParticleSystem"のコンポーネントには数多くの設定項目が存在します。
期待通りのエフェクトを作成するのはかなりの技術と根気が要求されます。

Unityのアセットストアでは様々な人が開発したパーティクルが数多く配布されています。
・アセットストア パーティクルシステム
 https://www.assetstore.unity3d.com/jp/#!/search/page=1/sortby=price/query=category:125

まずは頑張って自身で作るよりもストアから期待通りのエフェクトを探して利用/改変した方が良い結果が得られるかもしれません。

アンダーワールドの仕組みを探る その1(黒色の透過)

前回記事の続きとなります。
・HoloLensでホログラムにアクションを付ける
 http://bluebirdofoz.hatenablog.com/entry/2017/04/19/001341

今回はチュートリアルで体験したアンダーワールドについて、その仕組みを確認していきます。
まずはもう一度アプリを起動して、アンダーワールドを出現させます。
f:id:bluebirdofoz:20170420003012j:plain
その状態でhololensが穴より下に位置するように屈んでみると…
f:id:bluebirdofoz:20170420003018j:plain
見えない壁をすり抜けてアンダーワールドの世界に入れました。


UnityのプロジェクトでUnderworldを有効化してみると理由は一目瞭然です。
f:id:bluebirdofoz:20170420003026j:plain
この真っ黒な正方形のオブジェクトがアンダーワールドです。試しに"DarkBox"のメッシュを外してみると…
f:id:bluebirdofoz:20170420003035j:plain
中にアンダーワールドのオブジェクトが作り込まれていました。

ここで思い出すのは一番最初のチュートリアルで背景色に行った設定ですね。
・HOLOLENS 初めての開発 “HOLO”WORLD
 https://azure-recipe.kc-cloud.jp/2016/12/hololens-tutorial1/
・Hololensでの開発で知っておくと便利なこと
 http://qiita.com/miyaura/items/0f4cedb30101ab6a952b

Hololensでは黒色が透過扱いになります。
ここでは背景色にカラー[R 0][G 0][B 0][A 0]の真っ黒を指定することで背景を透過させていました。
つまり、この巨大なボックスも真っ黒なので透過扱いになっているという考えです。

仮説が正しいか確認するため、ボックスの色を白に変えて再実行してみます。
f:id:bluebirdofoz:20170420003054j:plain
どうやら正しかったようです。見渡す限りの巨大な白いボックスが現れました。
f:id:bluebirdofoz:20170420003115j:plain


さて、ここで一つ気になることがあったので試してみます。
"DarkBox"のメッシュを外し、色を付けないことによるオブジェクト透過の状態でアプリを起動してみます。
f:id:bluebirdofoz:20170420003129j:plain
結果、こちらのやり方では最初からアンダーワールドが見える状態となりました。
f:id:bluebirdofoz:20170420003159j:plain

今回の調査で面白いのは、同じ透過でもオブジェクトに色を付けない透過と黒色での透過は動作が異なるという点ですね。
黒色での透過は、そのオブジェクトに内包されたオブジェクトも含めて透過するということです。

これはhololensが黒色を透過色として扱っているだけということを考えれば当然の動作ですが、盲点でした。
この知恵は知っていれば何かに使えるかもしれません。

HoloLensでホログラムにアクションを付ける

Unityの機能により、3Dオブジェクトにアクションを付けることができます。
ただ物を配置するだけではなく、仮想現実と仮想現実が衝突したとき、何らかのアクションを起こすこともできます。

今回はチュートリアルの続きです。
・HOLOLENS ホログラムにアクションを付ける HOLOGRAPHIC FUN
 https://azure-recipe.kc-cloud.jp/2016/12/hololens-tutorial7/

説明通りにチュートリアルを行い、アプリを実行してみました。


まず折り紙をタップ操作で手頃な位置に配置します。
f:id:bluebirdofoz:20170419001247j:plain
そしてボールをタップすると以前のチュートリアルで実装した重力操作によりボールが折り紙に落下します。
f:id:bluebirdofoz:20170419001305j:plain
すると折り紙が爆発して消え去り、空間にできた穴にはアンダーワールドとも言うべき世界が広がっていました。

待って。チュートリアルの割りにいきなりハイレベルなことされて付いていけない。
というか、ホログラムのアクションうんぬんよりこっちの技術が気になる。。

とても面白いチュートリアルです。少しずつ読み解いて、この技術も身に着けていきます。


今回の追加スクリプトは以下。
HitTarget.cs

using UnityEngine;

public class HitTarget : MonoBehaviour
{
    // These public fields become settable properties in the Unity editor.
    public GameObject underworld;
    public GameObject objectToHide;

    // Occurs when this object starts colliding with another object
    void OnCollisionEnter(Collision collision)
    {
        // Hide the stage and show the underworld.
        objectToHide.SetActive(false);
        underworld.SetActive(true);

        // Disable Spatial Mapping to let the spheres enter the underworld.
        SpatialMapping.Instance.MappingEnabled = false;
    }
}

OnCollisionEnterは衝突イベントが発生したときに呼び出される関数です。
スクリプトは折り紙のオブジェクトに追加しており、ボールと折り紙が接触した際に呼び出されます。
今回の追加コードは各オブジェクトのアクティブ状態を変化しているだけです。

折り紙とアンダーグラウンドのアクティブ状態を変更しています。
ここから分かるのは折り紙オブジェクトのアクティブ状態はここでfalseにしているので、
爆発のアクションと、空間に空いた穴の表現は、underworldオブジェクト側で実装された処理ということです。

ひとまず"HolographicAcademy-Holograms-101"のチュートリアルはこれで最後なので、少しずつ理解します。

球体が弾け飛びユニティちゃんが現れる

以前紹介した下記のページには"TiledWall"シェーダ以外にも様々なシェーダが紹介されています。
今回はその中でもポリゴン分解を行うシェーダを使ってみます。
・HoloLens で使える Near Clip 表現について解説してみた
 http://tips.hecomi.com/entry/2017/03/27/003631

利用したのは"HoloLens/NearClip/DestructionAdditiveGS"というシェーダです。
シェーダとは3Dポリゴンの色の見た目を決めるものとばかり思っていました。
ポリゴン変形が可能とは驚きです。

利用方法として、球体のオブジェクトをタップすると、そのオブジェクトが分解する。
すると、球体の中からユニティちゃんが出てくる…という演出に利用することを考えました。

実現にはタップ操作が行われたときに、シェーダのプロパティを操作する必要があります。
しかしシェーダとはいえ、他のコンポーネントとそれほど操作方法は変わりません。
変数の参照方法に少し確認が必要なくらいでした。
・マテリアルのプロパティをスクリプトから変更【Unity】
 http://kan-kikuchi.hatenablog.com/entry/Material


マテリアルの反映とスクリプトの修正を行い、動作を確認します。
f:id:bluebirdofoz:20170418012509j:plain
起動時、目の前に白い球体が表れます。この球体をタップすると…
f:id:bluebirdofoz:20170418012531j:plain
オブジェクトが弾け飛び、中からユニティちゃんが表れました。


写真では良い感じに撮っていますが、今のままだと様々な問題があります。
まず球体にもアタリ判定があるため、シェーダを消しただけだと、障害物に干渉しまくります。
シェーダを消した後、球体オブジェクトそのものを無効化する必要があります。
あと画像では上手く隠していますが、ユニティちゃんの足が球体から飛び出しています。
奇麗に見せるには初め、ユニティちゃんは縮こまっていて、エフェクトと共に立ち上がるようなアクションが必要です。
更に球体がライティング無関係の真っ白です。これは元のシェーダを修正する必要があります。。
f:id:bluebirdofoz:20170418012746j:plain
プロジェクトで見ると、今はこんな感じです。

この辺を凝りだすと、コードをひたすらゴリゴリ書くだけの作業となってしまうのでブログとしては悩みものです。。
ただ、私のモチベーションとしては一先ずこのアプリに集中したいのでブラッシュアップは進めたいと思います。
その中で新しい技術が欲しくなることもあるでしょう。


今回は問題ありませんでしたが、シェーダのコードによっては下記のような問題も発生する模様です。
情報として残しておきます。
・Unity5のStandardシェーダのパラメタをスクリプトからいじろうとして丸一日潰れた話
 http://dnasoftwares.hatenablog.com/entry/2015/03/19/100108

また、シェーダを変形するにあたってスクリプト内でスリープをかけるということをしました。
Unityのスクリプト内でスリープ処理を行う場合、下記のような手段を用いる必要があるようです。
(きちんとは理解できておらず、理屈は説明できません。備忘録として残しておきます)
・WaitForSeconds
 https://docs.unity3d.com/jp/540/ScriptReference/WaitForSeconds.html

以下のようなコードを実装しました。0.05f秒ごとにシェーダのプロパティを変更しています。

IEnumerator Effect()
{
    float attval = 0.0f;
    for (int loop = 0; loop < 20; loop++)
    {
        attval = attval + 0.05f;
        renderer.material.SetFloat("_Destruction", attval);
        renderer.material.SetFloat("_PositionFactor", attval);
        renderer.material.SetFloat("_RotationFactor", attval);
        yield return new WaitForSeconds(0.05f);
    }
}

シェーダ(TiledWall)を読み解く

前回、紹介したシェーダ(TiledWall)ですが、Unityではシェーダでああいった表現が可能なんですね。
・空間マッピングのシェーダを変更する
 http://bluebirdofoz.hatenablog.com/entry/2017/04/16/170500
これまでシェーダについては単なるテクスチャというレベルの認識でしかありませんでした。

しかし、有名なhololensのアプリの一つであるHoleLenz(ホールレンズ)でもシェーダの機能を活用しているようです。
MRの活用には今後、避けては通れない技術っぽいです。
・別世界への穴へ入れるHoloLens向けアプリ『HoleLenz Gate』が配信
 http://www.moguravr.com/hololenz-gate/


今回初めて動的なシェーダを活用したので、その理解を深めておきたいところです。
という訳で、今回はTiledWallのコードを読み解いていきます。

そもそもシェーダの基礎知識についてですが、同ブログの以下記事で紹介されていました。
・Unity のシェーダの基礎を勉強してみたのでやる気出してまとめてみた
 http://tips.hecomi.com/entry/2014/03/16/233943

細かな用語や修飾子については以下のページがまとまっています。
・Unity Shader まとめ
 http://unitech.hatenablog.com/entry/2015/03/11/001953

これを理解した上で、TiledWallのコードを見てみます。
TiledWall.shader

Shader "HoloLens/SpatialMapping/TiledWall"
{
    // Properties
    // カラーやベクトルなどのプロパティをこの箇所に記述
    Properties
    {
        _Color("Color", Color) = (1, 1, 1, 1)
        _TilesPerMeter("Tiles per Meter", Float) = 10
        _Mask("Mask", Int) = 1
    }
    CGINCLUDE

    // "UnityCG.cginc"をインクルード
    #include "UnityCG.cginc"
    // "UnityCG.cginc"に定義される以下の"appdata_base"構造体を利用するため
    // struct appdata_base {
    //     float4 vertex   : POSITION;
    //     float3 normal   : NORMAL;
    //     float4 texcoord : TEXCOORD0;
    // };
    // "UnityCG.cginc"には他にも色々なヘルパ関数が存在する
    // ・UnityCG.cginc
    //   https://gist.github.com/hecomi/9580605

    // v2f構造体の定義
    // v2fという名前は慣例で Vertex To Fragmentの略
    // 頂点シェーダからフラグメントシェーダに複数の値を渡す時に利用する
    struct v2f
    {
        float4 vertex : SV_POSITION;
        float3 worldPos : TEXCOORD0;
        UNITY_VERTEX_OUTPUT_STEREO
    };
    // "SV_POSITION","	TEXCOORD0"の記載はセマンティクスと呼ばれ、変数のタグの役割をする
    // フラグメントシェーダへの入力(頂点シェーダの出力)には以下の種類が定義されている
    // Type(s)         Tag          Description
    // float4          SV_POSITION  MVP 変換後の座標
    // float3          NORMAL       MVP 変換後の法線
    // float4          TEXCOORD0    1番目のテクスチャの UV 座標
    // float4          TEXCOORD1    2番目のテクスチャの UV 座標
    // float4          TANGENT      接線
    // float4, fixed4  COLOR0       線形補間された色
    // float4, fixed4  COLOR1       線形補間された色
    // Any                          タグを持たない何でも良い値
    // UV座標とはテクスチャ画像の意味
    // http://alfa.hatenablog.jp/entry/2015/08/04/022548
    // "worldPos"変数が"TEXCOORD0"タグなのは"vert"関数での変換により
    // 入力したワールド座標をテクスチャの座標として利用しようとしているため

    // "Properties"の値を利用するため、同名変数を宣言する
    // "_TilesPerMeter"の参照変数
    float _TilesPerMeter;
    // "_Color"の参照変数
    fixed4 _Color;

    // 座標と時間を元に異なる少数値を算出する関数
    inline float toIntensity(float3 pos)
    {
        // fracは引数の小数部を返す
        // lengthは引数の長さを返す
        // _Timeは時間を取得する
        // _Timeの構造体は"float4(t/20、t、t×2、t×3)"であり
        // _Time.yは処理されていない時間情報(t)
        return frac(length(pos) - _Time.y);
    }

    // 頂点シェーダ
    v2f vert(appdata_base v)
    {
        // 返り値として利用するv2f構造体の変数を作成
        v2f o;
        // インスタンス ID がシェーダー関数にアクセス可能になる
        UNITY_SETUP_INSTANCE_ID(v);
        // VR向けの頂点シェーダーへの変換を行う
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
        // 同次座標において、オブジェクト空間からカメラのクリップ空間へ点を変換する
        o.vertex = UnityObjectToClipPos(v.vertex);
        // mulは乗算 unity_ObjectToWorldはモデル(ワールド)マトリクス
        // 物体の頂点ごとのワールド座標を取得し、worldPosに代入している
        o.worldPos = mul(unity_ObjectToWorld, v.vertex);
        // 構造体を返す
        return o;
    }
    // 利用関数の詳細な説明は以下にある
    // https://docs.unity3d.com/ja/540/Manual/GPUInstancing.html
    // https://docs.unity3d.com/ja/540/Manual/SL-BuiltinFunctions.html

    // フラグメントシェーダ
    fixed4 frag(v2f i) : SV_Target
    {
        // floorは指定された値以下の最大の整数を返す(返却値は浮動小数点のため、7.8なら7.0)
        float3 worldIndex = floor(i.worldPos.xyz * _TilesPerMeter);
        // ワールド座標に_TilesPerMeterを乗算してから除算することで空間をボックスセルに区切る
        float3 boxelCenter = worldIndex / _TilesPerMeter;
        // toIntensity関数を通して_Colorに乗算する小数値を作成する
        float intensity = toIntensity(boxelCenter);
        // 描画するカラーを返却する
        return _Color * intensity;
    }

    ENDCG

    // SubShader
    // この箇所に以下の本体を記述
    //  - サーフェイスシェーダ または
    //  - 頂点およびフラグメントシェーダ または
    //  - 固定関数シェーダ
    SubShader
    {
        Tags 
        { 
            // Tagはあくまでタグ付けなので実動作には影響しない?
            // "RenderType(レンダータイプ)"を"Opaque(不透明)"に指定
            "RenderType"="Opaque" 
            // "Queue(描画順)"を"Geometry(デフォルト)-1"に指定
            // Background→Geometry→AlphaTest→Transparent→Overlayの順で描画される。
            // つまり通常オブジェクトより背景に近いシェーダとして設定している
            "Queue"="Geometry-1"
        }

        // OCCLUSIONのシェーダーからの名前付きパスを使用する
        // 本プログラム中で何に利用しているかは不明
        UsePass "HoloLens/SpatialMapping/Occlusion/OCCLUSION"

        Pass
        {
            // ZWrite:デプスバッファに書き込みするか制御
            // 部分的に透過のエフェクトを描く場合、"ZWrite Off"に切り替え
            ZWrite Off
            // ZTest:デプステストの実行方法
            // LEqual:既に描画されているオブジェクトと距離が等しいか、より近い場合に描画する
            //        それより遠い場合はオブジェクトで隠す
            ZTest LEqual
            // Blend:透過のカラー作成に使用
            // SrcAlpha:このステージの値はソースα値を乗算する
            // OneMinusSrcAlpha:このステージの値はフレームバッファの(1-Source Alpha)を乗算する
            Blend SrcAlpha OneMinusSrcAlpha
            // 詳細は以下を参照
            // https://docs.unity3d.com/ja/540/Manual/SL-CullAndDepth.html
            // https://docs.unity3d.com/ja/540/Manual/SL-Blend.html

            // Stencil:ステンシルバッファはピクセルマスクごとにピクセルを保存や廃棄することを目的とする
            // ステンシルバッファは、通常、1 ピクセルあたり 8 ビットの整数である
            Stencil 
            {
                // 比較した結果、値がバッファに 0 ? 255 の整数で書き込まれる
                Ref [_Mask]
                // Comp:関数はバッファの現在の内容と基準値の比較に使用される
                // NotEqual:ピクセルのレファレンス値がバッファの値と等しくない場合のみレンダリングします
                Comp NotEqual
            }
            // 本シェーダでは特に処理を担っていない?(OCCLUSIONの引用?)

            // サーフェイスシェーダのブロック開始
            CGPROGRAM
            // 関数"vert"を頂点シェーダーとしてコンパイル
            #pragma vertex vert
            // 関数"frag"をフラグメントシェーダーとしてコンパイル
            #pragma fragment frag
            // コンパイルするシェーダーターゲットの指定。
            // 5.0の場合、DX11 シェーダーモデル 5.0.を指定している。
            #pragma target 5.0
            // 指定のレンダラー用にのみシェーダーをコンパイルします。
            // d3d11はDirect3D 11/12.
            #pragma only_renderers d3d11
            ENDCG
            // サーフェイスシェーダのブロック終了
            // Cg/HLSL スニペット(頂点シェーダ / フラグメントシェーダ)
            // 頂点シェーダはすべての頂点に対して座標変換を行う
            // フラグメントシェーダはすべてのピクセルに対して計算を行う
            // 処理の流れは 頂点シェーダ→フラグメントシェーダ となる
        }
        // ここでは用いられていないがライティングに作用するシェーダーを書きたいならば
        // 以下のサーフェスシェーダーの記述を利用する
        // #pragma surface 関数名 [optionalparams]
        // このシェーダはライティングによる影響を記述していない
        // (頂点シェーダ / フラグメントシェーダを使うシェーダはそもそもテクスチャや形状の変化が目的のため)
    }
}

これだけでも半日掛かりでコメントを付けることになりました。
尚、まだ不明な点があったりします。(UsePassやStencilの用途とか)

ある程度は理解できたので、TiledWallに少し手を加えて確認してみます。
現状だと走査エフェクトの間隔が短く、動きが速すぎるので、少々目立ちすぎるように感じます。
toIntensity関数を以下のように変更してみました。

    inline float toIntensity(float3 pos)
    {
        float timeval = _Time.y / 2;
        float result = frac(length(pos) - timeval);
        float coef = (length(pos) - timeval) * -1;
        int sector = coef % 5;
        if (sector > 0) result = 0;
        return result;
    }

利用する時間係数を2で割ることで時間による変化がゆっくりと進むようにしています。
またセクタ数を判定することで、5回に1回だけ走査エフェクトが走るようにしました。

反映して確認してみます。
f:id:bluebirdofoz:20170417002249j:plain
画像だと分かりづらいですが、走査エフェクトがゆっくりと今までの5回に1回の頻度で走るようになりました。
これであまり目立たず、しかし定期的に空間マッピングを確認できます。

長くなりました。ここまでです。
一つのシェーダの理解だけで一苦労でした。
昔、シェーダの技術はそれだけで本が一つ書けると聞きましたが、こういうことですか。
そもそもシェーダ用の言語(Cg言語)を理解する必要があるため、かなり奥深いです。

更に困りごととしてデバッグが非常に難しいです。ログは仕込めるのでしょうか。
今後の学習課題とします。

空間マッピングのシェーダを変更する

驚いたことに、hololensを個人的に購入した方が会社に3名もいました。
やはり開発者からはとても注目されているツールのようです。

さて、その1人の会社の先輩からhololensの面白いプロジェクトを教えてもらいました。
以下のページで紹介されています。
・HoloLens で使える Near Clip 表現について解説してみた
 http://tips.hecomi.com/entry/2017/03/27/003631


このページでは空間マッピングについて様々なデモンストレーションを見ることができます。
私がまず気になったのはTiled Wallというシェーダでした。
以下に動画が投稿されています。
・Tiled Wall
 https://www.youtube.com/watch?v=EvUgYo0JoZ8&feature=youtu.be


空間マッピングがリアルタイムでスキャニングされている表現がシェーダで行われています。
(あくまでシェーダの表現であって実際のスキャニングとは連携していないですが)
個人的に現実世界と仮想オブジェクトを複合させるMRでは「それっぽさ」が一番大切だと考えています。
是非とも、ユニティちゃん鬼ごっこアプリに仕込んじゃいましょう。

因みに上記のサンプルプロジェクトは以下から取得可能です。
・hecomi/HoloLensPlayground
 https://github.com/hecomi/HoloLensPlayground


ダウンロードすると以下のアセットにシェーダがありました。
HoloLensPlayground-master\Assets\Holo_Spatial_Shading\Materials

ユニティちゃんの操作スクリプトとして利用しているXboxOneController.csにはマテリアルの切り替え機能がありました。
そのまま利用しちゃいます。マテリアルをドラッグして反映するだけです。

アプリを動かしてみると、空間マッピングの見た目が変わりました。
f:id:bluebirdofoz:20170416170454j:plain
ちなみに色が変えれたので緑にしてみています。
映画マトリックスの影響か。こういった電脳空間的表現は緑が似合う気がします。

hololensの標準搭載アプリHologramsを利用する

hololensには標準で様々なアプリがインストールされています。
今回はその中の3Dモデルを表示する「Holograms」アプリを紹介します。

簡単に説明してしまえば、画像表示アプリの3Dデータ版でしょうか。
(hololensには「Photos」という普通の画像表示アプリもインストールされています)


起動方法はhololensのメニューを開き、「Holograms」アプリを選択して起動します。
f:id:bluebirdofoz:20170415235540j:plain
初期状態でサンプルの3Dデータが保存されていますので、今回はスペースシャトルを選びます。
f:id:bluebirdofoz:20170415235550j:plain
オブジェクトが表示されますので、指先でドラッグ操作を行い、好きなところに配置します。
f:id:bluebirdofoz:20170415235557j:plain
配置しました。空中に配置しているので好きな方向から眺めることができます。
f:id:bluebirdofoz:20170415235613j:plain
下から覗き込めばオブジェクトの下部が見えます。
f:id:bluebirdofoz:20170415235619j:plain
なお、このとき配置したオブジェクトは意図的に消さない限り、電源再起動しても部屋の同じ位置に残り続けます。


さて次は再生ボタンのある3Dデータがありますのでこれも配置してみましょう。
f:id:bluebirdofoz:20170415235635j:plain
配置しました。再生ボタンのある3Dデータはアニメーション再生可能なオブジェクトです。
f:id:bluebirdofoz:20170415235649j:plain
宇宙飛行士が漂いつつ、手を振っています。


さて、この「Holograms」アプリで表示・再生可能な3Dデータのフォーマットは何なのでしょうか?
・How to add holograms to the hologram app
 https://forums.hololens.com/discussion/851/how-to-add-holograms-to-the-hologram-app

どうやら独自フォーマットのようです。hololensを購入すれば、お手軽に3Dデータを保存して表示とはいきません。


では手段がないのかと言えば、そうではありません。
Microsoftは有料アプリとして、汎用フォーマットの3Dファイル表示アプリを別途リリースしていました。
f:id:bluebirdofoz:20170417024233j:plain
購入ページと紹介ページです。
・Verto Studio VR
 https://www.microsoft.com/en-us/store/p/verto-studio-vr/9p32wn9sxn7r
・HoloLensを使って3Dファイルを現実世界に表示するMRアプリ「VertoStudio VR」
 http://vrinside.jp/news/vertostudio-vr-mr-app/

対応しているデータフォーマットは FBX, OBJ, STL, DAE とのこと。
STLフォーマットはCADソフト用のファイル形式ですね。
CADデータを仕事で利用している方はhololensと本アプリを用いると仕事が便利になるかもしれません。
($99の有料アプリのため、流石に私は試せず。。)

購入ページだとSTLフォーマットの記載は見当たりませんが、以下のページで記載がありました。
・Verto Studio VR - 3D Modeling for HoloLens
 https://mixed.reality.news/forum/press-release-verto-studio-vr-3d-modeling-for-hololens-0176014/