ホワイトボードで線がヌルヌル描ける仕組み
初めまして!バーチャルキャストクライアント開発に携わっている江口です。
今回はバーチャルキャストのプリセットアイテム「ホワイトボード」で線がヌルヌル描ける仕組みを紹介したいと思います。他の記事よりも初歩的な内容になりますが、私自身まだまだ Unity 初心者ですのでどうぞお手柔らかにお願いします(汗)
1年以上前からバーチャルキャストを遊んでいただいている方は、この一年でホワイトボードの挙動にいくつか大きな変更があったことを覚えておられるかもしれません。
日付 | バージョン | 変更内容 |
---|---|---|
2019年4月11日 | Ver1.5.4a | ホワイトボードの描画が滑らかになった |
2019年8月29日 | Ver1.7.2a | ホワイトボードの線が途切れないようになった |
この記事ではこの二つの改善点に注目していきます。
フレームの狭間
バーチャルキャストはゲームエンジンである Unity をベースに開発されています。ゲームエンジンを使用している以上、バーチャルキャスト内のあらゆる現象は一定の間隔 (フレーム) に区切られて演算・描画されることになります。VR ゲームであるバーチャルキャストは使用している VR 機器とパソコンのスペックに応じて毎秒約75~90フレーム (FPS) で動作しています。家庭用ゲーム機は通常 60FPS で動作していますので、それと比べると高いと感じられるかもしれません。しかし、現実世界の人間はこのフレームとフレームの間にも動き続けています。バーチャルキャストはその間に生じた人間の動きを認識することができません。初期のホワイトボードで素早くペンを動かすと線が点々になってしまっていたのは、この「狭間」が顕著に表れていた例と言えます。FPS が90の場合、一秒間に90個の点しか打つことができません。これではペンをちょっと速く動かしただけで線が途切れてしまいます。
この問題を解決するため、Ver1.5.4a ではフレームとフレームの間で起きたであろう移動を補う「補間」機能が実装されました。
狭間を補間する
仕組みとしては非常にシンプルなもので、Unity に標準で用意されている線形補間メソッド Vector2.Lerp を使用しています。
Vector2 Lerp (Vector2 a, Vector2 b, float t)
a に始点の座標、b に終点の座標、t に 線分 ab 間の割合 (0.0~1.0の間の値) を渡してあげれば、t における座標が返ってきます。例えば、ホワイトボードによって認識された2つの点、(0, 0) と (1, 1) を a と b に渡し、t に 0.5 を渡せば2つの点のちょうどど真ん中の座標 (0.5, 0.5)を得ることができます。あとは、t に渡す値を 0.1、0.2、0.3、0.4 ... 0.8、0.9、1.0 と少しずつ変えていけば始点と終点を滑らかにつなぐ一連の座標を得ることができます。それらの座標にも色を描画していくことでバーチャルキャストが認識できなかったフレームとフレームの間の手の動きを再現することができるのです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var lerpCount = 10; //補間する回数。この例では10回補間 for (var i = 1; i <= lerpCount; i++) { //t に入れる割合値を "現在の回数/合計回数" で出す var lerpWeight = (float)i / lerpCount; //始点、終点、割合を渡して補完する座標を算出 var lerpPosition = Vector2.Lerp(startCoordinate, endCoordinate, lerpWeight); //Lerpはブラシサイズの補間にも使用しています。ブラシサイズは1次元の値なので Mathf.Lerp を使用します var lerpSize = Mathf.Lerp(startSize, endSize, lerpWeight); //lerpSizeの大きさの点をlerpPositionに描画 Paint(lerpPosition, lerpSize); } |
シンプルな仕組みではありますが、滑らかにヌルヌルと線が描けると気持ち良いものです。Ver 1.5.4a では t の値の細かさをクオリティ設定に応じて変更していました。クオリティ設定が低の場合は5分割、中の場合は10分割、高の場合は15分割です。こうしたのは、ホワイトボードに点を書くという処理はそれなりに負荷のかかるものなので、低スペックな PC の場合、複数人が同時にペンを使用するとゲーム全体の FPS が落ちてしまうということがあったためです。このような仕組みのため、複数のプレイヤーが同じホワイトボードを見ていても、各プレイヤーのクオリティ設定に応じてそれぞれの PC 上で滑らかさの異なる線が描画されていました。プレイヤーの皆様、お気づきでしたでしょうか?
もっと滑らかかつ効率的に
もっとも、この実装には限界がありました。ペンを剣のように素早く降った場合、補完密度が足らず、線が途切れてしまうのです。
また、逆にペンを少ししか動かしていない時でも、無駄に多く点を描画してしまい、リソースを無駄遣いしているという面もありました。そこで、Ver 1.7.2a では補間回数の動的な変更が実装されました。つまり、ペンをゆっくり動かした場合は補間回数を少なく、ペンを素早く動かした場合は補間回数を多くする仕組みです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var pointDensity = 2.5f; //補間密度をいい感じにするパラメーター var lineLength = Vector2.Distance(startPoint, endPoint); //始点と終点の間の長さを求める var averageRadius = (startSize + endSize) / 4f; //ブラシサイズ(直径値)の平均半径を求める //補間密度パラメーターを使用して補間点間の距離を求める var interPointsDistance = averageRadius / pointDensity; //線の長さを補間点間の距離で割って補間回数を求める。CeliToIntは小数点以下を切り上げます。 var lerpCount = Mathf.CeilToInt(lineLength / interPointsDistance); //この lerpCount を先述のコードに渡せば滑らかな線が描けます |
この実装により、ペンを動かした距離に応じて丁度よい回数の補間が入るので描画リソースが無駄にならず、かつ素早く動かしたフレームは何十回も補間して滑らかに描画できるようになりました。
まとめ
こうしてみるとVRで一般的な 90FPS は本格的にお絵かきをするにはちょっと物足りないフレームレートなのではないかと感じました。ちなみに、某人気メーカーのペンタブレットは1秒間に200回位置情報を取得しているそうです。ペンタブレットの何十倍もの距離を扱うVRではそれ以上の取得回数が必要なのかもしれませんね。今後のVR機器の進化がとても楽しみです。
- Tag
- Unity