シンプルなビルボードを実現する
About
3DCGでは簡単なオブジェクトをビルボード呼ばれる一枚の板で表現することが度々あります。例えば炎や雲を表現する場合などには良く用いられ、特にパーティクルによる表現とされるものは、ほとんどがビルボードの概念を採用しています。
ビルボードはカメラに対して常に正面を向くように回転する1枚の板です。裏面を描画することを考慮しないため、ふつうは表面のみから構成されるシンプルで透明な板です。常に表面が見えるという特性から、表現しやすい対象が、雲などの表面と裏面との形状差異が少ないものとなります。また一つ一つの描画コストが低いため、パーティクルのような"大量に生じる何か"から構成される現象や物体を表現することに利用されます。
- Unity_Billboard.zip
- Unity4.x
- 画像リソースの著作権については放棄していません。
ビルボードとなる平面板の用意
ビルボードを実現するには、垂直方向を向き、2つの三角形からなる1枚のシンプルなPlane(板)を用意するのが最も良いです。しかしながらUnity(ここではバージョン4)でそれを実現するには、ビルボード以外の内容を多分に説明する必要があるため、ここでは割愛することにします。その代わりに、次の手順で垂直方向を向いた透明な板を疑似的に用意します。
正面を常にカメラの方向へ向ける、という機能を実現することを目的としているため、立体的なオブジェクトを用意しても本質的には問題ありません。常にカメラの方向を向いている立体的なオブジェクトもこの記事の内容で実現することができます。
- [Create > Plane] から一枚のPlane(板)を生成する(ポリゴン数が冗長だがここでは妥協する)。
- 生成したPlaneのサイズを適切に変更する。UntiyのPlaneは10*10の平面から構成されるため、x0.1で通常の1に等しくなる。
- 生成したPlaneをX軸方向に90°回転して垂直にする。
- [Menu > GameObject > Create Empty]から空の[GameObject]を追加する。
- GameObjectの座標を(0,0,0)にしておく。
- 生成した空のGameObjectの子に垂直にしたPlaneが入るようにする。
こうすることで、疑似的にではありますが、垂直なPlaneをGameObjectとして用意することができます。GameObjectに90°回転したPlaneを入れることによって、実質的にRotateが0となる垂直のPlaneが得られたことになります。
最もシンプルなビルボードの実現
常にカメラの方向を向くように回転させるスクリプトを用意して、そのスクリプトを対象のオブジェクト(透明な板)に追加することで、ビルボードの効果を実現します。
ビルボードは常にカメラの方向を向くオブジェクト(透明な板)です。したがって、まずは対象のカメラを取得しておく必要があります。ここでは利便性を考慮してpublicなフィールド変数を設けていますが、指定されなかった場合を考慮して、シーンのMainCameraを初期化時に代入するようにしておきます。
ここでUpdateによる更新時にカメラの方向を向けばよいわけですが、厳密な方法は置いておくとして、単純に適用されたオブジェクトの向く先(正面方向)をカメラの座標にするだけで期待する効果を実現できます。
/// <summary> /// ビルボード処理の対象となるカメラを指定します。 /// </summary> public Camera targetCamera; /// <summary> // Use this for initialization /// </summary> void Start () { //対象のカメラが指定されない場合にはMainCameraを対象とします。 if (this.targetCamera == null) targetCamera = Camera.mainCamera; } /// <summary> // Update is called once per frame /// </summary> void Update () { //カメラの方向を向くようにする。 this.transform.LookAt(this.targetCamera.transform.position); }
結果と問題
サンプルに用意したScene1では、カカシのテクスチャを用意してビルボードとしました。結果画像が次の図になります。カメラは左ドラッグで360度方向に回転し、スクロールすることで近寄ったり離れたりすることができます。どの方向へカメラを回しても、カカシが常に正面を向くことを確認してください。
しかしながら問題があります。カカシや樹木などの上下方向に延びる物体は、その上から覗き込むように映すと、得られる効果が不自然になります。結果画像の左はカメラを左右方向へ移動したもので、画像の右は上下方向へ大きく移動したものです。カカシはふつう倒れませんから、この絵は至極不自然です。
雲や炎のテクスチャであれば、360度様々な方向へ向く動作は正しいのですが、この例のように、上下方向へは回転してほしくない、あるいは左右方向には回転してほしくない物体もあります。そこでこの問題を次の項目で解決します。
サンプルのScene1に用意したスクリプトには次の項目の内容が含まれています。
縦方向の回転の抑制
縦方向の回転を抑制するには、正面方向となるカメラのY座標を、自身のY座標と同じ値にします。Updateメソッドでカメラの方向を向くように設定していますが、このとき、Y座標を変更していることに注目してください。
また、ここでは回転してほしい場合とそうでない場合とがあることを考慮して、新たにpublicなフィールド変数"enableVerticalRotation"を設けています。初期値をtrueとして、falseの時には縦方向の回転を抑制するようにします。カメラで回転させたくないオブジェクトにスクリプトを追加した時は、[Inspectorビュー]からチェックを外します。
/// <summary> /// ビルボード処理の対象となるカメラを指定します。 /// </summary> public Camera targetCamera; /// <summary> /// 縦方向への回転を制御します。 /// </summary> public bool enableVerticalRotation = true; /// <summary> // Use this for initialization /// </summary> void Start () { //対象のカメラが指定されない場合にはMainCameraを対象とします。 if (this.targetCamera == null) targetCamera = Camera.mainCamera; } /// <summary> // Update is called once per frame /// </summary> void Update () { //上下方向へは回転しないように抑制する場合。 if (enableVerticalRotation) { this.transform.LookAt(this.targetCamera.transform.position); } else { Vector3 target = this.targetCamera.transform.position; target.y = this.transform.position.y; this.transform.LookAt(target); } }
複数のビルボードがあるシーン
縦方向に回転するビルボードと、水平方向のみ回転するビルボードが混在するような、複数のビルボードからなるシーンをサンプルとして用意しました。サンプルプロジェクトの"Scene2"になります。
用意した"草のビルボード"は縦方向にも回転し、"カカシのビルボード"は引き続き水平方向にのみ回転します。
草のビルボードの縦方向の回転を抑制すると、次のような結果になります。好みの問題や表現する対象によって良し悪しは変化しますが、草の場合には回転させた方がそれっぽいです。
(補足)ビルボードの実装について
ここでは恐らく最も簡単なビルボードの実装について言及していますが、例えばカメラの方向を向く角度を度数などの値で制御するなどするスクリプトを実装して、ビルボードの見栄えをより良くすることなどが考えられます。一方で、著しく大量発生させる場合や計算コストの都合で、今回のようなシンプルな実装をすることもあるでしょうから、適切な実装方法を選択する必要があります。
ビルボードとするポリゴンについても検証する必要があります。少なくとも、サンプルのような草を表現する場合には、多数の頂点から構成される板ポリゴンは利用しません。3角形が2つ繋がって4角形となる、シンプルな板を採用するのが一般的でしょう。一方で複数の光源や角度から照らし出される物体、サンプルのカカシのような特徴的な物体は、少しばかり多いポリゴン数を採用しても良いかもしれません。より光源の影響の変化を受けやすくなるためです。配置する数などを考慮しつつ、適切な板ポリゴン、あるいはオブジェクトを選択します。