独自のシーングラフのローリング


23

こんにちは、Game Development SE!

私は、シンプルで非常に軽量なゲームエンジンを作成することを期待して、OpenGLを駆け巡っています。私はこのプロジェクトを、最終的には少しお金を稼ぐかもしれないが、どちらにしても楽しい学習体験だと思っています。

これまで、GLFWを使用して、基本的なI / O、ウィンドウ(非常に派手なF11フルスクリーンキー)、そしてもちろんOpenGLコンテキストを取得しました。また、GLEWを使用して、残りのOpenGL拡張機能を公開しました。これは、Windowsを使用しており、OpenGL 3.0+をすべて使用するためです。

これでシーングラフが表示されます。要するに、私は自分で転がしたいと思います。この決定は、OSGを見て、シーングラフの概念がどのようにねじれ、曲がり、壊れたのかに関するいくつかの記事を読んだ後に決定されました。そのような記事の 1 つでは、シーングラフがどのように開発されているかを説明しています...

次に、クリスマスツリーに飾りを吊るすなど、これらすべての余分なものを追加しました。ただし、飾りの一部はジューシーなステーキで、一部は生きた牛全体です。

類推に続いて、余分なコードの山や牛全体を縛る必要なしに、ステーキ、シーングラフがどうあるべきかの肉が欲しいです。

それで、それを念頭に置いて、私はシーングラフがどうあるべきか、単純なシーングラフをどのように実装すべきかを正確に疑問に思っていますか?ここに私が持っているものがあります...

1つの親、n子ツリーまたはDAG ...

  • ゲームオブジェクトの変換(位置、回転、スケール)を追跡する必要があります
  • 最適化のためにレンダリング状態を保持する必要があります
  • ビュー錐台内にないオブジェクトを選別する手段を提供する必要があります

次のプロパティで...

  • すべてのノードはレンダリング可能として扱われる必要があります(レンダリングしない場合でも)これは、...

    • すべてにcull()、state()、draw()メソッドが必要です(非表示の場合は0を返します)
    • cull()は、すべての子に対して再帰的にcull()を呼び出し、ノード全体およびすべての子に対して完全なカリングメッシュを生成します。もう1つのメソッドhasChanged()を使用すると、いわゆる静的メッシュでカリングジオメトリを各フレームで計算する必要がなくなります。これは、サブツリー内のノードが変更された場合、ルートまでのすべてのジオメトリが再構築されるように機能します。
  • レンダリング状態は単純な列挙で保持され、各ノードはこの列挙から必要なOpenGL状態セットを選択し、そのノードでdraw()が呼び出される前に状態がセットアップされます。これにより、バッチ処理が可能になり、指定された状態セットのすべてのノードが一緒にレンダリングされ、次の状態セットがセットアップなどになります。

  • ジオメトリ/シェーダー/テクスチャデータを直接保持するノードはありません。代わりに、ノードは共有オブジェクト(リソースマネージャーなどのシングルトンオブジェクトによって管理される可能性があります)を指す必要があります。

  • シーングラフのような状況可能にするために(多分プロキシノードを使用して)他のシーングラフを参照することができる必要があり、これをこのように複雑なマルチメッシュモデルを可能にする、/データのトンを追加することなく、シーングラフの周りにコピーするオブジェクト。

現在の設計について貴重なフィードバックを得たいと思っています。機能がありませんか?非常に優れた方法/デザインパターンはありますか?ややシンプルな3Dゲームのために、このデザインに含める必要のあるより大きなコンセプトがありませんか?等。

ありがとう、コーディ

回答:


15

コンセプト

基本的に、シーングラフは、空間構造の階層構造セットを表すのに役立つ双方向の非循環グラフにすぎません。

野生のエンジンは、前述のように、シーングラフに他のグッズを含める傾向があります。それを肉と牛のどちらとして見るかは、おそらくエンジンと図書館の経験に依存します。

軽量に保つ

Unity 3Dスタイルは、シーングラフノード(本質的には空間/地形構造ではなく位相構造)に本質的に空間パラメーターと機能を含めることを好みます。私のエンジンでは、ノードはUnity3Dよりもさらに軽量であり、スーパークラス/実装されたインターフェイスから不要なジャンクメンバーを多く継承します。これは、私が持っているものです。

  • 親/子ポインターメンバー。
  • 変換前の空間パラメーターメンバー:xyzの位置、ピッチ、ヨー、およびロール。
  • 変換行列; 階層チェーン内のマトリックスは、ツリーを再帰的に上下に移動することで非常に迅速かつ簡単に乗算でき、シーングラフの主な機能である階層空間変換を提供します。
  • このノードの変換行列のみupdateLocal()を更新するメソッド
  • これとすべての子孫ノードの変換行列updateAll()を更新するメソッド

...また、ノードクラスに運動方程式ロジックと速度/加速度メンバー(線形および角度)を含めます。あなたはそれを忘れ、あなたが望むなら代わりにあなたのメインコントローラーでそれを扱うことができます。しかし、それだけです-実際、非常に軽量です。覚えておいて、あなたはこれらを数千のエンティティに持つことができた。あなたが提案したように、それを軽くしてください。

階層の構築

他のシーングラフを参照するシーングラフについてあなたが言うことは...パンチラインを待っていますか?もちろんそうです。それが主な用途です。任意のノードを他のノードに追加できます。変換は、新しい変換のローカルスペース内で自動的に発生するはずです。あなたがしているのは、ポインタを変更することだけです。データをコピーしているわけではありません!ポインタを変更すると、より深いシーングラフが得られます。プロキシを使用すると、すべての手段で物事がより効率的になりますが、私はその必要性を見たことはありません。

レンダリング関連のロジックを避ける

シーングラフノードクラスを記述するときにレンダリングを忘れてください。さもないと、混乱を招くことになります。重要なのは、シーングラフであろうとなかろうと、データモデルがあり、レンダラーがそのデータモデルを検査し、それに応じて1、2にあるかどうかに応じてオブジェクトをレンダリングすることです。 、3または7次元。私が言いたいのは、レンダリングロジックでシーングラフを汚染しないことです。シーングラフは、トポロジとトポグラフィ、つまり接続性と空間特性に関するものです。これらはシミュレーションの真の状態であり、レンダリングが行われていない場合でも存在します(一人称視点から統計グラフ、テキストによる説明まで、太陽の下であらゆる形をとることができます)。ノードはレンダリング関連のオブジェクトを指していませんが、逆の場合もあります。これも考慮してください:ツリー全体のすべてのシーングラフノードがレンダリング可能になるわけではありません。多くは単なるコンテナになります。それでは、なぜレンダリングオブジェクトへのポインターにメモリを割り当てるのでしょうか?決して使用されないポインターメンバーでさえ、メモリを占有しています。そのため、ポインターの方向を逆にします。レンダー関連のインスタンスは、データモデル(シーングラフノードであるか、含まれている可能性があります)を参照します。また、コントローラーのリストを簡単に実行し、関連するビューにアクセスする簡単な方法が必要な場合は、O(1)読み取りアクセス時間に近づく辞書/ハッシュテーブルを使用します。こうすることで汚染がなくなり、シミュレーションロジックはどのレンダラーが配置されているかを気にしません。それでは、なぜレンダリングオブジェクトへのポインターにメモリを割り当てるのでしょうか?決して使用されないポインターメンバーでさえ、メモリを占有しています。そのため、ポインターの方向を逆にします。レンダー関連のインスタンスは、データモデル(シーングラフノードであるか、含まれている可能性があります)を参照します。また、コントローラーのリストを簡単に実行し、関連するビューにアクセスする簡単な方法が必要な場合は、O(1)読み取りアクセス時間に近づく辞書/ハッシュテーブルを使用します。こうすることで汚染がなくなり、シミュレーションロジックはどのレンダラーが配置されているかを気にしません。それでは、なぜレンダリングオブジェクトへのポインターにメモリを割り当てるのでしょうか?決して使用されないポインターメンバーでさえ、メモリを占有しています。そのため、ポインターの方向を逆にします。レンダー関連のインスタンスは、データモデル(シーングラフノードであるか、含まれている可能性があります)を参照します。また、コントローラーのリストを簡単に実行し、関連するビューにアクセスする簡単な方法が必要な場合は、O(1)読み取りアクセス時間に近づく辞書/ハッシュテーブルを使用します。こうすることで汚染がなくなり、シミュレーションロジックはどのレンダラーが配置されているかを気にしません。また、コントローラーのリストを簡単に実行し、関連するビューにアクセスする簡単な方法が必要な場合は、O(1)読み取りアクセス時間に近づく辞書/ハッシュテーブルを使用します。こうすることで汚染がなくなり、シミュレーションロジックはどのレンダラーが配置されているかを気にしません。また、コントローラーのリストを簡単に実行し、関連するビューにアクセスする簡単な方法が必要な場合は、O(1)読み取りアクセス時間に近づく辞書/ハッシュテーブルを使用します。こうすることで汚染がなくなり、シミュレーションロジックはどのレンダラーが配置されているかを気にしません。世界容易になります。

カリングについては、上記を参照してください。関心領域のカリングは、シミュレーションロジックの概念です。つまり、この(通常はボックス、円形、または球体)エリアの外側の世界を処理しません。これは、レンダリングが発生する前に、メインコントローラー/ゲームループで行われます。一方、錐台カリングは純粋にレンダリングに関連しています。ですから、今すぐカリングを忘れてください。シーングラフとは何の関係もありません。それに焦点を当てることで、達成しようとしていることの真の目的を曖昧にします。

最後のメモ...

ここにはレンダリングに関するすべての詳細が含まれているため、Flash(具体的にはAS3)のバックグラウンドから来ているという強い印象を受けます。はい、Flash Stage / DisplayObjectパラダイムには、シーングラフの一部としてすべてのレンダリングロジックが含まれています。しかし、Flashは、必ずしもあなたがしたくないという多くの仮定をします。本格的なゲームエンジンの場合、パフォーマンス、利便性、適切なSoCによるコードの複雑さの制御を理由に、この2つを混在させない方がよいでしょう。


1
ニック、ありがとう。私は実際には3Dアニメーター(フラッシュではなく、実際の3D)になったプログラマーなので、グラフィックスの観点から考える傾向があります。それで十分でない場合は、Javaで始めて、その言語に浸透している「すべてがオブジェクトでなければならない」という考え方から自分をpr索してきました。シーングラフをレンダリングおよびカリングコードから分離する必要があると私は確信しましたが、今では私のギアはまさにそれを達成する方法で回転しています。私はそれのように、レンダラ自身の明確なシステムを処理すると思っているの参照データを変換するためのシーングラフなど
コーディ・スミス

1
@CodySmith、助かりました。恥知らずなプラグインですが、SoC / MVCに関するフレームワークを維持しています。そうすることで、私はすべてが中央のモノリシックなオブジェクトにあるべきだと主張する業界のより伝統的な陣営に打撃を与えました。しかし、彼らは一般的に言っても、レンダリングをシーングラフとは別にしてください。SoC / SRPは、私が十分に強調できないものです。必要以上に多くのロジックを単一のクラスに混在させないでください。あなたが私の頭に銃を置いたなら、私は同じクラスの混合論理上で複雑なオブジェクト指向継承チェーンさえ擁護したいと思います!
エンジニア

いいえ、コンセプトは好きです。そしてあなたの権利、これは私が長年ゲームデザインについて読んだどこかで見たSoCの最初の言及です。再度、感謝します。
コーディスミス

@CodySmithこれをもう一度閲覧しているときにすばやく考えました。一般的に、物事を切り離しておくのは良いことです。ただし、レンダリングを行うコードベース内のさまざまなタイプのモデルコントローラーオブジェクトの場合、s(インターフェイスまたは抽象クラス)のコレクションをこれらのコアモデルコントローラーオブジェクトの内部に保持しておくこと問題ありませんRenderable。これの良い例は、エンティティまたはUI要素です。したがって、特定のコアオブジェクトに関連するレンダラーのみに迅速にアクセスできます。エンティティクラスを汚染する実装仕様がなく、したがってインターフェイスを使用できません。
エンジニア

@CodySmithエンティティを使用するメリットは明らかです。ワールドビューポートとミニマップの両方にリプレゼンテーションがあります。したがって、コレクション。あるいは、モデルコントローラオブジェクトごとに、そのオブジェクトの内部で1つのレンダラースロットのみを許可できます。ただし、インターフェイスは一般的なものにしてください!詳細なし-ただRenderer
エンジニア
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.