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 坐标) .

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

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

谢谢你的时间!

4

3 回答 3

3

有多种空间加速度数据结构有助于回答“该区域中有哪些点”的问题。四叉树是 2D 的流行解决方案,但对于您的问题可能有点矫枉过正。可能最简单的方法是有一个 2D 网格,其中的点(星)被它们落入的网格正方形所包围。然后,您检查查看窗口重叠的网格正方形,并且只需要查看这些正方形的桶中的星星。如果你让你的网格方块比你的视图窗口大小大一点,你只需要检查最多四个桶。

如果您可以放大和缩小更复杂的结构,例如四叉树,可能是合适的。

于 2013-11-02T02:52:38.400 回答
1

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

  1. 过去我通常使用 BSC 星表

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

    • 118322 颗星
    • 3D坐标

因此,除非您使用太多星星,否则将它们全部渲染应该更快
- 您要渲染多少颗星星?- 你的星星是怎么渲染的?(我每颗星使用混合四边形)

什么平台/设置...
- 即使在我的旧设置 GeForce 4000 Ti、1.3GHz 单核 AMD 上也能正常工作 - 也适用于立体声 3D

你想要的 FPS 是多少?...我的模拟速度可以达到 30fps

如果您具有相似的值并且速度较低,则可能是您的渲染代码有问题(不是数据量)...

PS。

如果你有很大的空间可以覆盖你可以选择明亮的星星只给观众

  • 在每次超空间跳跃或其他任何事情之后
  • 基于相对大小和距离

你也用了太多的ifs来选星

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

我也看到你在为每颗星星打电话

  • 那是堆垃圾
  • 尽量避免调用函数
  • 它将大大提高速度!
  • 例如,您可以为每颗星添加一个标志,是否应该渲染它
  • 然后使用单个 for 渲染它们,并且没有对渲染函数的子调用
于 2013-11-02T10:10:03.240 回答
1

您可以尝试空间 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 将列出相关的星星及其边界框。

祝你好运!

于 2013-11-02T23:45:37.787 回答