【Unity】機能と性能面でLineRendererとGLを比較しました

最近、このツイートでお知らせした空間OS的なものを開発しています。


まだ詳細は秘密ですが、その中では線の表現も入れていこうと思っています。

ただ、HoloLensアプリ開発は表現に気をつけないとすぐにカクついてしまうため、線を描画するとしてもなるべく負荷を少なくする必要があります。

そこでOpenGLの記述ベースで線や図形を描画できるGLと、LineRendererを調べて、どんなことができるのか、同じ表現をしたときの負荷の違いを調べることにしました。

機能について (1, 2, 3章)

大雑把ですが、線を描くときに変わるパラメータは、

・太さ、細さ

・色

・見た目

と思います。そこで、LineRendererとGLで描画する方法を整理しました。

性能について (4, 5章)

LineRendererとGLでなるべく同じ条件になるように線をたくさん描いて、パフォーマンスを比較しました。

また、HoloLensで実際に見て、カクツキがあるかを調べました。



======

目次

0. 基本的な考え方

1. 線の太さを調整

2. 線の色を変更

3. 線の見た目を変更

4. パフォーマンスの比較 (Unity Editor)

5. 実行時の負荷比較(HoloLens)

6. Tips

7. 終わりに

======

0. 基本的な考え方

LineRendererの場合

まずは設定です。

GameObject -> Create Emptyで空のGameobjectを生成し、Component -> LineRendererをアタッチします。

f:id:Takyu:20170627090713j:plain

するとこのようなピンク色の線が表示されます。

f:id:Takyu:20170627091011j:plain

Inspector部分にある様々な設定を調整することで、色々な線を描くことができます。


線を描くときは、Positionsの中にxyz座標を指定します。また、一つのLineRendererコンポーネントでは一つの線を描く、つまり一筆書きとなります。

そのため、例えばこのように軸とグラフの2種類の線を描く場合は、2つのLineRendererが必要になります。


f:id:Takyu:20170702151654p:plain

Inspectorまたはスクリプトで2種類の線を書こうとしても、このようになってしまいます。

f:id:Takyu:20170702152340p:plain

f:id:Takyu:20170702152358p:plain

指定する順番が変わると形が変わる可能性はありますが、いずれにしろ一筆書きとなるのは変わらなそうです。


GLの場合

前回のブログにも少し記載しましたが、コンポーネントは不要でスクリプトのみで描くことができます。

magicbullet.hatenablog.jp


書き方は以降で紹介の通りです。また、LineRendererとは異なり、始点と終点を繰り返し指定することで線を描きます。そのため、一つのスクリプトで一筆書きでない複数の線を描画することが可能です。

たとえば、今回の記事の「4,パフォーマンスの比較(Unity Editor)」では一つのスクリプトで10000本以上の線を描いています。

また、同じスクリプトの中で色の変更も可能です。

1. 線の太さを調整

1-1. LineRendererでの線の太さを調整

1-1-1. Inspectorから調整

Line Rendererで線の太さを調整する場合、このようにwidth部分を変更するか、その下のグラフ(keyframe)を調整します。

▪️Widthを変更する場合

▪️keyframeを変更する場合(こっちは線の途中の太さを変えるとか、より細かい調整をするときに使います)



1-1-2. スクリプトで調整

以下のように記載します。


gist81a778f1e4c853ea1284678717bd349d

widthを変更するだけならば、

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

途中にある

float lineWidth = 100.0f;

の値を大きくすると太くなり、小さくすると細くなります。


専用プラグインこちらです。(4年前で更新が止まっています。動作未確認です)

github.com

2. 線の色を変更

2-1. LineRendererで線の色を変更

2-1-1. Inspectorで調整

マテリアルで変更できます。マテリアルのShaderをStandardにして、Albedoを変更すると色が変わります。


Emissionを有効にすると鮮やかな色になります。おそらくHoloLensではこっちの方がよいと思います。

ただ、Emissionを有効にすると、Emissionの色が強いのでAlbedoの変化が見えづらくなります。


なお、Line RendererのColorというパラメータを変えても、色は変わりませんでした。

f:id:Takyu:20170701141316j:plain

この辺り調査が足りてないので、どこかでわかったら追記します。


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の変換は、例えば下記のサイトを使わせていただくと便利と思います。 ありがとうございます。

mementoo.info


ちなみに、

rn.material.SetColor("_Color",new Color(255,0,0,255));

のように指定すると、表示はされますがエラーが出ます。

f:id:Takyu:20170630100345p:plain

f:id:Takyu:20170630100438p:plain
(正確には、このエラーは出たり出なかったりします)

また、Emissionを使っていないのに0.0〜1.0fで指定するよりも明るくなります。

ただし、Unity公式リファレンスによると、Colorクラスの値は0.0f〜1.0fで指定すると描かれているので、それに倣うのがよいと思います。

docs.unity3d.com


参考までに0.0f〜1.0fで指定する場合の例です。

f:id:Takyu:20170630100755p:plain

非常に見づらいですが、SetColorで0-255で指定したものと同じ箇所に赤色で線が描かれています。


2-2. GLで線の色を変更

スクリプト

GL.Color (new Color (1.0f, 1.0f, 0, 0.8F));

のように指定します。複数の色を使いたい時は、GL.Colorを再度宣言すると色を変更できます。


gist48c0e20a1d33a48350c4170955b4582e

f:id:Takyu:20170701141837p:plain

ここでも2-1-2で紹介したように、0〜255の値を入れると同じ色でも鮮やかになります。こっちはエラーログがでないのですが、使い方として正しいかは不明です。

3. 線の見た目を変更

3-1. LineRendererで線の見た目を変更

マテリアルのテクスチャを変更することで実現できます。

こちらにわかりやすくまとまってましたので引用させていただきました。ありがとうございます。

aki-tk.hateblo.jp

たとえばこのような表現ができます。

f:id:Takyu:20170630201651p:plain

具体的には、LineRender のInspectorでParticle/Additiveにしたマテリアル(ここではLineTexture)を指定します。あとは、マテリアル側で好きなテクスチャを指定するだけです。

f:id:Takyu:20170630201846p:plain

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で星の数を変えたときの変化

f:id:Takyu:20170701173618j:plain

▪️GLで星の数を変えたときの変化

f:id:Takyu:20170701173645j:plain


LineRendererの方はなぜかうまく撮れなかったのですが、Editorで見たとき、HoloLensで見たときは線の数が増えると密度が上がっていました。

Batch、Tris、Vertsの用語

公式HPではこのように説明されています。

Batch


“Batching” とは、エンジンが、リソース切り替えの為に発生する CPU のオーバーヘッドを減らすために、複数オブジェクトをチャンクにし、まとめてレンダリングしようと試みるものです。

わかりづらいですが、まとめてレンダリングする個数のことと解釈しました。

Tris

描画する三角形の数

Verts

描画する頂点数

docs.unity3d.com

Batch、Tris、Vertsの比較

それぞれ値を記録してグラフにしました。横軸が星を描いた数、縦軸がそれぞれの値です。

▪️グラフ(Batch数推移)

f:id:Takyu:20170701175746j:plain

▪️グラフ(Tris数推移)

f:id:Takyu:20170701202148j:plain

▪️グラフ(Verts数推移)

f:id:Takyu:20170701202208j:plain


このように、BatchだけはLineRendererの方が変化なく低かったのですが、TrisとVertsは星の数が多くなるほど差がつきました。

だいたい1000個を超えると顕著に差がつきました。

実行スクリプト

それぞれこのようなスクリプトを書いています。

5. 実行時の負荷比較(HoloLens)

さらに、HoloLensで表示させてみました。HoloLensの場合、値よりも見た目でカクツキがあるかが重要なので、動画で比較しました。

▪️HoloLensでLineRendererを試した例(星の数は1000)

▪️HoloLensでGLを試した例(星の数は1000)


このように、LineRendererではカクツキが出ています。多くの線を描きたい場合はGLを使う方がよさそうです。

6.Tips

6-1. GL.LINESのOnRendererObject()はUpdate()のように繰り返し呼ばれる。

そのため、例えばこのようなスクリプトを空のGameObjectにアタッチすると、星が点滅するような表現ができます。


gistaf7080ae6c6aa27d895a1014004faa8a

工夫次第で何かに使えそうですね。

7.終わりに

今回はLineRendererとGLの基本的な使い方を紹介したものですが、この辺りが把握できると色々と応用が効くように思います。

また、HoloLensでの動作確認をしてみて、処理負荷はBatchesの値よりもポリゴン数の数が影響することがわかりました。

少量であれば問題になりませんが、頂点数が6000を超える線(今回の星は6つの頂点で1つでした)を出す必要がでてきたときは、LineRendererよりもGLの方が負荷が小さくなります。

とくに複数の色を使うときはマテリアルを複数生成することになるので、LineRendererの方が負荷が高くなっていく気がします。

一方、GLではできないこともありそうなので、状況に応じてLineRendererとGLを切り替えて使うのがよさそうです。


HoloLensでも線を表現できることがわかってきたので、新しく考えている空間OS的なものにうまく取り入れていければと考えています。