1

私は現在、2D の架空の宇宙に数千の星がある趣味のプロジェクトに取り組んでいます。これらの星を画面にレンダリングする必要がありますが、すべての星を操作する必要はなく、常に表示されている星のみを操作する必要があることは明らかです。

概念を証明するために、すべての星を見て、プレイヤーの画面の境界に対してその座標をテストするブルート フォース アルゴリズムを作成しました。

for (const std::shared_ptr<Star>& star : stars_) {
    if (moved_)
        star->MoveStar(starfield_offset_, level_);
            position = star->position();
    if (position.x >= bounds_[0] &&
        position.x <= bounds_[1] &&
        position.y >= bounds_[2] &&
        position.y <= bounds_[3])
        target.draw(*star);
}

この不格好な方法は、実際には目に見える星だけを画面に描画しますが、明らかに線形時間で動作します。星は背景の一部にすぎず、率直に言って、プロセッサがフィルタリングに時間を費やす最も重要なものではないため、負荷を軽減するためのより高速なアルゴリズムを考案したいと考えています。

したがって、私の現在の考えは、バイナリ検索を使用して関連する星を見つける方法に沿っています。このためには、明らかにデータを並べ替える必要があります。ただし、座標データを並べ替える方法がよくわかりませんでした。(x 座標と y 座標の両方に関して) データを昇順で適切に並べ替えることができる絶対的な順序は考えられませんでした。 .

そこで、2 つの新しいコンテナーを実装しました。1 つは x 座標でソートされたデータ用で、もう 1 つは y 座標でソートされたデータ用です。私の最初の考えは、これらの 2 つの並べ替えられたセットの交点を取得し、結果の星を画面に描画することでした (x 座標と y 座標が画面の境界内にある星)。

struct SortedStars {
    std::vector<std::shared_ptr<Star>>::iterator begin, end;
    std::vector<std::shared_ptr<Star>> stars;
} stars_x_, stars_y_;

次に、これらのコンテナーを並べ替えました。

// comparison objects
static struct SortX {
    bool operator() (const std::shared_ptr<Star>& first, const std::shared_ptr<Star>& second)
        { return (first->position().x < second->position().x); }
    bool operator() (const std::shared_ptr<Star>& first, const float val)
        { return (first->position().x < val); }
    bool operator() (const float val, const std::shared_ptr<Star>& second)
        { return (val < second->position().x); }
} sort_x;

static struct SortY {
    bool operator() (const std::shared_ptr<Star>& first, const std::shared_ptr<Star>& second)
        { return (first->position().y < second->position().y); }
    bool operator() (const std::shared_ptr<Star>& first, const float val)
        { return (first->position().y < val); }
    bool operator() (const float val, const std::shared_ptr<Star>& second)
        { return (val < second->position().y); }
} sort_y;

void Starfield::Sort() {
    // clone original data (shared pointers)
    stars_x_.stars = stars_;
    stars_y_.stars = stars_;
    // sort as needed
    std::sort(stars_x_.stars.begin(), stars_x_.stars.end(), sort_x);
    std::sort(stars_y_.stars.begin(), stars_y_.stars.end(), sort_y);

    // set iterators to the outermost visible stars (defined by screen bounds)
    // these are updated every time the screen is moved
    stars_x_.begin = std::lower_bound(stars_x_.stars.begin(), stars_x_.stars.end(), bounds_[0], sort_x);
    stars_x_.end = std::upper_bound(stars_x_.stars.begin(), stars_x_.stars.end(), bounds_[1], sort_x);
    stars_y_.begin = std::lower_bound(stars_y_.stars.begin(), stars_y_.stars.end(), bounds_[2], sort_y);
    stars_y_.end = std::upper_bound(stars_y_.stars.begin(), stars_y_.stars.end(), bounds_[3], sort_y);

    return;
}

残念ながら、std::set_intersection の適切な比較関数や、イテレータを使用して手動で座標を比較できる方法を思い付くことができないようです。

私を正しい方向に向けてもらえますか?私の方法論や実装に関するフィードバックは大歓迎です。

御時間ありがとうございます!

4

3 に答える 3

3

「この領域のポイントは何か」という質問に答えるのに役立つ、さまざまな空間加速度データ構造があります。四分木は 2D の一般的なソリューションですが、問題に対してはやり過ぎかもしれません。おそらく最も簡単な方法は、点 (星) を含む 2D グリッドを、それらが入るグリッド スクエアでバケット化することです。次に、ビュー ウィンドウが重なっているグリッドの正方形を確認し、それらの正方形のバケット内の星を見るだけで済みます。グリッドの正方形をビュー ウィンドウのサイズより少し大きくすると、最大 4 つのバケットをチェックするだけで済みます。

ズームインおよびズームアウトできる場合は、四分木のようなより複雑な構造が適している可能性があります。

于 2013-11-02T02:52:38.400 に答える
1

Boost Geometry ライブラリの一部になった空間 R ツリーを試すことができます。

アプリケーションは次のように機能します。

スターの座標を「絶対」座標系でツリーに追加します。星のサイズが異なる場合は、ポイントではなく、各星のバウンディング ボックスを追加することをお勧めします。

#include <boost/geometry/index/rtree.hpp>
#include <boost/geometry/geometries/box.hpp>
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

typedef bg::model::point<float, 2, bg::cs::cartesian> point;
typedef bg::model::box<point> box;
typedef std::pair<box, Star*> value; //here "second" can optionally give the star index in star's storage 
bgi::rtree<value> rtree;

ユニバースを構築するときに、rtree を設定します。

for (auto star: stars)
{
    box b(star->position(), star->position()));
    bg::expand(b, point(star->radius(), star->radius());
    // insert new value
    rtree.insert(std::make_pair(b, star));   
}

それらをレンダリングする必要がある場合は、画面ウィンドウを「絶対」座標系に計算し、ウィンドウに重なっている星についてツリーにクエリを実行します。

box query_box(point(0, 0), point(5, 5));
std::vector<value> result_s;
rtree.query(bgi::intersects(query_box), std::back_inserter(result_s));

ここで、result_s は関連する星とその境界ボックスをリストします。

幸運を!

于 2013-11-02T23:45:37.787 に答える
1

私は何年もレンダリングに実際の星のデータを使用しており(心療スタイル)、OpenGL(VBO)での可視性の順序付け/選択がなければ速度の問題はありません

  1. 以前はBSC星図表をよく使っていました

    • +6.5等星まで
    • 星9110個
  2. 数年前、エンジンをヒッパルコス カタログに変換しました

    • 118322 個の星
    • 3D 座標

したがって、あまりにも多くの星を使用しない限り、それらすべてをレンダリングする方が高速なはず
です。レンダリングする星の数は? - スターはどのようにレンダリングされますか? (私はスターごとに混合クワッドを使用しています)

どのプラットフォーム/セットアップ...
- これは私の古いセットアップ GeForce 4000 Ti、1.3GHz シングルコア AMD - ステレオ 3D でもうまくいきました

あなたの望むFPSは何ですか?... シミュレーションでは 30fps で問題ありません

同様の値があり、速度が遅い場合は、レンダリング コードに問題がある可能性があります (データの量ではありません)...

PS。

カバーする大きなスペースがある場合は、視聴者のみに明るい星を選択できます

  • 各ハイパースペースジャンプの後、またはこれまでに
  • 相対的な等級と距離に基づく

また、星の選択にifを使いすぎています

  • レンダリングよりも遅い場合があります
  • 代わりに、視線方向と星の方向ベクトルのドット積だけを試してください
  • サインのみをテストします(背後にあるものは表示されません)
  • もちろん、クワッドを使用する場合は、CULL_FACE で作成できます

また、各スターのドローを呼び出していることがわかります

  • それはヒープトラッシングです
  • 可能な場合は関数の呼び出しを避けるようにしてください
  • それは速度を大幅に向上させます!!!
  • たとえば、レンダリングする必要があるかどうかに応じて、各星にフラグを追加できます
  • 次に、単一の for を使用してそれらをレンダリングし、render 関数へのサブコールを使用しません
于 2013-11-02T10:10:03.240 に答える