2013-11-02 16 views
1

我目前正在研究一個愛好項目,其中我有一個2D虛構宇宙中的幾千顆恆星。我需要將這些星星渲染到屏幕上,但顯然我不希望對所有這些星星進行操作 - 只有那些在任何給定時間都可見的星星。C++算法來過濾不相關的座標數據

對於概念證明,我寫了一個蠻力算法會看每一顆星星是和測試其座標對播放器的屏幕的界限:

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座標) 。

所以,我實現了兩個新的容器 - 一個用於按x座標排序的數據,另一個用y座標。我最初的想法是採取這兩種排序集合的交集並繪製所產生的恆星屏幕(星,它們的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想出一個適當的比較函數,或者我可以用我的迭代器手動比較座標的方法。

你們可以指點我正確的方向嗎?我的方法或實施反饋非常受歡迎。

謝謝你的時間!

+1

你應該查找[k-d trees](http://en.wikipedia.org/wiki/K-d_tree)。 – user1118321

+0

良好的通話!我正在根據接受的答案的建議來看看Quadtrees。 – SamuelMS

回答

3

有很多空間加速度數據結構可以幫助回答「這個地區有什麼問題」。 Quadtrees是2D的流行解決方案,但可能會爲您的問題矯枉過正。可能最簡單的方法是將二維網格與點(星)分開,並由它們所在的網格方塊構成。然後檢查您的視窗重疊的網格正方形,並只需查看這些正方形的桶中的星星。如果你使你的網格正方形比你的視窗大小大一點,你只需要檢查最多四個桶。

如果您可以放大或縮小像Quadtree這樣更復雜的結構,則可能是合適的。

+0

...除了在3D中它將是一個八叉樹,對吧? –

+0

我只看到x和y,所以我把這個問題看成2D。 – mattnewport

+0

你是對的。我看到了「明星」,並將其視爲3D。噢,好吧... –

0

我用真正的明星數據進行渲染(心身風格)多年,沒有速度的問題,沒有任何知名度排序/下的OpenGL(VBO)選擇

  1. 我通常使用BSC星表在過去

    • 星星達到+6。5mag
    • 9110星
  2. 幾年前,我將我的引擎依巴谷目錄

    • 118322星
    • 三維座標

所以,除非你使用太多更多的明星應該更快,只是把它們全部渲染成
- 您渲染多少顆星星? - 你如何呈現星星? (我用的每星形混合四)

什麼平臺/設置...
- 它運行良好,甚至在我的舊體制的GeForce 4000鈦,1.3GHz的單核心AMD - 也是在立體3D

什麼是你想要的FPS? ......我很好30fps的爲我的模擬

如果你有相似的價值觀和低速可能是有什麼不對您的渲染代碼(不與數據量)...

PS。

,如果你有一個大的空間來覆蓋你可以選擇明亮的恆星每個超空間跳躍或基於相對大小和距離

,你也什麼都

  • 後查看器只

    • 使用太多ifs進行明星選擇

      • 它們有時候比渲染速度慢
      • 嘗試觀看方向和星的方向向量,而不是
      • 的只是點積和只測試號(不看後面是什麼)
      • 當然
      • 如果你使用的四邊形然後CULL_FACE讓它爲你

      而且我看你是每個明星

      • 是堆搗毀
      • 儘量避免調用函數調用平局時,你可以
      • 它會提高速度!
      • 例如,你可以添加一個標誌,每個明星是否應該被渲染或者不
      • ,然後用單並沒有子調用使其渲染功能
  • 0

    你可以試試空間R- - 樹現在是Boost Geometry library的一部分。

    該應用程序可以工作如下:

    你把你的明星的協調,以樹的一些「絕對」座標系。如果你的恆星有不同的大小,你可能不想添加一個點,而是每個恆星的邊界框。

    #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將列出相關的星星和它們的邊界框。

    祝你好運!

    +0

    感謝您的回答!然而,就個人而言,我寧願儘可能少地使用Boost。 – SamuelMS