CrossRoad

XRを中心とした技術ブログ。 Check also "English" category.

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

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

まだ詳細は秘密ですが、その中では線の表現も入れていこうと思っています。ただ、HoloLensアプリ開発は表現に気をつけないとすぐにカクついてしまうため、線を描画するとしてもなるべく負荷を少なくする必要があります。

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

大雑把ですが、線を描くときに変わるパラメータは、太さ・細さ、色、見た目と思います。1, 2, 3章では、LineRendererとGLの描画方法を書いています。

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

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




0. 基本的な考え方

LineRendererの場合

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

Line Rendererコンポーネントのアタッチ手順

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

Line Rendererコンポーネントの設定直後の画面

Inspector部分にある様々な設定を調整することで、色々な線を描くことができます。線を描くときは、Positionsの中にxyz座標を指定します。また、一つのLineRendererコンポーネントでは一つの線を描く、つまり一筆書きとなります。

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

LineRendererコンポーネントを2つ使ってグラフを書いた例

1つの1LineRendererで2種類の線を書こうとしても、このようになってしまいます。

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というパラメータを変えても、色は変わりませんでした。

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

RGBの値から色を確認するツール - めめんと

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

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

Line Rendererで線の色を赤色に変更した例

Line RendererのSetColorで色を変更したときのエラーログ
(正確には、このエラーは出たり出なかったりします)

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

UnityEngine.Color - Unity スクリプトリファレンス

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

Line Rendererのパラメータを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

f:id:Takyu:20180709185357j:plain

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



3. 線の見た目を変更

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

マテリアルのテクスチャを変更することで実現できます。こちらにわかりやすくまとまってましたので、参考にさせていただきました。ありがとうございます。

レーザー光線を作る【Unity】 - Akitk-Labo

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

Line Rendererによるレーザ表現の例

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

Line Rendererでレーザ表現をするときの設定画面

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

LineRendererで星の数を変えたときの変化

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

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

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

Batch、Tris、Vertsの用語

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

Batch

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

引用元:レンダリング統計ウィンドウ - Unity マニュアル

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

Tris、Verts

描画する三角形数および頂点数。

引用元:レンダリング統計ウィンドウ - Unity マニュアル

Batch、Tris、Vertsの比較

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

▪️グラフ(Batch数推移)
グラフ(Batch数推移)

▪️グラフ(Tris数推移)
グラフ(Tris数推移)

▪️グラフ(Verts数推移)
グラフ(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的なものにうまく取り入れていければと考えています。