HALF-LIFE2 LOG

2005-12-28

[][]リーフの可視性とパフォーマンス向上について

リーフに関する処理手順

マップの可視性に関するコンパイルは、以下の手順で行われます。ここで出てくるリーフ、リーフの可視性については後述しますが、まずここでは処理の流れを見てください。

  1. vbsp(マップを作成、マップをリーフに分割)
  2. vvis(リーフ間の可視性を計算し、見える見えないを判断)
  3. ゲーム中(プレイヤーがいるリーフから可視と判断されるリーフを全て描画する)

リーフとは

vbspではマップをゲーム用のデータに変換すると同時に、ワールドブラシに沿って、空間をリーフというエリアに分割します。このリーフはワールドブラシによって区切られた凹みのないエリアです。

リーフはワールドブラシの形に沿って分割されるため、複雑なワールドブラシがあれば、それに従ってリーフも複雑に分割されてしまいます。この細部化を回避するためには、後述のfunc_detail等を使います。


リーフの可視性とは

ゲーム内での描画の可否の判断は、プレイヤーに見えるか見えないかではなく、あるリーフから他のリーフが見えるか見えないか、で判断されます。図上で見れば、あるリーフから他のリーフに対して、遮蔽物を横切らずに1本でも直線が引ける場合は、その対象リーフ全体が「見える」と判断されます。そのため、プレイヤーから実際には絶対に見えない領域でも、リーフの切り方によっては見えると判断され描画されてしまうことがあり、描画パフォーマンスを低減させる原因となります。

この見える見えないの判断をリーフの可視性と呼ぶことにします。


リーフの可視性のコンパイル

リーフの可視性はコンパイル時にvvisによって計算されます。リーフが細分化されれば、それだけ描画される物は少なくなりますが、リーフが多くなれば当然コンパイル時間は増えていきます。コンパイルに時間がかかるのは、ほとんどの場合がこのリーフ計算の過負荷のためです。

逆にリーフが少なければ計算時間は減りますが、場合によっては必要な遮蔽ができていなくてゲーム中のパフォーマンスを低下させてしまうことがあります。しかし的確なリーフの分割によって、コンパイル時間も描画パフォーマンスも、ある程度までは共に向上させることができます。


リーフ分割するもの

vbspでリーフを分割する際に、分割の基準になるものは基本的に「ワールドブラシ」です。一部の特殊なtools系のテクスチャを張ったブラシ以外は、リーフ分割の基準となります。次の項に示す遮蔽物の条件とは多少異なりますのでご注意ください。以下に簡単にその条件を示します。

  • リーフ分割の基準となるもの:ワールドブラシ
    • エンティティ(func_detail等)に結び付けられているものではリーフ分割しない
    • 一般的な透過テクスチャではリーフ分割される
    • triiger, skip, playerclipなどのtools系テクスチャではリーフ分割しない
    • invisible, nodrawテクスチャではリーフ分割する
    • hintテクスチャ面ではリーフ分割する
    • ディスプレースメントではリーフ分割しない
    • ブラシではないエンティティ(prop_staticなど)ではリーフ分割しない

prop_staticなどのエンティティやfunc_detailなどのエンティティに結びつけたブラシ、そしてディスプレースメントではリーフ分割をしませんのでご注意ください。強制的にリーフ分割をしたい場合には、hintテクスチャを使います。hintテクスチャについては後の項で簡単に説明しています。


遮蔽物となるもの

vvisでのリーフの可視性判断をする際に、遮蔽物として判断されるものとされないものは、基本的に「ワールドブラシ」です。ただし貼り付けられるテクスチャなどによって、遮蔽物と判断されたりそうでなかったりします。リーフ分割の条件とは多少異なりますのでご注意ください。以下に簡単にその条件を示します。

  • 遮蔽物として判断されるもの:ワールドブラシ
    • エンティティ(func_detail等)に結び付けられているものは遮蔽物とみなされない
    • 透過テクスチャが張られているものは遮蔽物とみなされない
    • ディスプレースメントは遮蔽物とみなされない
    • "tools/nodraw"テクスチャは遮蔽物とみなされる
    • 他のtools系テクスチャは基本的に遮蔽物とみなされない
    • ブラシではないエンティティ(prop_staticなど)は遮蔽物とみなされない

これだけです。逆に遮蔽物として判断されないものはそれ以外全て、ということになりますが、一部の代表的なものを列記しておきます。

  • エンティティに結び付けられたブラシ(func_detail, func_brushなど)
  • prop_static, prop_dynamicなどのエンティティ
  • ディスプレースメント
  • 透過テクスチャを張ったワールドブラシ

ここで注意が必要なのは、prop_staticやディスプレースメント。これらは一見エリアを遮蔽しているように見えますが、コンパイラ上では遮蔽物として判断されていません。例えば窓やドアなどのprop_staticを使って壁を塞いでいる場合、そこは「穴」として認識されます。

またディスプレースメントで囲んだ洞窟などは、まったく遮蔽されていないのと同じ状態です。床面にディスプレースメントを使っている場合、壁で遮られた向こう側が地面の下で見えているために遮蔽されないということもあります。遮蔽を行いたい場合にはディスプレースメントやプロップの中にnodrawのワールドブラシを仕込むなどの作業が必要となります。


func_detail

func_detailは遮蔽物として判断されたくないワールドブラシがある場合に使用します。たとえば細い柱、固定された箱など、視界を遮蔽しない物、遮蔽するにしてもそれほど効果がない物に対して使うことにより、リーフの分割を抑え、コンパイル時間を速める効果があります。

また視界を遮蔽するものでも、階段やカーブした壁など、リーフを多数に分割してしまうワールドブラシの場合には、func_detailを工夫してリーフの数を減らすことができます。

ただし、何でもかんでもfunc_detailにすれば良いというものでもありません。前述した通り、func_detailは遮蔽物として判断されないため、普通の壁などをfunc_detailにしてしまうと見えないはずのところまで描画されてしまい、ゲーム中のパフォーマンスを低下させます。また、ワールドブラシの一部だけをfunc_detailにしてしまうと、そこが「穴」として認識され、逆にリーフを増やしてしまうことがあり、コンパイル時間も描画パフォーマンスも共に低下させてしまいます。ワールドブラシをfunc_detailにするときには、可視性への影響と周囲のブラシとのつながりを考えて行う必要があります。


hintブラシ

"tools/hint"テクスチャを張ったブラシは、その面で強制的にリーフを分割する機能を持ちます。vbspが意図しないリーフ分割を行ってしまう場合や、描画高速化のために意図的にリーフを分割したい場合にこのテクスチャを使ってください。通常、hintテクスチャはブラシの1面のみに貼り付け、残りの5面には"tools/skip"テクスチャを貼り付けます。

hintブラシを配置するときには、端がかならず壁に接するようにしてください。隙間があると、そこに余計なリーフを作ってしまい、コンパイル時間を増加させてしまうことがあります。

ヒントについては以下のサイト等を参考に(英語サイトです。そのうち訳します)。


1024ユニットごとのリーフ分割

ワールドモデル、ヒントブラシの他に、もうひとつリーフ分割が必ず行われる場所があります。1024ユニットごと、エディタ上で言えば、水色と赤茶色の線で表されるグリッド部分では、必ずリーフが分割されます。このラインが部屋の真ん中や通路の真ん中を通っていると、意図せぬリーフ分割が発生してしまい、コンパイル時間を増加させます。逆に上手く使えば、hintブラシを配置することなく、意図した部分でリーフを分割することができます。


リーフに関する対策

  • コンパイル高速化対策
    • 視界を遮らないブラシや複雑なブラシをfunc_detailにする
    • func_detailなどのブラシエンティティで壁に無駄な穴を開けないようにする
    • 無駄な空間や隙間をnodrawやskyboxで埋める
    • 無闇にhintブラシを置かない(必要なところだけ)
    • 1024ユニットで区切られる箇所を考慮してマップ全体の配置を考える
  • 描画パフォーマンス対策
    • 視界を遮るブラシはワールドブラシのままにしておく
    • 無駄な空間や隙間をnodrawやskyboxで埋める
    • prop_staticなどでできた「穴」をnodrawのワールドブラシで埋める
    • ディスプレースメントの裏にnodrawブラシを置く
    • 空に突き抜けたリーフや極端に大きなリーフがある場合はhintを使って分割する
    • 見えない面にはnodrawブラシを張っておく

最後に

リーフ分割の最適化処理は、必ずいつでもできるというわけではありません。特に野外マップについては、リーフ分割の削減と描画パフォーマンスの向上は難しい問題になります。それでも、何もやらない場合よりは上記の対策を施した方が改善はされると思います。場合によっては、ワールドブラシで壁を追加するなど、マップに手を加えることによってパフォーマンスを向上できることもあります。

また、ここではリーフについてのみ記述していますが、func_areapotalやfunc_areapotalwindow、func_occluder、光源の種類、ライトマップ、エンティティのフェード属性、func_lodなどによっても描画パフォーマンスを向上させることができるようです。

トラックバック - http://halflife2.g.hatena.ne.jp/tetsu23/20051228