MRが楽しい

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

GitHubの利用手順(アカウント登録)

本日は GitHub の利用手順です。
本記事では GitHub のアカウント作成の手順を記事にします。

アカウントの作成

GitHub のトップページからアカウント作成が可能です。
github.com
f:id:bluebirdofoz:20190510091419j:plain

以下の項目を入力し、[Sign up for GitHub]ボタンをクリックします。
・Username:ユーザ名(公開ユーザーID、他ユーザとの重複不可)
・Email:登録するメールアドレス
・Password:パスワード
f:id:bluebirdofoz:20190510091428j:plain

Bot確認の画面が開きます。
画面の指示に従って、操作を行います。
f:id:bluebirdofoz:20190510091436j:plain

次にプラン選択画面が開きます。
個人向けの場合、無償の Free プランと月額7ドルの Pro プランがあります。
f:id:bluebirdofoz:20190510091447j:plain

Pro プランでは以下の機能が利用できます。
・共同編集者を無制限に追加可能(Freeの場合3名まで)
GitHub Pageの利用(Freeの場合公開リポジトリのみ)
GitHub Wikiの利用(Freeの場合公開リポジトリのみ)
・保護ブランチ(Freeの場合公開リポジトリのみ)
・コードオーナー(Freeの場合公開リポジトリのみ)
リポジトリインサイトグラフの追加機能(Freeの場合公開リポジトリのみ)
2019/05/10現在では Free プランでもプライベートリポジトリは無制限に利用可能です。

今回は Free プランを利用します。
[Help me set up an organization next]にチェックを入れると、続けて組織アカウントを作成します。今回は利用しません。
[Send me updates on GitHub news, offers, and events]にチェックを入れると、GitHub に関する情報が登録メールアドレスに配信されます。今回は利用しません。
[Continue]ボタンをクリックします。
f:id:bluebirdofoz:20190510091527j:plain

最後にアンケート画面が開きます。
・What is your level of programming experience?:プログラムの経験について
・What do you plan to use GitHub for? :GitHubの利用目的について(3つ選択)
・What are you interested in?:その他、気になる事について
入力が完了したら[Submit]ボタンをクリックします。
f:id:bluebirdofoz:20190510091535j:plain

登録メールアドレスへ GitHub からの確認メールが送信されます。
f:id:bluebirdofoz:20190510091544j:plain

届いたメールを開き、[Verify email address]ボタンをクリックします。
f:id:bluebirdofoz:20190510091553j:plain

以上でアカウントの作成は完了です。
f:id:bluebirdofoz:20190510091600j:plain

サインイン方法

GitHub のトップページの[Sign up]からサインインが可能です。
github.com
f:id:bluebirdofoz:20190510091610j:plain

Pro プランとの差異となる機能の詳細

GitHub Pageについて
help.github.com
GitHub Wikiについて
help.github.com
・保護ブランチについて
help.github.com
・コードオーナーについて
help.github.com
リポジトリインサイトグラフについて
help.github.com

UnityのStatsボタンでレンダリング統計ウィンドウを確認する

本日は Unity の調査枠です。
[Stats]ボタンで表示されるレンダリング統計ウィンドウの情報を記事にします。

レンダリング統計ウィンドウ

レンダリング統計ウィンドウは Unity の[Game]ウィンドウにある[Stats]ボタンで有効化できます。
f:id:bluebirdofoz:20190509092127j:plain
本ウィンドウの[Graphics]項目には以下の情報が含まれます。

フレームあたりの時間とFPS

1つのフレームを処理とレンダリングするのに要した時間と、相互関係にあるフレーム数/秒(FPS)を表示します。
この数字にはフレーム更新とゲームビューのレンダリングに要した時間のみが含まれます。
エディターが Scene ビューと Inspector の描画、エディター専用の処理を行うのに要した時間は含みません。

Batches

バッチングによる Batch の統合数を表示します。
「バッチング」とはエンジンが複数オブジェクトをチャンクにして、まとめてレンダリングしようと試みるものです。
リソース切り替えの為に発生する CPU のオーバーヘッドを減らすために用いられます。
まとめたメッシュごとの処理単位を Batch と呼びます。

Saved by batching

ドローコールバッチングで統合されたドローコールの数を表示します。
バッチングを確実に行うためには異なるオブジェクトでもできる限りマテリアルを共有します。
レンダリングステートを変更すると、同じステート集団の batches は分割されます。

Tris and Verts

描画する三角形数(Tris)および頂点数(Verts)を表示します。

Screen

画面サイズ、およびメモリ使用量を表示します。

SetPass

レンダリングパスの数を表示します。
これは CPU から GPU に対して描画命令を送る回数を意味します。
Batch 毎に SetPass Call -> Draw Call の処理が実行されます。
SetPass Call で CPU から GPU にマテリアルの設定値などが通知されます。
Draw Call は CPU が GPU に対して設定値によるメッシュの描画を命令することです。
異なる Batch であっても同じマテリアルの SetPass Call などは統合されます。

Visible Skinned Meshes

レンダリングされたスキンメッシュの数を表示します。

Animations

再生しているアニメーションの数を表示します。

レンダリング統計ウィンドウの変化を確認する

一通り、その意味合いを確認したところで、実際に統計の変化を確認してみます。
まずは試しに1つの Cube オブジェクトを配置してレンダリング統計ウィンドウを確認します。
すると、Batches と Set Pass Call の値が 6 になりました。
f:id:bluebirdofoz:20190509092143j:plain

Cube オブジェクトを1つ配置しただけの変化にしては増え過ぎのように感じます。
メニューから Window -> Frame Debugger を起動してレンダリングの内容を細かく確認してみます。
f:id:bluebirdofoz:20190509092152j:plain

すると、Shadows.RenderShadowMap で Batches が 4 増えていることが分かりました。
Unityではライトで照らされるオブジェクトには自動的に影が描画されます。
この影を描画する際にも Batches や Set Pass Call が増加します。
f:id:bluebirdofoz:20190509092202j:plain

試しに[Directional Light]オブジェクトの[Shadow Type]を[No Shadows]に変更して、影の描写を止めてみます。
f:id:bluebirdofoz:20190509092211j:plain

影を描画を止めると、Shadows.RenderShadowMap の処理が消えて、Batches と Set Pass Call の値が 2 になりました。
f:id:bluebirdofoz:20190509092222j:plain

Cube オブジェクトを1つ描画するには、まだ Batches が1つ多いです。
こちらも Frame Debugger で確認すると、これはレンダリングバッファのクリア処理であることが分かります。
f:id:bluebirdofoz:20190509092231j:plain

これで Cube オブジェクトは 1 の Batches で処理されていることが確認できました。
f:id:bluebirdofoz:20190509092241j:plain

UnityのSkybox背景を無地の背景に切り替える

本日は Unity の調査枠です。
UnityのSkybox背景を無地の背景に切り替える方法を記事にします。
f:id:bluebirdofoz:20190508085140j:plain

Skyboxを切り替える

Unity はプロジェクト作成時にシーン背景としてデフォルトで Skybox が設定されています。
背景を無地にしたい場合や、レンダリング性能の確認を行う際には Skybox が不要になります。

Skybox を無効化するには Scene と Camera のそれぞれの設定を変更する必要があります。
f:id:bluebirdofoz:20190508085154j:plain

Cameraの背景を無地に変更する

Camera の背景設定を変更します。
Main Camera のオブジェクトを選択し、Inspector ビューを開きます。
[Camera]コンポーネントの[ClearFlags]の設定を[Skybox]から[Solid Color]に変更します。
f:id:bluebirdofoz:20190508085203j:plain

これでカメラの背景描画部分には[Background Color]の単色カラーが表示されるようになります。
f:id:bluebirdofoz:20190508085213j:plain

Sceneの背景を無地に変更する

Camera の設定を変更しただけだと[Scene]ビューの Skybox は表示されたままです。
[Scene]ビューの Skybox も非表示にしたい場合には[Scene]ビューの[画像]アイコンをクリックします。
プルダウンに表示される[Skybox]をクリックしてチェックを外します。
f:id:bluebirdofoz:20190508085226j:plain

これで[Scene]ビューの Skybox も無効化されます。
f:id:bluebirdofoz:20190508085237j:plain

参考ページ

docs.unity3d.com

Blenderで利用可能なpythonスクリプトを作る その8(クリーンアップ)

本日は Blender の技術調査枠です。
Blenderで利用可能なpythonスクリプトを作ります。
bluebirdofoz.hatenablog.com

孤立/重複頂点削除のクリーンアップを実行

指定オブジェクトに孤立/重複頂点削除のクリーンアップの処理を実行します。
スクリプト例ではオブジェクトに以下のクリーンアップを実行します。

大きさ0を融解:面積が0の面を削除し、1つの頂点にまとめる
孤立を削除:どの面にもつながっていない辺や頂点を削除する
重複頂点を削除:重複している頂点を1つの頂点にまとめる

・cleanup_mesh_object.py

# bpyインポート
import bpy

# オブジェクトに以下のクリーンアップを実行
# ・大きさ0を融解:面積が0の面を削除し、1つの頂点にまとめる
# ・孤立を削除:どの面にもつながっていない辺や頂点を削除する
# ・重複頂点を削除:重複している頂点を1つの頂点にまとめる
# 引数   arg_objectname:指定オブジェクト名
# 戻り値
def cleanup_mesh_object(arg_objectname='Default'):
  # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する
  for ob in bpy.context.scene.objects:
    # 非選択状態に設定する
    ob.select=False
  # 指定オブジェクトを取得する
  selectob = bpy.data.objects[arg_objectname]
  # 変更オブジェクトをアクティブに変更する
  bpy.context.scene.objects.active = selectob
  # 編集モードに移行する
  bpy.ops.object.mode_set(mode='EDIT', toggle=False)
  # 頂点を全選択した状態とする
  bpy.ops.mesh.select_all(action='SELECT') 
  # 大きさ0を融解(結合距離 0.0001)
  bpy.ops.mesh.dissolve_degenerate(threshold=0.0001)
  # 変更を反映するため再び頂点を全選択
  bpy.ops.mesh.select_all(action='SELECT') 
  # 孤立を削除(頂点、辺のみ)
  bpy.ops.mesh.delete_loose(use_verts=True, use_edges=True, use_faces=False)
  # 孤立を削除で全選択が解除されるので再び頂点を全選択
  bpy.ops.mesh.select_all() 
  # 重複頂点を削除(結合距離 0.0001、非選択部の結合無効)
  bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False)
  # オブジェクトモードに移行する
  bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
  return

# 関数の実行例
cleanup_mesh_object('Cube')

f:id:bluebirdofoz:20190507025117j:plain

穴を埋めるクリーンアップを実行

指定オブジェクトに[穴を埋める]のクリーンアップの処理を実行します。
・fillholes_mesh_object.py

# bpyインポート
import bpy

# オブジェクトに以下のクリーンアップを実行
# ・穴を埋める:辺ループの穴を埋める(辺数:4)
# 引数   arg_objectname:指定オブジェクト名
# 戻り値
def fillholes_mesh_object(arg_objectname='Default'):
  # 他のオブジェクトに操作を適用しないよう全てのオブジェクトを走査する
  for ob in bpy.context.scene.objects:
    # 非選択状態に設定する
    ob.select=False
  # 指定オブジェクトを取得する
  selectob = bpy.data.objects[arg_objectname]
  # 変更オブジェクトをアクティブに変更する
  bpy.context.scene.objects.active = selectob
  # 編集モードに移行する
  bpy.ops.object.mode_set(mode='EDIT', toggle=False)
  # 頂点を全選択した状態とする
  bpy.ops.mesh.select_all(action='SELECT') 
  # 穴を埋める(辺数:4)
  bpy.ops.mesh.fill_holes(sides=4)
  # オブジェクトモードに移行する
  bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
  return

# 関数の実行例
fillholes_mesh_object('Sphere')

f:id:bluebirdofoz:20190507025127j:plain

3Dモデルの大きな穴をメッシュのクリーンアップや辺選択を使って埋める

本日は Blender の技術調査枠です。
3Dモデルの大きな穴をBlenderのクリーンアップや辺選択を使って埋める方法を記事にします。

Blenderのクリーンアップで穴を埋める

Blenderにはクリーンアップという機能があり、3Dモデルのエラーをある程度自動で修復できます。
その中の一つに「穴を埋める」という機能があり、3Dモデルに空いた穴を検出して自動で埋めることができます。
以下でクリーンアップ利用方法を記事にしています。
bluebirdofoz.hatenablog.com

クリーンアップで穴を埋める

以下のような穴空きのあるUV球を用意しました。
f:id:bluebirdofoz:20190506221249j:plain

左半分には1つの面を削除した穴があります。
右半分には連続する2つ以上の面を削除した大きな穴があります。
f:id:bluebirdofoz:20190506221259j:plain

これをクリーンアップしてみます。
[編集モード]のメニューから メッシュ -> クリーンアップ -> 穴を埋める を実行します。
f:id:bluebirdofoz:20190506221315j:plain

結果、以下のように1つの面を削除した穴のみが埋められます。
連続する2つ以上の面を削除した大きな穴は面が貼られていません。
f:id:bluebirdofoz:20190506221327j:plain

クリーンアップでより大きな穴を埋める

2019/05/07追記。
確認したところ、クリーンアップの[穴を埋める]でも辺数を指定すれば、大きな穴も埋められたので追記します。
頂点を全選択した[編集モード]のメニューから メッシュ -> クリーンアップ -> 穴を埋める を実行します。
f:id:bluebirdofoz:20190507024450j:plain

[穴を埋める]パネルが表示されます。
ここの[辺数]を変更すれば、指定した辺数以下の辺ループの穴が埋められます。
f:id:bluebirdofoz:20190507024505j:plain

辺選択を使って大きな穴を埋める場合

辺選択で連続性のない境界辺を選択し、[面張り]を実行することでも穴を埋めることができます。
[編集モード]で[辺選択]のモードに切り替えます。
f:id:bluebirdofoz:20190506221429j:plain

メニューから 選択 -> 特徴で全選択 -> 非多様体 を実行します。
f:id:bluebirdofoz:20190506221438j:plain

[非多様体を選択]パネルが開きます。
[境界]にチェックを入れると、穴が開いているで境界となっている辺が全て選択されます。
f:id:bluebirdofoz:20190506221449j:plain

この状態でメニューから メッシュ -> 面 -> 面を張る を実行します。
f:id:bluebirdofoz:20190506221506j:plain

これで選択中の全ての辺ループに面が貼られます。
f:id:bluebirdofoz:20190506221518j:plain
意図的に開けているような大きな穴でも埋めてしまうので注意が必要です。
完全に閉じられた想定の3Dモデルに対して利用します。

HoloLensの3DアプリでBlueToothAdvertisementを送信する

本日は HoloLens の技術調査枠です。
前回とは逆に、HoloLensの3DアプリでBlueToothAdvertisementを送信する方法を記事にします。
以下の記事の続きです。
bluebirdofoz.hatenablog.com

プロジェクトとシーンの準備

以下の記事を元にHoloLens(WindowsMR)プロジェクトを作成します。
bluebirdofoz.hatenablog.com

2019/5/3現在、MRTK 2017 の最新バージョンは 2017.4.3.0 です。
f:id:bluebirdofoz:20190505222845j:plain

BluetoothAdvertisementを参考に作成したサンプルコード

BluetoothAdvertisement のパブリッシャクラスのコードを参考に、Advertisement を送信するクラスを作成しました。
・BluetoothAdvertisePublisher.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if WINDOWS_UWP
using System;
using Windows.Storage.Streams;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
#endif

public class BluetoothAdvertisePublisher : MonoBehaviour
{
    /// <summary>
    /// テキストオブジェクトの参照
    /// </summary>
    public UnityEngine.UI.Text MessageText;
    string showmessage;

    /// <summary>
    /// 起動時処理
    /// </summary>
    void Start()
    {
        App_Message("Start");
#if WINDOWS_UWP
        // 初期化
        Awake_BluetoothLEAdvertisementPublisher();

        // ハンドラ登録
        OnEnable_BluetoothLEAdvertisementPublisher();

        // パブリッシャ起動
        Run_BluetoothLEAdvertisementPublisher();
#endif
    }

    /// <summary>
    /// 定期実行
    /// </summary>
    private void Update()
    {
        if (showmessage.Length != 0)
        {
            MessageText.text = showmessage;
            showmessage = "";
        }
    }

    /// <summary>
    /// テキスト更新
    /// </summary>
    /// <param name="message">メッセージ</param>
    void App_Message(string message)
    {
        showmessage = message;
    }

    /// <summary>
    /// 終了時処理
    /// </summary>
    void OnApplicationQuit()
    {
#if WINDOWS_UWP
        // 終了
        OnDisable_BluetoothLEAdvertisementPublisher();
#endif
    }

#if WINDOWS_UWP
    /// <summary>
    /// Bluetooth LE advertisement の制御とカスタマイズに使用するBluetoothLEAdvertisementPublisher
    /// </summary>
    private BluetoothLEAdvertisementPublisher publisher;

    /// <summary>
    /// BluetoothLEAdvertisementPublisher初期化(Awake)
    /// </summary>
    public void Awake_BluetoothLEAdvertisementPublisher()
    {
        // 新しいパブリッシャインスタンスを作成して初期化します。
        publisher = new BluetoothLEAdvertisementPublisher();

        // advertisement にペイロードを追加する必要があります。
        // ペイロードがない、または無効なパブリッシャーは起動できません。
        // ペイロードを設定する必要があるのは、どのパブリッシャに対しても1回だけです。

        // manufacturer-specific セクションを追加します。
        // まず、製造元データセクションを作成します。
        var manufacturerData = new BluetoothLEManufacturerData();

        // 製造元データのIDを設定します。ここでは 0xFFFE の値を指定します。
        manufacturerData.CompanyId = 0xFFFE;

        // manufacturer-specific セクション内にデータペイロードを設定します
        // ここでは以下の16ビットのUUIDを使用します。
        // 0x1234 - > { 0x34、0x12}(リトルエンディアン)
        var writer = new DataWriter();
        UInt16 uuidData = 0x1234;
        writer.WriteUInt16(uuidData);

        // 書き込みデータのバッファ長はアドバタイズペイロードに収まる必要があります。
        // 収まらない場合は例外が発生します。
        manufacturerData.Data = writer.DetachBuffer();

        // 製造元データを advertisement パブリッシャに追加します。
        publisher.Advertisement.ManufacturerData.Add(manufacturerData);
    }

    /// <summary>
    /// BluetoothLEAdvertisementPublisher初期化(OnEnable)
    /// </summary>
    public void OnEnable_BluetoothLEAdvertisementPublisher()
    {
        // パブリッシャのステータスを監視するためのイベントハンドラを登録します。
        // これにより、advertisement が提供されたかどうか、がわかります。
        // また、Bluetooth がユーザーによってオフになっているなどの予期しないエラーについても通知します。
        publisher.StatusChanged += OnStatusChanged_AdvertisementPublisher;
    }

    /// <summary>
    /// BluetoothLEAdvertisementPublisher終了(OnDisable)
    /// </summary>
    public void OnDisable_BluetoothLEAdvertisementPublisher()
    {
        // コンテキストを離れるときは、パブリッシャを停止します。
        // パブリッシャが停止されていなくても、パブリッシャが破棄されるとスキャンは自動的に停止します。
        publisher.Stop();
        // リークを防ぐためにリソースを解放する場合、ハンドラの登録を解除します。
        publisher.StatusChanged -= OnStatusChanged_AdvertisementPublisher;
    }

    /// <summary>
    /// BluetoothLEAdvertisementPublisherサスペンド時処理
    /// </summary>
    public void Suspending_BluetoothLEAdvertisementPublisher()
    {
        // 中断時にはパブリッシャを停止します。
        publisher.Stop();
        // リークを防ぐためにリソースを解放する場合、ハンドラの登録を解除します。
        publisher.StatusChanged -= OnStatusChanged_AdvertisementPublisher;
    }

    /// <summary>
    /// BluetoothLEAdvertisementPublisher再開時処理
    /// </summary>
    public void Resuming_BluetoothLEAdvertisementPublisher()
    {
        // ハンドラを登録してパブリッシャを再開します。
        publisher.StatusChanged += OnStatusChanged_AdvertisementPublisher;
        // 再開時にパブリッシャを開始します。
        publisher.Start();
    }

    /// <summary>
    /// BluetoothLEAdvertisementPublisher実行処理(Run)
    /// </summary>
    public void Run_BluetoothLEAdvertisementPublisher()
    {
        // パブリッシャの Start() を呼び出すと advertisement が開始されます。
        publisher.Start();
    }

    /// <summary>
    /// BluetoothLEAdvertisementPublisher停止処理(Stop)
    /// </summary>
    public void Stop_BluetoothLEAdvertisementPublisher()
    {
        // パブリッシャの Stop() を呼び出すと advertisement が停止します。
        publisher.Stop();
    }

    /// <summary>
    /// パブリッシャのステータスが変更されたときにイベントハンドラとして呼び出されます。
    /// </summary>
    /// <param name="publisher">イベントをトリガーした発行者のインスタンス</param>
    /// <param name="eventArgs">発行者ステータス変更イベントに関する情報を含むイベントデータ</param>
    private async void OnStatusChanged_AdvertisementPublisher(
        BluetoothLEAdvertisementPublisher publisher,
        BluetoothLEAdvertisementPublisherStatusChangedEventArgs eventArgs)
    {
        // このイベントハンドラを使用して、発行者のステータスを監視できます。
        // パブリッシャーがシステムによって中止された場合、エラーを検出できます。
        BluetoothLEAdvertisementPublisherStatus status = eventArgs.Status;
        BluetoothError error = eventArgs.Error;

        // 変更イベント処理を行う
        // 変更イベントメッセージをテキストオブジェクトに表示する
        App_Message("StatusChange Event");
    }
#endif
}

Empty オブジェクトを作成し、本スクリプトをアタッチしました。
f:id:bluebirdofoz:20190505222905j:plain

スクリプトの MessageText にテキストオブジェクトの参照を追加します。
f:id:bluebirdofoz:20190505222915j:plain

Capabilityの追加

UWPアプリで Bluetooth を利用する場合、Capability の設定が必要です。
メニューから Edit -> Project Settings - Player を開きます。
f:id:bluebirdofoz:20190505222926j:plain

Inspector ビューから Publishing Setttings を開きます。
[Capabilities]欄の[Bluetooth]にチェックを入れます。
f:id:bluebirdofoz:20190505222938j:plain

後は HoloLens 向けにプロジェクトをビルドしてインストールします。
UnityプロジェクトのビルドとHoloLensへのインストール手順については以下を参照してください。
bluebirdofoz.hatenablog.com

HoloLensでの動作確認

この状態で HoloLens 上で動作確認を行います。
HoloLens 上でアプリを起動すると、Bluetooth のアドバタイズが送信されます。
f:id:bluebirdofoz:20190505223008j:plain

受信側アプリの実行

PC側で前回ビルドした BluetoothAdvertisement アプリを起動します。
[Foreground watcher]のタブを開き、[Run]を実行して BlueTooth のアドバタイズを受信します。
HoloLens で送信されたアドバタイズを受信すると、IDとデータ値が表示されます。
f:id:bluebirdofoz:20190505223019j:plain

HoloLensの3DアプリでBlueToothのアドバタイズを取得する

本日は HoloLens の技術調査枠です。
HoloLens の3Dアプリで BlueTooth のアドバタイズを受信する方法を記事にします。
以下の記事の続きです。
bluebirdofoz.hatenablog.com

プロジェクトとシーンの準備

以下の記事を元にHoloLens(WindowsMR)プロジェクトを作成します。
bluebirdofoz.hatenablog.com

2019/5/3現在、MRTK 2017 の最新バージョンは 2017.4.3.0 です。
f:id:bluebirdofoz:20190504225944j:plain

BluetoothAdvertisementを参考に作成したサンプルコード

BluetoothAdvertisement のウォッチャークラスのコードを参考に、テキストオブジェクトに結果を出力するクラスを作成しました。
・BluetoothAdvertiseWatcher.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if WINDOWS_UWP
using System;
using Windows.Storage.Streams;
using Windows.Devices.Bluetooth.Advertisement;
#endif

public class BluetoothAdvertiseWatcher : MonoBehaviour {

    /// <summary>
    /// テキストオブジェクトの参照
    /// </summary>
    public UnityEngine.UI.Text MessageText;
    string showmessage;

    /// <summary>
    /// 起動時処理
    /// </summary>
    void Start ()
    {
        App_Message("Start");
#if WINDOWS_UWP
        // 初期化
        Awake_BluetoothLEAdvertisementWatcher();

        // ハンドラ登録
        OnEnable_BluetoothLEAdvertisementWatcher();

        // ウォッチャー起動
        Run_BluetoothLEAdvertisementWatcher();
#endif
    }

    /// <summary>
    /// 定期実行
    /// </summary>
    private void Update()
    {
        if (showmessage.Length != 0)
        {
            MessageText.text = showmessage;
            showmessage = "";
        }
    }

    /// <summary>
    /// テキスト更新
    /// </summary>
    /// <param name="message">メッセージ</param>
    void App_Message(string message)
    {
        showmessage = message;
    }

    /// <summary>
    /// 終了時処理
    /// </summary>
    void OnApplicationQuit()
    {
#if WINDOWS_UWP
        // 終了
        OnDisable_BluetoothLEAdvertisementWatcher();
#endif
    }

#if WINDOWS_UWP
        /// <summary>
        /// Bluetooth LE スキャンを制御およびカスタマイズするための BluetoothLEAdvertisementWatcher 
        /// </summary>
        private BluetoothLEAdvertisementWatcher watcher;
        
        /// <summary>
        /// BluetoothLEAdvertisementWatcher初期化(Awake)
        /// </summary>
        public void Awake_BluetoothLEAdvertisementWatcher()
        {
            // ウォッチャーインスタンスを作成して初期化します。
            watcher = new BluetoothLEAdvertisementWatcher();

            // 指定のパブリッシャによってアドバタイズされたデータを探すようにアドバタイズフィルタを設定します。
            // すべてのフィルタを削除する場合はウォッチャ設定をコメントアウトします。
            // フィルタを指定しない場合、受信したすべての BluetoothAdvertisement がイベントハンドラを通じて通知されます。

            // API間でフィルタ制限を決定するには、以下のプロパティを使用します。
            //      MinSamplingInterval, MaxSamplingInterval, MinOutOfRangeTimeout, MaxOutOfRangeTimeout

            // 特定の advertisement ペイロードを監視するための advertisement フィルタの設定を行います。
            // マッチングしたい製造元データセクションを作成します。
            var manufacturerData = new BluetoothLEManufacturerData();

            // 製造元データのIDを設定します。ここでは 0xFFFE の値を指定します。
            manufacturerData.CompanyId = 0xFFFE;

            // manufacturer-specific セクション内にデータペイロードを設定します
            // ここでは以下の16ビットのUUIDを使用します。
            // 0x1234 - > { 0x34、0x12}(リトルエンディアン)
            var writer = new DataWriter();
            writer.WriteUInt16(0x1234);

            // 書き込みデータのバッファ長はアドバタイズペイロードに収まる必要があります。
            // 収まらない場合は例外が発生します。
            manufacturerData.Data = writer.DetachBuffer();

            // 製造元データをウォッチャーの advertisement フィルターに追加します。
            watcher.AdvertisementFilter.Advertisement.ManufacturerData.Add(manufacturerData);


            // 近接通信用の信号強度フィルタの設定を行います。
            // 機器が範囲内にある場合にのみイベントを伝播するように信号強度フィルターを構成します。
            // 環境によって advertisement が表示されない場合は、これらの値を調整します。
            // ここでは範囲内閾値を - 70dBmに設定します。
            // RSSI > = -70dBm の advertisement が「範囲内」と見なされます。
            watcher.SignalStrengthFilter.InRangeThresholdInDBm = -70;

            // 範囲外の閾値を-75dBmに設定します(範囲内閾値との差にある程度のバッファを入れます)。
            // advertisement が「範囲内」と見なされなくなったタイミングを判断するため、
            // OutOfRangeTimeout と組み合わせて使用します。
            watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -75;

            // 範囲外タイムアウトを2秒に設定します。
            // advertisement が「範囲内」と見なされなくなった時期を判断するため、
            // OutOfRangeThresholdInDBm と組み合わせて使用します。
            watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(2000);

            // デフォルトでは、サンプリング間隔はゼロに設定されています。
            // この場合、サンプリングは行われず、受信したすべての情報は Received イベントで返されます。
            // ウォッチャー設定は以上です。
        }

        /// <summary>
        /// BluetoothLEAdvertisementWatcher初期化(OnEnable)
        /// </summary>
        public void OnEnable_BluetoothLEAdvertisementWatcher()
        {
            // 受け取った提供情報を処理するためのハンドラーを登録します。
            // 受信ハンドラがアタッチされていないとウォッチャーを起動できません。
            watcher.Received += OnReceived_AdvertisementWatcher;

            // 様々な条件で停止処理を行うためのハンドラーを登録します。
            // Bluetooth無線がオフになった、またはStopメソッドが呼び出された場合に実行されます。
            watcher.Stopped += OnStopped_AdvertisementWatcher;
        }

        /// <summary>
        /// BluetoothLEAdvertisementWatcher終了(OnDisable)
        /// </summary>
        public void OnDisable_BluetoothLEAdvertisementWatcher()
        {
            // コンテキストを離れるときは、ウォッチャーを停止します。
            // ウォッチャーが停止されていなくても、ウォッチャーが破棄されるとスキャンは自動的に停止します。
            watcher.Stop();
            // リークを防ぐためにリソースを解放する場合、ハンドラの登録を解除します。
            watcher.Received -= OnReceived_AdvertisementWatcher;
            watcher.Stopped -= OnStopped_AdvertisementWatcher;
        }

        /// <summary>
        /// BluetoothLEAdvertisementWatcher実行処理(Run)
        /// </summary>
        public void Run_BluetoothLEAdvertisementWatcher()
        {
            // ウォッチャーの Start() を呼び出すとスキャンが開始されます。
            watcher.Start();
        }

        /// <summary>
        /// BluetoothLEAdvertisementWatcher停止処理(Stop)
        /// </summary>
        public void Stop_BluetoothLEAdvertisementWatcher()
        {
            // ウォッチャーの Stop() を呼び出すとスキャンが停止します。
            watcher.Stop();
        }

        /// <summary>
        /// advertisement を受信したときにイベントハンドラとして呼び出されます。
        /// </summary>
        /// <param name="watcher">イベントをトリガーしたウォッチャーのインスタンス</param>
        /// <param name="eventArgs">advertisement イベントに関する情報を含むイベントデータ</param>
        private async void OnReceived_AdvertisementWatcher(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
        {
            App_Message("Reseived");
            // EventArgsクラスのプロパティにアクセスすることで、受け取った advertisement に関するさまざまな情報を入手できます。
            
            // イベントのタイムスタンプを取得します。
            DateTimeOffset timestamp = eventArgs.Timestamp;
            
            // advertisement の種類を取得します。
            BluetoothLEAdvertisementType advertisementType = eventArgs.AdvertisementType;

            // 受信信号強度インジケータ(RSSI)を取得します。
            Int16 rssi = eventArgs.RawSignalStrengthInDBm;

            // Bluetooth LE Advertisement を送信しているデバイスのBluetoothアドレスを取得します。
            ulong BlueToothAddress = eventArgs.BluetoothAddress;

            // ペイロードに含まれる advertisement デバイスのローカル名(存在する場合)を取得します。
            string localName = eventArgs.Advertisement.LocalName;

            // manufacturer-specific セクションがあるかどうかを確認します。
            // ある場合は、manufacturer-specific セクションのデータを取得します。
            // 複数ある場合、最初のセクションのみ取得します。
            string manufacturerDataString = "";
            var manufacturerSections = eventArgs.Advertisement.ManufacturerData;
            if (manufacturerSections.Count > 0)
            {
                // リストの最初のものだけを取得する
                BluetoothLEManufacturerData manufacturerData = manufacturerSections[0];
                byte[] data = new byte[manufacturerData.Data.Length];
                using (DataReader reader = DataReader.FromBuffer(manufacturerData.Data))
                {
                    reader.ReadBytes(data);
                }
                // IDとmanufacturer-specific セクションのデータを16進形式で取得します。
                manufacturerDataString = string.Format("0x{0}: {1}",
                    manufacturerData.CompanyId.ToString("X"),
                    BitConverter.ToString(data));
            }

            // 受信処理を行う
            // 受信データをテキストオブジェクトに表示する
            App_Message(manufacturerDataString);
        }

        /// <summary>
        /// ウォッチャが停止または中止されたときにイベントハンドラとして呼び出されます。
        /// </summary>
        /// <param name="watcher">イベントをトリガーしたウォッチャーのインスタンス</param>
        /// <param name="eventArgs">ウォッチャーが停止または中断した理由に関する情報を含むイベントデータ</param>
        private async void OnStopped_AdvertisementWatcher(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementWatcherStoppedEventArgs eventArgs)
        {
            // 終了処理を行う
            // 終了メッセージをテキストオブジェクトに表示する
            App_Message("Stop Event");
        }
#endif
}

Empty オブジェクトを作成し、本スクリプトをアタッチしました。
f:id:bluebirdofoz:20190504230019j:plain

スクリプトの MessageText にテキストオブジェクトの参照を追加します。
f:id:bluebirdofoz:20190504230029j:plain

Capabilityの追加

UWPアプリで Bluetooth を利用する場合、Capability の設定が必要です。
メニューから Edit -> Project Settings - Player を開きます。
f:id:bluebirdofoz:20190504230042j:plain

Inspector ビューから Publishing Setttings を開きます。
[Capabilities]欄の[Bluetooth]にチェックを入れます。
f:id:bluebirdofoz:20190504230054j:plain

後は HoloLens 向けにプロジェクトをビルドしてインストールします。
UnityプロジェクトのビルドとHoloLensへのインストール手順については以下を参照してください。
bluebirdofoz.hatenablog.com

送信側アプリの実行

PC側で前回ビルドした BluetoothAdvertisement アプリを起動します。
[Foreground publisher]のタブを開き、[Run]を実行して BlueTooth のアドバタイズを実行しておきます。
f:id:bluebirdofoz:20190504230125j:plain

HoloLensでの動作確認

この状態で HoloLens 上で動作確認を行います。
正常に BlueTooth のアドバタイズを受信すると、テキストオブジェクトにIDとデータ値が表示されます。
f:id:bluebirdofoz:20190504230149j:plain