この記事は「NeosVR reso Advent Calendar 2020」、および「NeosVR Advent Calendar 2020」の11日目です
昨日は、@nuwaa さんの「『NeosVR reso』というコミュニティ 」でした
NeosVRから現実世界に干渉し、生活や仕事をより良いものへと改変していくコミュニティ「NeosVR reso」のオーガナイザー、piacereです
ご覧いただいて、ありがとうございます
私は、分散・並列データサイエンスに強い「Elixir」というプログラミング言語を2015年から使い始め、2017年以降はコミュニティ「fukuoka.ex」を主催しつつ、業務でも積極登用してて、現在は業務/プライベート共に、ほぼこの言語しか使っていない状態なんですが、国内ではまだまだマイナーな部類の言語です
しかし、世界を見てみると、StackOverflowという有名サイトが行った「世界でもっとも高給取りなプログラミング言語ランキング」で5位に入っている言語で、更にDiscordやSlack、Spotifyをはじめとする世界的サービスでも使われてるんです
StackOverflow
https://insights.stackoverflow.com/survey/2019#top-paying-technologies
上記資料の日本語紹介
https://news.mynavi.jp/article/20190415-807198/
今回、このElixirとNeosVRを繋いで、気軽にVR WebSocketプログラミングをしてみます
内容が、面白かったり、役に立ったら、「LGTM」よろしくお願いします
お知らせ:NeosVR reso Advent Calendar、2位達成ヽ(=´▽`=)ノ
今年初登場のNeosVR reso Advent Calendar、Webテクノロジーカテゴリで2位獲得です
https://qiita.com/advent-calendar/2020/neosvr_reso
なお、「NeosVR |> AR投影アプリ」で使っているElixirも、プログラミング言語カテゴリで2位でした
https://qiita.com/advent-calendar/2020/elixir
NeosVR |> WebSocket |> Elixirで何ができるの?
その1例として、NeosVR内のオブジェクト情報を、ARアプリに配信し、現実世界と重ね合わせる投影が可能になります
NeosVRとElixirの悪魔合体でVRと現実世界が繋げられる
NeosVRの良いところ
NeosVRは、LogiXというプログラミングツールを使って、HTTP GET/POSTやWebSocketをVR内でプログラミングできるため、NeosVRの外にあるAPIや、WebSocket対応しているシステムもしくは機器とカンタンに接続できます
これを使うと、NeosVRから様々な現実のサービスやインフラを利用したり、NeosVR内で起こったことや行ったアクションを現実世界に反映させたり、NeosVRで見ているVR風景を現実に投影させるといったことが可能です
更に、WebSocketであれば、現実で起こった出来事をセンサーやIoTで拾い、NeosVRの中に送り込んで処理することもできるので、現実で誰かが見ている風景をVR内で複数人で同時共有したり、MIDI楽器を打鍵したらNeosVR内で音として鳴らしてみんなで聞く、といったことも可能です
ただしLogiXは、ノードプログラミングであるため、データ処理や演算、認証処理といったインフラ構築やWebサービス開発で頻出するロジック開発にはあまり向いていないので、複雑な処理を行いたい場合は、外部にサーバを立てて、ラップさせた方がラクできます
Elixirの良いところ
Elixirは、DB接続するAPI開発(REST API)のような、現実のサービスやインフラを作ることが、コマンド1発で出来ますし、WebSocket開発も、「Channel」という機能を使えば、接続確認やサーバ構築といった、面倒なことを一切スッ飛ばし、受け取りたいデータ/流したいデータのことだけを考えて作れるので、一瞬で開発が完了します
更に、JSONやバイナリデータのハンドリング(パース、パターンマッチングによるデータ内容次第での処理分岐)や、それらデータそのものの加工や集計がメチャクチャ強いのも、データサイエンスに強いElixirならではの特徴です
そのため、他サービス/機器のAPIやWebSocketをラップし、Elixirで適切なデータ加工/集計を行うことで、NeosVRと他サービス/機器を繋ぐ際、そのどちらにも最適なデータ交換を提供できます
また、このラップ構成にした場合、DiscordやSlack、Spotifyでやっているような、大量アクセスを処理する負荷分散構成に近くなり、「リクエストの受付」と「本体処理」を分離できるようにもなるため、ユーザ急増やサービスグロースといった「嬉しい悲鳴」が起こっても、性能改善に追われたり、高額なクラウド費用を支払うこと無く、安全にスケールアウトしていくことができます
そんな2つを悪魔合体させるとどうなる?
端的に言えば、「VR空間」と「現実世界」を、超絶お手軽に繋げられるようになります
それによる恩恵は、今月後半のAdvent Calendarに詳しく書くので、ココではざっくり解説ですが、以下のような世界観が叶います
- 「VR」 → 「現実(+IoT)」 → 「VR」
- VRで起こるイベントや起こすアクションで現実にIoTなど経由で干渉し、その結果をセンシングして、VR改変を行う
- 「現実(+IoT)」 → 「VR+AI・ML」 → 「現実(+AR)」
- 現実で起こるイベントや起こすアクションをVR+AI・ML内で処理し、その結果に基づいて、サイネージや3Dプリンタなどによって現実改変を行う
これはまさに「Society 5.0」もしくは「インダストリー 4.0」の世界観ですね
Society 5.0 - 科学技術政策 - 内閣府
https://www8.cao.go.jp/cstp/society5_0/
インダストリー 4.0とは
https://seizo-net.com/industry40/
実際、どのような活用方法があるかは、下記コラムをご覧ください
「NeosVR |> AR投影アプリ」が拓く生活と仕事:人はより「現実」へと出ていき、世界が変わったことを理解する
https://qiita.com/piacerex/items/fcb29251e37b1ac3e063
その先に、どのような世界観があるかは、下記コラムをご覧ください
NeosVRに見出した可能性と未来について:「4つの世界」は「7つの世界」に
https://qiita.com/piacerex/items/7c29778e19e5b281f293
NeosVR+ElixirでVR WebSocketプログラミング
それでは、これら構想(妄想?)の基本となる、NeosVR+ElixirによるVR WebSocketプログラミングを行っていきましょう
まず、NeosVR側のWebSocket送信について、LogiXプログラミング解説を行い、その後、ElixirでのWebSocketサーバ構築の解説をします
NeosVR側の全体像
NeosVR側では、NeosVRオブジェクトの属性を取得して、それをJSON化し、WebSocket送信しています
思ったよりもロジックが少ないように見えますが、それは「BluePrint」というモジュール化を行うツールの影響です
ここから、各モジュールの中身を見ていきます
①:個々のオブジェクト属性をオブジェクト名付きJSON化する
オブジェクト属性を拾い出し、JSON化し、オブジェクト名を付与するところまでを「ObjectJsoner」で行っていて、中身は、「ObjectDecomposerOnChanged」と「XYZJSONBuilder」、「XYZWJSONBuilder」、「JSONBuilder」、「ObjectJSONBinder」で構成されています
①-1:オブジェクト属性のJSON化
最初は「ObjectDecomposerOnChanged」ですが、これはオブジェクトのPosition/Rotation/Scaleのいずれかに変化があった場合、Trueを返しつつ、変化があった場合のJSON構築のためのPosition/Rotation/Scaleの値を次に流しています
JSON構築のためのPosition/Scaleなどの3値は、「Float3UpdateChecker」で、前回変化時とのFloat3の比較を行い、変化していれば、Trueを「ObjectDecomposerOnChanged」に渡します
なおRotationは「FloatQUpdateChecker」で処理していますが、コチラはFloat3では無く、FloatQで比較を行います
①-2:オブジェクト属性のJSON化
オブジェクトから取り出したPosition/ScaleなどのXYZ座標値や、RotationのXYZWの4値は、「XYZJsonBuilder」によって、JSON化します
「ToJSON」でキー名と各値をJSONキーバリュー形式にします
ちなみにPosition/Scaleは3値なので、「XYZJsonBuilder」で処理しますが、Rotationは4値なので、「XYZWJsonBuilder」で処理していますが、要領はXYZJsonBuilderと同じです
①-3:JSON化されたオブジェクトPosition/Rotation/Scaleを連結
JSON化されたオブジェクトPosition/Rotation/Scaleは、カンマ区切りで連結する必要があるため、「JSONBuilder」で繋ぎ合わせます(幾つか空欄のテキスト入力を接続していますが、これはデバッグ用なので、気にしないでください)
①-4:オブジェクト属性JSONにオブジェクト名を付与
JSON化されたオブジェクトPosition/Rotation/Scaleの上位に、オブジェクト名(オブジェクトのSlotから取得)の付与を「ObjectJSONBinder」で行います
なお、VR |> AR投影アプリでは、オブジェクトのユニーク性を、オブジェクト名で行っている(もっと良いオブジェクトを一意に識別できる情報がNeosVRオブジェクトに存在していたら、ぜひ教えてください)ため、投影したいオブジェクトは、全て名前を変えておく必要があります
②:オブジェクト属性群を接続してWebSocket送信
複数のオブジェクト群の属性を1つのJSONに繋ぎ合わせ、WebSocket送信する「ObjectJsonSender」は、「ProjectorJsonBuilder」と「TimingReducer」、「WebSocketSend」で出来ています
②-1:複数のオブジェクト群の属性を1つのJSONに繋ぎ合わる
「ProjectorJsonBuilder」は、個々のオブジェクト属性を、1つのJSONに繋ぎ合わせ、その上位にくる「id」や「data」キー、それらに関するデリミタを付与し、デバッグしやすくするための改行も付与します
②-2:オブジェクト属性群JSONをWebSocket送信
ここまでで、送信するJSONは出来上がったので、最後に、WebSocketでこのJSONを送信する部分です
まずNeosVRでWebSocketを扱うには、何らかのオブジェクトに、「Attach Component」で「WebSocketClient」を追加してあげる必要があります
ここでは、下図右にあるコーン(三角錐)のオブジェクトに、「WebSocketClient」をアタッチしました
②-2-1:WebSocket接続、チャンネルjoin
その後、WebSocketClient配下の「IsConnected」にチェックを入れると、このオブジェクトからWebSocketでElixirに接続します
接続後は、「neos:logix」というWebSocketルーム(後述するElixir側でこのルームを構築します)に「phx_join」を投げるというjoin用パーツ「WebSocketSendJoin」を作ります
ここでWebSocket送信のために、「WebSocket Text Message Sender」というノードを使います
このパーツが出来上がったら、「Pulse」をクリックすれば、ルームにjoinできます
②-2-2:WebSocket送信
それから、送信するJSONをWebSocket送信するパーツ「WebSocketSend」を作りますが、ここでも「WebSocket Text Message Sender」を使います
なお、WebSocket送信間隔を「TimingReducer」内にある「Timer」で0.1秒間隔で定期送信するようなロジックになっており、FPSを意図的に抑制していますが、上記で「ObjectDecomposerOnChanged」によりオブジェクトのPosition/Rotation/Scaleが変わったときのみTrueを返す部分も作っているため、「オブジェクト変化があったら送信」で繋ぎ変えるとFPSを落とさずに連携するモードになります
もしくは、「0.1秒間隔経過、かつオブジェクト変化があったら送信」というような形にすると、ムダな通信量を削減できます
ここまででNeosVR側は完了で、オブジェクトの変化がElixir側に送信されるようになります
Elixir側
Elixir側は、非常にカンタンで、「Channel」という機能を使ってWebSocketサーバを立てて、NeosVRから受け取ったオブジェクト属性群をブロードキャストしているだけです
Phoenix PJ配下に生成済みの下記コードに、コメントアウトを解除するだけで、WebSocketサーバの構築は完了です
defmodule BasicWeb.UserSocket do
use Phoenix.Socket
## Channels
# channel "room:*", BasicWeb.RoomChannel
channel "neos:*", BasicWeb.NeosLogixChannel # <- radd here
…
WebSocket受信時の処理は、下記のようなコードを作成し、handle_in()でWebSocket受信時のアクションを書いてあげればOKです
defmodule BasicWeb.NeosLogixChannel do
use Phoenix.Channel
def join( "neos:logix", _message, socket ) do
{ :ok, socket }
end
def handle_in( "new_msg", %{ "body" => body }, socket ) do
broadcast!( socket, "new_msg", %{ body: body } )
{ :noreply, socket }
end
end
参考:VR |> AR投影アプリ
VR |> AR投影アプリは、この中では最もヘビィな造りをしていて、今回の本題から逸れるため、別の機会にシェアしたいと思いますが、概要だけお伝えすると、ARアプリ側で、現実を写すカメラ画像の上に、NeosVRから送られたオブジェクト属性群JSONを元に、ARオブジェクト生成/変更/消滅を行い、重ね合わせを行っているだけです
そのため、Elixirでのデータ加工等は特に行っておらず、NeosVRから受け取ったJSONをそのままElixirがブロードキャストし、それをVR |> AR投影アプリが受け取り、上記AR処理を行っています
VR |> AR投影アプリでどんなことが可能になるのか?
NeosVR内で作ったオブジェクトやワールドや、NeosVRに取り込んだCADデータを、任意の緯度/軽度に出現させ、それをARグラスやARコンタクトで見ることで、「現実空間へのデジタル建造」が誰にでも出来るようにします
これを応用して、航空機やドローンで撮影した街や家屋の点群データをNeosVRに取り込み、その土地に住む方も、これから移住を考えている方も、果ては遠く海外からも、NeosVR内で改造した街に実際に行き、街歩きしながらAR投影で自分の作った街を楽しめる … そんな未来の地方創生を、NeosVR+Elixirで叶えていく活動をしています
現時点では、VRとARを繋ぐテクノロジーがまだあまり発展していないため、それをイチ早く実現/体験できるNeosVR+Elixirには、とてつもなく大きな可能性があると見ています
いま作っているもの
オブジェクト群のPosition/Rotation/Scaleのリアルタイム反映と、テクスチャ/メッシュのプリロード、親子関係を持つオブジェクトの一斉同期までは出来ていて、下記が現在、開発中です
- オブジェクトテクスチャのNeosVRからVR |> AR投影アプリへのリアルタイム反映
- WebSocket経由の通信でバイナリも投げられるようにする
- 現時点は、NeosVRとVR |> AR投影アプリの両方にテクスチャを別々にインポートしている
- オブジェクトメッシュのNeosVRからVR |> AR投影アプリへのリアルタイム反映
- NeosVR側が「MeshX」という形式を使っているので、まず解析から
- 現時点は、NeosVRとVR |> AR投影アプリの両方にメッシュを別々にインポートしている
MeshXの攻略を一緒にやってくれる方、絶賛募集中です
最後に
VR進化の最先端である「NeosVR」と、プログラミング言語進化の最先端である「Elixir」の2つが悪魔合体させると、わりかし気軽に、「VR空間」と「現実世界」を繋げられることが伝われば幸いです
私自身、NeosVRでVRプログラミングに触れ始めて、まだ日も浅いため、NeosVR側の解説は拙いところも多いのですが、こうした活動にご興味あれば、下記の私のTwitter DMでご連絡ください
piacere の Twitter DM(リンク先の赤枠部分をクリックしてください)
https://twitter.com/piacere_ex
p.s.このコラムが、面白かったり、役に立ったら…
明日の記事は、@rhenium_vrcさんの「NeosFesta2を支えるダイナミックスポーンシステム」です