最適化
Java3D のパフォーマンスを上げるコーディング方法についての解説です。
ほとんどは Retained Mode に関する事です。
分かり難い訳もありますが、ご了承ください。
この解説は
この 記事を参考に書かれています (英語)。
API /
Java3D 1.1 の最適化 /
コツ /
戻る /
トップページ
最適化のための API
- Java3D API の中には最適化を目的としたものがいくつか用意されています。
つまり、これらの API をうまく使う事で最適化できるわけです。
-
-
Capability bits
- Capability bit によってそのオブジェクトの値が
変化するかどうかがわかるので、
最適化処理が施される可能性があります。
-
-
コンパイル
- BranchGroup クラスと SharedGroup クラスにある
compile() メソッドでその
Group にぶら下がっているオブジェクト群をコンパイルできます。
コンパイルされたオブジェクトは
Capability bit で設定された属性のみ変更できます。
逆に言えば、Capability bit で設定されていない属性は
変更されないことを利用した内部形式に変換され、
パフォーマンス向上に利用されます。
-
-
有効範囲
- Bounds オブジェクトは
Light, Behavior, Fog, Clip, Background,
BoundingLeaf, Sound, Soundscape
などのオブジェクトに設定するように作られています。
これは有効範囲を設定する事で、
有効範囲外のレンダリング時にそれらのオブジェクトの処理を
無視できるからです。
-
-
Rendering の順序
- SceneGraph は木構造のため、
Node には順番があるわけではありません
(例えば、配列で物体を管理していたら配列の添え字という順番がありますね)。
その点を利用して
OrderedGroup クラスや半透明の物体などの特殊なもの以外に対しては
物体の描画順をパフォーマンスを向上するように変えています。
-
-
Appearance の構成
- Shape3D オブジェクトは Geometry オブジェクトと
Appearance オブジェクトを参照してます。
そして、その Appearance オブジェクトは
さらに多くの別のオブジェクトへの参照
のみで構成されています。
この参照のみで構成されている点を利用して最適化を行う事ができます。
Appearance オブジェクトが参照しているオブジェクトの属性の変更を
調べる手段を簡単化し、
低レベルのレンダリング API の変更を最小限にとどめることができます。
Java3D 1.1 で行われている最適化
- Java3D 1.1 で現在行われている最適化について紹介します。
-
-
ハードウェア
- Java3D は低レベルのレンダリング API として
OpenGL と Direct3D を使っています。
OpenGL や Direct3D はハードウェアアクセラレーションが
受けられるので、結果として
Java3D もハードウェアアクセラレーションを受けています。
-
-
コンパイル
- 現在の Java3D はコンパイルを使った最適化を 1 つだけ行っています。
BranchGroup オブジェクトはコンパイルされると
その下にぶら下がっている Shape3D オブジェクトを
検索する時間を短縮するように処理されます。
変化しない Shape3D オブジェクト群を
1 つにまとめて BranchGroup オブジェクトに関連付けます。
-
-
状態でソートされた Rendering
- SceneGraph が木構造である事を利用して物体の
Rendering 順序を最適化しています。
Rendering される物体はその特性で並び替えられます。
並び替えの基準となる特性は順番に
Light, Texture, Geometry Type, Material, localToVworld transform
となります。
-
-
View Frustum Culling
- Java3D は view frustum culling を実装しています。
view frustum culling は特定の Canvas3D オブジェクトに対する
処理が行われる時に行われます。
これは低レベルグラフィックス API
が処理するオブジェクト数を減らす事ができます。
-
-
マルチスレッド
- Java3D API はマルチスレッド環境を意識して作られています。
現在の実装は完全にマルチスレッド化されています。
可視判定やレンダリング、Behavior のスケジューリング、
音声のスケジューリング、入力のスケジューリング、衝突判定
などは並列実行されます。
最適化のコツ
- 最適化、特に高速化するためのコツを解説します。
-
-
Capability bits
- アプリケーションの機能を良く検討して必要な
Capability bit のみをセットします。
Capability bit がセットされていない属性については
様々な最適化が受けられます。
-
-
有効範囲
- 様々な Leaf Node に対しての空間的な広がりを考えて
それに応じて Bound オブジェクトを決めます。
こうする事でレンダリングする場所から
離れている場所にあるオブジェクトの処理が省略され、高速化されます。
ただし、幾何学的な Bound オブジェクトには適応されず、
自動的に計算された
Bound オブジェクトにのみ適応される点に注意してください。
-
-
Shape3D オブジェクトの数
- 現在の Java3D の実装では Shape3D オブジェクトの使用には
ある量のオーバーヘッドがあります。
Shape3D オブジェクトの数を減らした方が良いという事です。
しかし、空間的な局所性のない Shape3D オブジェクトは
view frustum culling を効果的に無効にして
逆にパフォーマンスを向上させます。
そのため、view frustum culling の影響を考えてバランスの取れた
Shape3D オブジェクトの使い方を実験してみる事が大切です。
-
-
幾何学形とそのフォーマット
- 大抵のレンダリングハードウェアは
長い triangle strip をレンダリングする時に
最大のパフォーマンスが得られます。
しかし、大抵のファイルに記録されている幾何学情報は
ばらばらの三角形や小さな triangle fan (ポリゴン)
で構成されてしまっています。
Java3D のユーティリティーパッケージには
com.sun.j3d.utils.geometry.Stripifier クラスがあります。
これは、与えられた幾何学形を長い
triangle strip に変換するクラスなので、
試してみる価値はあります。
また、大抵のレンダリングハードウェアは
single triangle fan の長いリストよりは
ばらばらの三角形の長いリストのほうが速く処理できます。
Stripifier クラスは今後、より効率的な
stripification ができるように更新されていきます。
-
-
Appearance, Texture, Material の参照
- Java3D は物体の表示順をソートしてパフォーマンスを上げようとします。
その機能を生かすためにできるだけ
Shape3D オブジェクト間で
Appearance, Texture, Material
オブジェクトを共有したほうが効率的になります。
-
-
スレッド
- スレッド機能のある Java 言語でのプログラミングは強力である一方、
上手に注意深く使わないとパフォーマンスを落としかねません。
Java のスレッドを使うに当たって注意すべき点が 2,3 点あります。
-
-
demand driven fashion
- 1 つめはスレッドを demand driven fashion で使う事です。
これはするべき処理がある時のみスレッドを走らせるという事です。
無駄に走っているスレッドは Java3D も含めて
システムから処理時間を奪ってしまいます。
-
-
プライオリティー
- 2 つめはスレッドのプライオリティーを適切に設定する事です。
ほとんどの JavaVM
はプライオリティーを積極的に使うようになっていきます。
低すぎるプライオリティーはスレッドを枯渇させてしまいますし、
高すぎるプライオリティーはシステムの残りを枯渇させてしまいます。
プライオリティーをどうしようか迷った時は
デフォルトのプライオリティーを使ってください。
-
-
Behavior クラス
- 3 つめはそのアプリケーションが本当にそのスレッドを
必要としているのか検討する事です。
1 フレームに 1 回だけ実行すればよいものであれば
毎フレーム動いてくれる Behavior クラスを使った方が効率的です。
-
-
Java3D のスレッド
- Java3D は実装にたくさんのスレッドを使用していますので、
上に書いたような予防策が必要です。
ほとんどの場合、Java3D はそのスレッドを効率的に使います。
demand driven fashion ですし、
デフォルトのプライオリティーを使っていますが、
その方法に完全にのっとっていない場合もわずかながらあります。
-
-
Behavior
- その様な例の 1 つめは
未決定の WakeupOnElapsedTime 基準にある
Behavior scheduler です。
この場合、最小 WakeupOnElapsedTime 基準に満ちた時に
wakeup しなければならないからです。
そのため、WakeupOnElapsedTime クラスを使う事で
Behavior scheduler を必要以上に動かしてしまうことがあります。
-
-
衝突判定
- Java3D のスレッドがうまく制御されない
2 つめの例は衝突判定がされる時です。
衝突判定される時、衝突判定用スレッドは事実上無駄に動いています。
これは現在の実装の欠点なので、将来的には修正されるでしょう。
衝突判定システムを使う代わりに 1 フレームに 1 回
PickSegment クラスか PickShape クラスで
picking を使う方法があります。
これは特殊な形のアプリケーションとして使えます。
アプリケーションは幾何学形状に基づいた衝突判定ではなく、
bound に基づいた衝突判定を使うべきです。
-
-
音声
- 3 つめの例は Sound subsystem です。
現在の音声レンダリングエンジンの限界のために
音声の使用は内部で高いプライオリティーのスレッドを
起動させてしまいます。
この事は逆にパフォーマンスに影響を与えてしまいます。
-
-
一般のスレッド
- Java3D は完全にマルチスレッド化されているので、
システムにある CPU の数を増やす事で
大きくパフォーマンスを向上させる事ができます。
正確にアニメーションをさせたいアプリケーションには
2 つの CPU で十分です。
音声や衝突判定などの機能を使い出すともっと多くの
CPU が役に立ってきます。
注意: Solaris 環境で使う時は
ネイティブスレッドが有効である点に注意してください。
Green thread は複数の CPU を活用しません。
-
-
Occlusion Culling のための Switch クラスの使用
- もし、アプリケーションが 1 人称の視点で
動作環境を詳しく把握しているのであれば、
Switch クラスを簡単な occlusion culling の実装に使えます。
Switch オブジェクトの子のうち、
今見えていないものを OFF にします。
もし、この種の知識があれば
この方法はかなり便利なテクニックとなります。
-
-
アニメーションのための Switch クラスの使用
- ほとんどのアニメーションは物体に効果を与える変換を
変えていく事でできています。
アニメーションがかなり単純で繰り返すものだったら
flipbook テクニックがアニメーションに使えます。
単純にアニメーションの全フレームを
Switch オブジェクトにぶら下げ、
SwitchValueInterpolator オブジェクトをその
Switch オブジェクトに使います。
この方法はスムーズなアニメーションのために
メモリ消費量を増やしてしまいます。
-
-
OrderedGroup
- OrderedGroup クラスやそのサブクラスの実体は
その他の Group ほど高いパフォーマンスは得られません。
これは物体の表示順をソートする最適化が受けられないためです。
OrderedGroup を使わない代価案があるのでしたら
そちらを使った方が効率的です。
-
-
LOD Behavior
- 複雑なシーンでは
詳細な描写を必要としない物体をレンダリングするのに使う
Geometry クラスを減らすために
LOD Behavior を使う事でパフォーマンスの向上が望めます。
高速なレンダリング速度を得るために
メモリー消費量を増やすという選択肢もあります。
-
-
Picking
- もし、厳密な幾何学形状に基づいた picking が必要ないのでしたら
Bound に基づいた picking を使います。
-
-
物体の移動と ViewPlatform の移動
- 全部の物体を単純に移動する時は物体
1 つ 1 つを動かすのではなく、
ViewPlatform オブジェクトを動かすべきです。
当然の事ですが、
変更する Transform3D オブジェクトの数が減るからです。
戻る