c++ - Z 排序的 3d 对象列表

标签 c++ opengl

在 C++/OpenGL 应用程序中,我有一堆排列在 3d 空间中的半透明对象。由于半透明,对象必须按从远到近的顺序绘制。 (出于“Transparency Sorting”中描述的原因。)

幸运的是,相机是固定的。因此,我计划维护一个指向 3d 对象的指针集合,按相机 Z 排序。每一帧,我将遍历该集合,绘制每个对象。

快速插入和删除很重要,因为存在的对象经常变化。

我正在考虑使用 std::list 作为容器。为了插入,我将使用 std::lower_bound 来确定新对象的去向。然后我将在 lower_bound 返回的迭代器处插入。

这听起来像是一种明智的方法吗?根据我提供的详细信息,您是否预见到我忽略的任何主要性能问题?

最佳答案

我认为 std::list 永远不是这个用例的好选择。虽然插入效率很低,但您需要遍历列表以找到插入的正确位置,这使得它的复杂度为 O(n)。

如果您想保持简单,std::set 已经比 std::list 好得多,甚至更易于应用。它是作为平衡树实现的,因此插入的复杂度为 O(log n),只需调用容器上的 insert() 方法即可完成。迭代器按排序顺序为您提供元素。它确实有迭代期间非本地内存访问模式的缺点,这使得它对缓存不友好。

我想到了另一种方法,直觉上应该非常有效。它的基本思想类似于@ratchet_freak 已经提出的,但它不会在每次迭代时复制整个 vector :

  • 包含数据主要部分的容器是 std::vector,它始终保持排序。
  • 新元素被添加到“溢出”容器中,它可以是一个 std::set,或另一个保持排序的 std::vector。这只允许达到一定的大小。
  • 在迭代时,同时遍历主容器和溢出容器,使用与归并排序类似的逻辑。
  • 当溢出容器达到大小限制时,将其与主容器合并,产生一个新的主容器。

粗略的代码草图:

const size_t OVERFLOW_SIZE = 32;

// Ping pong between two vectors when merging.
std::vector<Entry> mainVecs[2];
unsigned activeIdx = 0;

std::vector<Entry> overflowVec;
overflowVec.reserve(OVERFLOW_SIZE);

void insert(const Entry& entry) {
    std::vector<Entry>::iterator pos =
        std::upper_bound(overflowVec.begin(), overflowVec.end(), entry);
    overflowVec.insert(pos, 1, entry);

    if (overflowVec.size() == OVERFLOW_SIZE) {
        std::merge(mainVecs[activeIdx].begin(), mainVecs[activeIdx].end(),
                   overflowVec.begin(), overflowVec.end(),
                   mainVecs[1 - activeIdx].begin());

        mainVecs[activeIdx].clear();
        overflowVec.clear();
        activeIdx = 1 - activeIdx;
    }
}

void draw() {
    std::vector<Entry>::const_iterator mainIt = mainVecs[activeIdx].begin();
    std::vector<Entry>::const_iterator mainEndIt = mainVecs[activeIdx].begin();

    std::vector<Entry>::const_iterator overflowIt = overflowVec.begin();
    std::vector<Entry>::const_iterator overflowEndIt = overflowVec.end();

    for (;;) {
        if (overflowIt == overflowEndIt) {
            if (mainIt == mainEndIt) {
                break;
            }
            draw(*mainIt);
            ++mainIt;
        } else if (mainIt == mainEndIt) {
            if (overflowIt == overflowEndIt) {
                break;
            }
            draw(*overflowIt);
            ++overflowIt;
        } else if (*mainIt < *overflowIt) {
            draw(*mainIt);
            ++mainIt;
        } else {
            draw(*overflowIt);
            ++overflowIt;
        }
    }
}

关于c++ - Z 排序的 3d 对象列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26634740/

相关文章:

linux - SDL2/GL 上下文在 linux mint 上默认为 v3.0

java - 如何在不进行矩阵变换的情况下旋转glDrawArrays的对象?

java - 开始使用jogl,找不到DebugGL类

c++ - 如何定义创建前一个对象的默认构造函数

c++ - 运算符重载 +=

c++ - 是否有任何 100% C++11 兼容的 std 实现?

c++ - 为 C++ 中读取的每一行执行特定功能

c++ - 无法在 Eclipse 中构建项目

c++ - 无法在 Windows 桌面的 Microsoft Visual Studio Express 2013 中打开源文件 GL/glut.h

c++ - cin 跳过 while