用于过滤不相关坐标数据的C ++算法

时间:2013-11-02 02:41:10

标签: c++ algorithm sorting binary-search set-intersection

我目前正致力于一个爱好项目,在这个项目中,我在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坐标)

所以,我实现了两个新容器 - 一个用于按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提供适当的比较函数,也不能使用我的迭代器手动比较坐标的方法。

你们能指出我正确的方向吗?对我的方法或实施的反馈非常受欢迎。

谢谢你的时间!

3 个答案:

答案 0 :(得分:3)

有各种各样的空间加速度数据结构可以帮助回答“该区域中的哪些点”的问题。 Quadtrees是2D的流行解决方案,但对您的问题可能有点过分。可能最简单的方法是使用一个二维网格,其中的点(星形)被它们落入的网格方块所取代。然后,您可以检查视图窗口重叠的网格方块,只需要查看这些方块中的星形。如果你使网格方块比视窗大小稍大,你只需要检查最多四个桶。

如果你可以放大和缩小像Quadtree这样的更复杂的结构可能是合适的。

答案 1 :(得分:0)

我使用真实的星形数据进行渲染(心身风格)多年,并且在OpenGL(VBO)下没有任何可见性排序/选择时没有速度问题

  1. 我过去常用过BSC明星目录

    • 最高达+ 6.5mag
    • 9110颗星
  2. 几年前我将我的引擎转换为hipparcos目录

    • 118322 stars
    • 3D坐标
  3. 因此,除非你使用过多的星星,否则将它们全部渲染起来应该更快 - 你渲染了多少颗星星? - 你是如何呈现星星的? (我每颗星使用混合四极杆)

    什么平台/设置...
    - 即使在我的旧设置GeForce 4000 Ti,1.3GHz单核AMD上也能很好地工作 - 也是立体声3D

    你想要的FPS是多少? ......对于我的模拟,我的30fps很好

    如果你有相似的值,低速可能是你的渲染代码有问题(没有数据量)......

    PS。

    如果你有很大的空间可以覆盖,你只能选择明亮的星星

    • 每次超空间跳跃之后或者
    • 基于相对幅度和距离

    你也会使用太多ifs进行明星选择

    • 它们有时比渲染
    • 尝试查看方向和星方向向量的点积
    • 仅测试标志(不要看后面是什么)
    • 当然如果您使用四边形,那么CULL_FACE会为您制作

    我也看到你正在为每个明星打电话

    • 那是堆垃圾
    • 尽量避免在
    • 时调用函数
    • 它会提高速度!!!
    • 例如,如果要渲染的话,你可以为每个星星添加一个标志
    • 然后使用单个for和没有子渲染渲染函数来渲染它们

答案 2 :(得分:0)

您可以尝试空间R-tree,它现在是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;

在构建Universe时,填充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将列出相关的星星及其边界框。

祝你好运!