最近、このツイートでお知らせした空間OS的なものを開発しています。
空間OS的なものを作り始めました。空間にどうやって情報を表示して操作すればよいかを色々試してみようと思っています。 #HoloLens pic.twitter.com/ZKWgt1WnvQ
— Limes@XRDeveloper (@WheetTweet) 2017年6月26日
まだ詳細は秘密ですが、その中では線の表現も入れていこうと思っています。ただ、HoloLensアプリ開発は表現に気をつけないとすぐにカクついてしまうため、線を描画するとしてもなるべく負荷を少なくする必要があります。
そこでOpenGLの記述ベースで線や図形を描画できるGLと、LineRendererを調べて、どんなことができるのか、同じ表現をしたときの負荷の違いを調べることにしました。
大雑把ですが、線を描くときに変わるパラメータは、太さ・細さ、色、見た目と思います。1, 2, 3章では、LineRendererとGLの描画方法を書いています。
4, 5章では、パフォーマンスを比較しています。LineRendererとGLでなるべく同じ条件になるように線をたくさん描いて負荷を調べました。
また、HoloLensで実際に見て、カクツキがあるかを調べました。
0. 基本的な考え方
LineRendererの場合
まずは設定です。GameObject -> Create Emptyで空のGameobjectを生成し、Component -> LineRendererをアタッチします。
するとこのようなピンク色の線が表示されます。
Inspector部分にある様々な設定を調整することで、色々な線を描くことができます。線を描くときは、Positionsの中にxyz座標を指定します。また、一つのLineRendererコンポーネントでは一つの線を描く、つまり一筆書きとなります。
そのため、例えばこのように軸とグラフの2種類の線を描く場合は、2つのLineRendererが必要になります。
1つの1LineRendererで2種類の線を書こうとしても、このようになってしまいます。
指定する順番が変わると形が変わる可能性はありますが、いずれにしろ一筆書きとなるのは変わらなそうです。
GLの場合
前回のブログにも少し記載しましたが、コンポーネントは不要で、スクリプトのみで描くことができます。
UnityのGLを使って、HoloLensで線を描画してみる - CrossRoad
書き方は以降で紹介の通りです。また、LineRendererとは異なり、始点と終点を繰り返し指定することで線を描きます。そのため、一つのスクリプトで一筆書きでない複数の線を描画することが可能です。
たとえば、今回の記事の「4,パフォーマンスの比較(Unity Editor)」では一つのスクリプトで10000本以上の線を描いています。また、同じスクリプトの中で色の変更も可能です。
1. 線の太さを調整
1-1. LineRendererでの線の太さを調整
1-1-1. Inspectorから調整
Line Rendererで線の太さを調整する場合、このようにwidth部分を変更するか、その下のグラフ(keyframe)を調整します。
Widthを変更する場合
keyframeを変更する場合(こっちは線の途中の太さを変えるとか、より細かい調整をするときに使います)
1-1-2. スクリプトで調整
以下のように記載します。
gist81a778f1e4c853ea1284678717bd349d
widthを変更するだけならば、このようにSetWidthで設定します。
lr.SetWidth(0.2f,0.2f);
keyframeをスクリプトで調整する場合は、AnimationCurveを使います。
AnimationCurve curve = new AnimationCurve(); curve.AddKey(0.0f, 0.2f); curve.AddKey(0.2f, 0.2f); lr.widthCurve = curve;
このように、AnimationCurveクラスを使ってkeyの位置を指定すると、太さを詳細に変更できます。
1-2. GLでの線の太さを調整
GLでは太さを変更するような関数は用意されていないようです。細長い四角形を描くか、Unity社で開発された専用プラグインを使う方法があります。細長い四角形を描く方法はこのブログを参考にさせていただきました。ありがとうございます。
unity_script_opengl - FreeStyleWiki
このようなコードを書いて、空のGameObjectにアタッチします。
(長いのでこの記事には表示しておりません。リンク先よりご確認ください)
gist973944f16a75b7708e7b64aebe25a681
途中にあるlineWidthの値を大きくすると太くなり、小さくすると細くなります。
専用プラグインはこちらです。
GitHub - keijiro/unity-linewidth-plugin: Call glLineWidth from Unity (OS X)
ただし、4年前で更新が止まっています。動作未確認です。
2. 線の色を変更
2-1. LineRendererで線の色を変更
2-1-1. Inspectorで調整
マテリアルで変更できます。マテリアルのShaderをStandardにして、Albedoを変更すると色が変わります。
Emissionを有効にすると鮮やかな色になります。おそらくHoloLensではこっちの方がよいと思います。
ただ、Emissionを有効にすると、Emissionの色が強いのでAlbedoの変化が見えづらくなります。なお、Line RendererのColorというパラメータを変えても、色は変わりませんでした。
この辺り調査が足りてないので、どこかでわかったら追記します。
2-1-2. スクリプトで調整
スクリプトで色を変える場合、マテリアルからSetColorというパラメータで変更します。
(長いのでこの記事には表示しておりません。リンク先よりご確認ください)
gist993f5f00b15dd2ccfcb5e5c2ca6f0cb9
ここで、SetColorに表示する値は0-1.0fであることに注意してください。
つまり、
R : 0.0f〜1.0f : 0〜255
G : 0.0f〜1.0f : 0〜255
B : 0.0f〜1.0f : 0〜255
という関係があります。0〜1と0〜255の変換は、例えば下記のサイトを使わせていただくと便利と思います。 ありがとうございます。
ちなみに、下記のように指定すると、表示はされますがエラーが出ます。
rn.material.SetColor("_Color",new Color(255,0,0,255));
(正確には、このエラーは出たり出なかったりします)
また、Emissionを使っていないのに0.0〜1.0fで指定するよりも明るくなります。ただし、Unity公式リファレンスによると、Colorクラスの値は0.0f〜1.0fで指定すると描かれているので、それに倣うのがよいと思います。
UnityEngine.Color - Unity スクリプトリファレンス
参考までに0.0f〜1.0fで指定する場合の例です。
非常に見づらいですが、SetColorで0-255で指定したものと同じ箇所に赤色で線が描かれています。
2-2. GLで線の色を変更
スクリプトでGL.Colorを以下のように指定します。
GL.Color (new Color (1.0f, 1.0f, 0, 0.8F));
複数の色を使いたい時は、GL.Colorを再度宣言すると色を変更できます。
(長いのでこの記事には表示しておりません。リンク先よりご確認ください)
gist48c0e20a1d33a48350c4170955b4582e
ここでも2-1-2で紹介したように、0〜255の値を入れると同じ色でも鮮やかになります。こっちはエラーログがでないのですが、使い方として正しいかは不明です。
3. 線の見た目を変更
3-1. LineRendererで線の見た目を変更
マテリアルのテクスチャを変更することで実現できます。こちらにわかりやすくまとまってましたので、参考にさせていただきました。ありがとうございます。
たとえばこのような表現ができます。
具体的には、LineRender のInspectorでParticle/Additiveにしたマテリアル(ここではLineTexture)を指定します。あとは、マテリアル側で好きなテクスチャを指定するだけです。
3-2. GLで線の見た目を変更
GLを使うときのスクリプトのCreateLineMaterial()メソッドの中に、このような記述があります。
Shader shader = Shader.Find ("Hidden/Internal-Colored")
これを他のShaderに変えると見た目が変化しました。Shaderでテクスチャを指定すれば、テクスチャに応じた見た目の変化ができる可能性はあります。
しかし、先ほどのレーザーのような色合いはある程度の太さが必要ですが、GLでは標準で太くする機能がないので、少し難しいかもしれません。
4. パフォーマンスの比較
同じ数だけ、そして同じ色だけ線を描画して、batch数やVert数を比較してみました。LineRendererとGL.LINESの書き方が異なるため完全一致はしていませんが、ある程度近い形にはなったと思います。今回は線で星型を描き、描いた数を増やしながら、パフォーマンスを図る指標であるBatches、Tris、Vertsの数を調べました。
【手順】
- 空のGameObjectを2つ準備し、片方はLineRendererコンポーネントをつける。もう一方は、標準コンポーネントは何もつけない
- 多数の星と線を描くスクリプトを準備し、それぞれのGameObjectにアタッチする。
- 星を描いた数を1として、10、100、1000、5000、10000個の星と、間につなぐ線を描く。Unity Editorで実行したときのStatを見てパフォーマンスを記録
スクリーンショット一覧
- LineRendererで星の数を変えたときの変化
- GLで星の数を変えたときの変化
LineRendererの方はなぜかうまく撮れなかったのですが、Editorで見たとき、HoloLensで見たときは線の数が増えると密度が上がっていました。
Batch、Tris、Vertsの用語
公式HPではこのように説明されています。
Batch
“Batching” とは、エンジンが、リソース切り替えの為に発生する CPU のオーバーヘッドを減らすために、複数オブジェクトをチャンクにし、まとめてレンダリングしようと試みるものです。
引用元:レンダリング統計ウィンドウ - Unity マニュアル
わかりづらいですが、まとめてレンダリングする個数のことと解釈しました。
Tris、Verts
描画する三角形数および頂点数。
引用元:レンダリング統計ウィンドウ - Unity マニュアル
Batch、Tris、Vertsの比較
それぞれ値を記録してグラフにしました。横軸が星を描いた数、縦軸がそれぞれの値です。
▪️グラフ(Batch数推移)
▪️グラフ(Tris数推移)
▪️グラフ(Verts数推移)
このように、BatchだけはLineRendererの方が変化なく低かったのですが、TrisとVertsは星の数が多くなるほど差がつきました。だいたい1000個を超えると顕著に差がつきました。
実行スクリプト
それぞれこのようなスクリプトを書いています。
▪️LineRendererのスクリプト
(長いのでこの記事には表示しておりません。リンク先よりご確認ください)
giste2e8f3c61f1d6f3f28226e5453066f6b
▪️GLのスクリプト
(長いのでこの記事には表示しておりません。リンク先よりご確認ください)
gist70cec7bc14e6cbdb4e86a5e987003efa
5. 実行時の負荷比較(HoloLens)
さらに、HoloLensで表示させてみました。HoloLensの場合、値よりも見た目でカクツキがあるかが重要なので、動画で比較しました。
▪️HoloLensでLineRendererを試した例(星の数は1000)
▪️HoloLensでGLを試した例(星の数は1000)
このように、LineRendererではカクツキが出ています。多くの線を描きたい場合はGLを使う方がよさそうです。
6.Tips
GL.LINESのOnRendererObject()は、Update()のように繰り返し呼ばれます。そのため、例えばこのようなスクリプトを空のGameObjectにアタッチすると、星が点滅するような表現ができます。
(長いのでこの記事には表示しておりません。リンク先よりご確認ください)
gistaf7080ae6c6aa27d895a1014004faa8a
工夫次第で何かに使えそうですね。
7.終わりに
今回はLineRendererとGLの基本的な使い方を紹介したものですが、この辺りが把握できると色々と応用が効くように思います。また、HoloLensでの動作確認をしてみて、処理負荷はBatchesの値よりもポリゴン数の数が影響することがわかりました。少量であれば問題になりませんが、頂点数が6000を超える線(今回の星は6つの頂点で1つでした)を出す必要がでてきたときは、LineRendererよりもGLの方が負荷が小さくなります。とくに複数の色を使うときはマテリアルを複数生成することになるので、LineRendererの方が負荷が高くなっていく気がします。
一方、GLではできないこともありそうなので、状況に応じてLineRendererとGLを切り替えて使うのがよさそうです。HoloLensでも線を表現できることがわかってきたので、新しく考えている空間OS的なものにうまく取り入れていければと考えています。