Houdiniやってる

最近、HoudiniというCGソフトに入門している。

twitter.com

twitter.com

twitter.com

twitter.com

業務で水のシミュレーションをする機会があったんだけど、どうやらHoudiniって奴を使うと高品質なシミュレーションができそうということで、今月からHoudini入門することになった。 基本的なところ k2 に教えてもらいつつ、土日も色々さわって遊んでみている。

感想

あんまり体系的に学んでないけど、特徴としては以下のような感じ。

  • プロシージャルな表現が得意
  • 物理シミュレーションがすごい
  • プログラマーフレンドリー

Houdiniでは、基本的なジオメトリーにノードを繋げていって、動きや形状を操作していく。 ので、ある3Dモデルをグリッドに大量に並べたり、ノイズで動かしたり、更にパーティクルだしたり、みたいな事がボタンポチポチするだけで出来る。

TouchDesignerというリアルタイムパフォーマンス向けソフトがあるが、これは実はHoudiniのforkらしく、基本的な操作がよく似ている。 僕は去年からTouchDesignerをちょいちょい使っていたので、勘で操作したら思い通りに動いてくれてとても助かった。

「Houdiniはプログラマーフレンドリー」ということもよく言われている。 これはスクリプトで頂点やattributeを制御できるというのもあるけど、なんというか、勘ビリティ……勘で操作していい感じに動くっていう所が大きい気がする。 例えば、オブジェクトの位置の入力欄に勘で sin($FF) と入れたらサイン波の動きになったりして、コード書ける人なら適当にいじったら動く。

CG/シミュレーションを学びたい

先日MTRL KYOTOで行われたSPEKTRAのイベントで、ゲストの比嘉さんが「CGを学ぶためにHoudiniを学んだ」と言っていた。 僕も最近まさにCGをちゃんと勉強したいと思っていたので、今回仕事で触らせてもらえてラッキーだと思ってる。

僕はシェーダーからグラフィックスの世界に入って、初歩的なシェーディングやレンダリングパイプラインはなんとなくわかってきたけど、 ゲームCGや、HoudiniやC4Dのようないわゆるプリレンダ系のグラフィックスを見ると、一度ちゃんとグラフィックスを学ばないといけないな~と思っていた所だった。

Houdiniは物理シミュレーションに力を入れていて、水流やパーティクル、炎や煙、布などのリアルなシミュレーションがボタンぽち~で出来る。 別にシミュレーション手法の中身に詳しくなれるわけではないけど、現代のCGのトピックをザッとさらうことができる。 ハリウッドでも使われてる今時のCGってこんな感じなのか~。

公式チュートリアル動画見てると「シニアマセマティシャン」って肩書の人が出てきてビビる。アカデミック~

今回水のシミュレーションを行うにあたって、HoudiniやNVIDIAのライブラリがどういうアルゴリズムを採用してるのか調べていくと、現代の最先端の手法と最新のPCでも、リアルタイムできれいなシミュレーションをするのはまだまだ難しいことがわかってきた。 僕はゲームをやらないので、なんとなく「今時のゲームCGはメチャクチャ高品質だし、リアルタイムシミュレーションもサクサクなんやろな~」と思っていたんだけど、認識の甘さを痛感させられた。 しかし、同時にシミュレーションの世界の奥深さを垣間見ることができて面白かった。

(めっちゃ勉強したらリアルタイムFLIPソルバー実装できないかな……)


なんかオススメのチュートリアルとかあったら教えてください~~

Kotlinで音声認識 + Nearby APIでオフライン通信するアプリ作った

クライアントに納品するプロジェクトで、タブレット/スマホ連動の音声認識アプリを作ることになった。 仕組みはこんな感じ。

f:id:amagitakayosi:20190121121614p:plain
アプリの仕組み

特定の単語を認識して、両デバイスでコンテンツを再生する、というヤツ。 音声認識タブレットでやっていて、単語を認識したらスマートフォンにイベントを送信している。

当初はWeb技術で実現しようとしていた。

PCでいい感じに動くところまではサクッと作れたんだけど、Android実機で動作確認した所、音声認識開始の「ピコン♪」という音が無限になり続ける事態に遭遇した。 まさにこの状況↓

ブラウザでマイク入力から書き起こしを行うツールを作った - mizchi's blog

Androidで開いたら無限にポホロンポロロン音が出続けていた

2019/01/20 23:33
b.hatena.ne.jp

てな感じで困っていたけど、試しにAndroid Studioで検証してみたら意外とサクッとできそうだったので、ラスト1週間でAndroidアプリを作ってみることにした。

音声認識

Web Speech API

Web Speech APIは、SpeechRecognition音声認識)と SpeechSynthesis音声合成)からなる、ブラウザのAPIだ。

developer.mozilla.org

SpeechRecognitionを使うと、ブラウザ上で音声認識して文字列に起こせる。 多言語対応もしている。 音声認識ライブラリで日本語対応してるものって全然無いんだけど、Web Speech APIなら何も考えずに使えて便利。

現状動くのはChromeのみ。 裏ではGoogle Cloud Speech-to-Textを呼んでるらしい。 あっちは利用回数に応じて料金がかかるんだけど、Web Speech APIだと無料だし回数制限も特に無い。 オフラインでも使えるけど、オンラインのときに比べるとかなり精度が落ちるのは、GoogleAPIを叩けないからだと思う。

Web Speech APIは使い方も異常に手軽で、関数を呼んだらコールバックに認識結果が返ってくる。 MediaStream系のAPI使ったことある人なら一瞬で使えると思う。

大抵どんな言葉も認識してくれるし、認識したい単語を指定する事もできる。 特定の単語をコマンドとして使いたいときに便利だ。

試しに、じゃんけんアプリを作ってみると、こんな感じになる↓

// じゃんけんデータ
const hands = ['グー', 'チョキ', 'パー'];
const winnerOf = {
  'グー': 'パー',
  'チョキ': 'グー',
  'パー': 'チョキ',
};

// 音声認識の初期化
const recognition = new SpeechRecognition();
recognition.lang = 'ja-JP';

// 認識する単語を指定
const speechRecognitionList = new SpeechGrammarList();
speechRecognitionList.addFromString(
  '#JSGF V1.0; grammar colors; public <color> = グー | チョキ | パー ;', 
  1
);
recognition.grammars = speechRecognitionList;

// 認識結果が返ってきた時の処理
recognition.onresult = (e) => {
  const yourHand = e.results[0][0].transcript;

  // CPU側の手をランダムに決定
  const myHand = hands[(Math.random() * 3) | 0];

  // 勝敗を判定
  if (yourHand === winnerOf[myHand]) {
    console.log('あなたの勝ち!');
  }
  else {
    console.log('あなたの負け!');
  }
};

// 認識開始
recognition.start(); 

最近だと、 id:mizchi がWeb Speech APIで文字起こしツール作ってバズってた。

mizchi.hatenablog.com

問題は、Android端末でこのAPIを呼ぶと、「ポポン」という通知音が鳴ってしまうことだ。 今のところこの通知音を消す方法は無いみたい。 root取得すれば回避できるらしいけど、仕事でやるのはちょっと……。

ただ、調べている過程で、どうやらAndroidAPIを叩けば通知音を消せるらしいことがわかった。 という訳で、今回はAndroid標準の音声認識APIを使うことにした。

Android.SpeechRecognizer

Androidには、OS標準で音声認識APIが用意されている。 Android 2.2 (API Level 8) から使えたらしい。

こちらもWeb Speech APIと同じく、裏ではGoogleAPIを呼んでいそう(推測だが)。 オフラインでも利用できるが、やはり精度はガクッと落ちる。

僕がAndroid開発に慣れていないせいか、APIの利用方法が少し複雑だと感じたが、Javaに慣れてくるとこんなもんなのかもしれない。

利用事例をググると、すぐに今回と同じケースのブログを見つけた。

kivantium.hateblo.jp

今回のアプリでは、基本的には↑の記事とほぼ同じ要領で音声認識APIを利用している。 ただ、エラー処理だけ少し気をつける必要があった。

具体的には、エラーコードが ERROR_RECOGNIZER_BUSY だった場合に即リスタートしてしまうと、無限にエラーが出まくった後に音声認識APIが全く成功しなくなり、端末を再起動するまで直らないという事があった。 原因は不明。もしかしたら端末側の問題だったりするのかなあ……。 今回は ERROR_RECOGNIZER_BUSY の時だけタイムアウトを入れることで対処した。

override fun onError(error: Int) {
    val delay = if (error == SpeechRecognizer.ERROR_RECOGNIZER_BUSY) 1000L else 100L

    val handler = Handler { restartRecognition(); true }
    Timer("restartRecognition", true).schedule(delay) {
        handler.obtainMessage().sendToTarget()
    }
}

また、今回はオフライン環境だったのと、認識したい単語が発音しづらいものだったため、普通に使うだけでは期待する認識精度に達しなかった。 例えば、「花」という単語を認識したいけど、認識結果は「棚」「穴」などが返ってきてしまう。 このような、発音の近くて誤認識されやすい単語も認識候補に追加することで、認識がうまく行かなくてもちゃんと動作するようになった。

Nearby Connections API

バイス間の通信にはNearby Connections APIを使用した。 タブレット側で音声認識を行い、特定の単語を検出すると、スマートフォン側にイベントを送り、2つのデバイスで同時にアクションを起こす。

developers.google.com

Nearby Connections APIは、オフライン状態の機器間でデータをやりとり出来るAPIGoogleのNearbyプロジェクトの一環らしい。 Nearbyプロジェクトには他にも2つAPIがあるので、ドキュメントを参照する際に間違えないよう注意。

  • Nearby Connections API: オフライン機器間の通信
  • Nearby Messages API: インターネットを介したメッセージング
  • Fast Pair: Bluetooth Low Energyで機器を同期するためのシステム

今回はインターネットに接続していないデバイスを用いるので、Nearby Connections APIを利用した。 一応Nearby Messages APIも一応試して見たけど、それなりに速い回線でも10sec以上ラグが出たりするので、今回の用途には向いていないと判断した。

Nearby Connections APIは、内部的にはWiFi DirectとBluetoothを組み合わせて機器間の通信を実現しているが、ユーザーは仕組みを意識する必要がない。 今回はごく短い文字列を送ったけど、結構大きめのファイルも送受信できるみたい。

インストールは、build.gradleに以下の行を追加するだけ。 APIキーの発行とかも不要。

implementation 'com.google.android.gms:play-services-nearby:16.0.0'

あとは、メッセージの受信や接続時のコールバックを書いてあげて接続するだけでよい。

// 接続時の処理を書く
val connectionCallback = object: ConnectionLifecycleCallback() { ... }

// メッセージ受信した時の処理を書く
val payloadCallback = object: PayloadCallback() { ... }

// トポロジー設定。スター型とクラスター型を選べる
val advertisingOptions = AdvertisingOptions.Builder().setStrategy(Strategy.P2P_STAR).build()

 mConnection
    .startAdvertising("hostNickname", "projectId", connectionCallback,  advertiseOptions)
    .addOnSuccessListener { /* 起動成功時の処理 */ }
    .addOnFailureListener { /* 失敗時の処理 */ }

このように便利なAPIだけど、欠点もいくつかある。

まず、たまにメッセージの取りこぼしがある。 メッセージによって状態遷移するようなアプリの場合、何もメッセージが無い時は一定の時間で自動的にデフォルト状態に戻るようにする必要がある。

また、なぜか接続に失敗することがある。 接続が切れたら自動で再接続するようにしたけど、同時エラーが出続けて、最悪の場合は端末を再起動するまで直らなかった。 一度接続してしまえば滅多に切れないので、アプリを起動した時さえ気をつければ大丈夫なんだけどね……。

オフラインで気軽に通信できて面白いので、興味のある人はゲームとか作ってみてください。

Androidについて

今回初めてAndroidアプリを作ったんだけど、Android Studioがメチャクチャ良くできてるし、Kotlinも書きやすかった。

Kotlin、便利ですね。 言語的にはScalaにめっちゃ似てる。case class的な物があったり、コンパニオンオブジェクトがあったり、valとvarの扱いが全く同じだったり。JVM言語だから当然か……。 僕はJavaはほとんど書いたことが無いんだけど、前職でScalaを書いていたので、Kotlinはほぼノーコストで書けた。

あと、Android開発の基本的な事をググると、古のジャバに関する情報や、怪しいSE塾みたいなページがたくさん出てきて困るんだけど、検索ワードに「Kotlin」を入れるだけで、ある程度信頼できる情報が出てくるのも便利だった。

とはいえ、UIはほぼモック画像を貼り付けただけだし、アプリ全体の設計をどうしたら良いか全然わからなかった。 次アプリやるときはその辺ちゃんと勉強したいな〜〜


今回のプロジェクトでは技術スタックは完全に僕に任せられており、短い納期でもいろいろ試行錯誤できたのはチャレンジングでとても楽しかった。 こんな感じでいろんな分野をつまみ食いして技術の幅を広げていけると良いな。

参考URL