2013-11-02 15 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