关于该项目的:
我正在开发一个Opengl射线追踪器,它能够加载obj文件并跟踪它。我的应用程序用assimp加载obj文件,然后使用着色器存储对象将所有三角形面(顶点和索引)发送到片段着色器。基本结构即将呈现结果从碎片着色器到一个四边形。
当我加载更大的obj-s (超过100个三角形)时,计算机需要很长时间才能完成交叉操作,所以我开始创建一个BVH树来加速这个过程。我的BVH根据AABB中包含的三角形的平均中值递归地将空间分割成两个轴对齐包围框。
我成功地构建了BVH树结构(在CPU上),现在我想把它转换成一个简单的数组,然后将它发送到片段着色器(到一个着色器存储缓冲区)。
下面是负责将BVH根节点转换为数组的方法:
BvhNode bvhNode; //global variable
BvhNode* putNodeIntoArray() {
int size=bvhNode.getNumberOfNodes();
BvhNode nodesArray[size];
int current_index = 0;
vector<BvhNode> tempNodes;
tempNodes.push_back(bvhNode);
BvhNode current_node;
while (!tempNodes.empty()) {
current_node = tempNodes.at(0);
tempNodes.erase(tempNodes.begin());
nodesArray[current_index] = current_node;
if(!current_node.isLeaf)
{
tempNodes.push_back(current_node.children.at(0)); // Segmentation error occurs here!
tempNodes.push_back(current_node.children.at(1));
}
current_index++;
}
return nodesArray;
}
关于问题的:
我不知道为什么,但是当我想要push_back tempNodes
向量的第一个子向量时,它会给我一个分割错误(上面的注释可以看到确切的位置)。在我看来,current_node.children.at(0)
似乎不存在,但实际上它是根据调试器存在的。我试图编写reference (&)操作符:tempNodes.push_back(¤t_node.children.at(0));
,但在本例中,它给出了对象的奇怪坐标。我试图将函数中的变量定义为全局变量- -试图避免-的作用域问题,并将current_node
变量定义为指针。不幸的是,他们都没有给我更好的结果。
这是我的BvhNode类,如果有帮助的话:
class BvhNode {
public:
BBox bBox;
int depthOfNode;
vector<BvhNode> children;
int order;
bool isLeaf;
bool createdEmpty = false;
vector<glm::vec3> primitiveCoordinates;
BvhNode() {}
BvhNode(BvhNode *pNode) {}
BvhNode(vector<glm::vec3> &primitiveCoordinates) {
this->primitiveCoordinates = primitiveCoordinates; }
void buildTree(vector<glm::vec3>& indicesPerFaces, int depth) {... }
更新1:
我根据评论更新了方法。因此,我将返回值的类型更改为BvhNode*的向量。该算法工作良好,直到到达将叶节点放入std::向量的过程。所以当它开始把图的最后一层放到向量上时,它给了我一个错误:
Program received signal SIGSEGV, Segmentation fault.
0x00007fbda4d69c01 in __GI___libc_free (mem=0x555be3a9dba0) at malloc.c:3123
3123 malloc.c: No such file or directory.
我设法将七个节点(除了树叶级别之外的树的所有深度级别)放入向量中。我也试着运行valgring,但实际上valgring并没有给我任何错误,不像在CLion中那样。
这是我改进的方法。我对分割错误的位置和修复进行了评论。
BvhNode bvhNode;
vector<BvhNode> putNodeIntoArray() {
int size=bvhNode.getNumberOfNodes();
// FIX: I modified the array into an std::vector
vector<BvhNode> nodesArray(size);
int current_index = 0;
vector<BvhNode> tempNodes;
tempNodes.push_back(bvhNode);
BvhNode current_node;
while (!tempNodes.empty()) {
current_node = tempNodes.front();// Segmentation error!!
tempNodes.erase(tempNodes.begin());
nodesArray.at(current_index)=current_node;
nodesArray.at(current_index).children.clear();
// FIX: I also modified this not to iterate through leaves' children, because they don't exist.
if(!current_node.children.empty())
{
tempNodes.push_back(current_node.children.at(0));
tempNodes.push_back(current_node.children.at(1));
}
current_index++;
}
return nodesArray;
}
发布于 2020-05-09 04:57:32
实际上问题是在创建Bvh树的过程中。因为我想以2*i+1和2*i+2的形式到达子节点,所以我使用了一种方法来填充二叉树(每个级别都有最大的节点数)。在这种方法中,我不得不将空节点推到树上。
我创建了这样的空节点:
BvhNode emptyNode;
而不是这样:
BvhNode *emptyNode = new BvhNode();
因此,当方法完成时,emptyNode
的生存期就结束了。这就导致了分割错误。
此外,还有一个问题,正如Balá所指出的,在创建平坦的二叉树期间,我按值存储了BvhNode。这个选项使得应用程序速度慢得多,也是另一个可能导致分割错误的地方。
发布于 2020-05-07 13:05:16
您的向量按值在任何地方存储BvhNode
s。这意味着每次您push_back
一个节点时,都会调用它的复制构造函数,该构造函数依次在节点内复制children
向量成员,该成员复制自己的元素等等。这基本上会导致每次插入或删除节点时都复制/释放完整的子树。
这反过来会导致内存碎片,这最终会导致向量重新分配失败并导致分段错误。
如果没有完整的代码,我可以推荐以下两点:
。
https://stackoverflow.com/questions/61648217
复制相似问题