const int nBuckets=12;
std::vector<Bounds3> buckets(nBuckets);
std::vector<int> numbuckets(nBuckets);
BVHBuildNode* BVHAccel::recursiveSAHBuild(std::vector<Object*> objects)
{
BVHBuildNode* node = new BVHBuildNode();
// Compute bounds of all primitives in BVH node
Bounds3 bounds;
for (int i = 0; i < objects.size(); ++i)
bounds = Union(bounds, objects[i]->getBounds());
int dim = bounds.maxExtent();
int b=0;
if (objects.size() == 1) {
// Create leaf _BVHBuildNode_
node->bounds = objects[0]->getBounds();
node->object = objects[0];
node->left = nullptr;
node->right = nullptr;
return node;
}
else if (objects.size() == 2) {
node->left = recursiveSAHBuild(std::vector{objects[0]});
node->right = recursiveSAHBuild(std::vector{objects[1]});
node->bounds = Union(node->left->bounds, node->right->bounds);
return node;
}
else{
for(int i=0;i<objects.size();++i){
switch(dim){
case 0:
b=nBuckets*bounds.Offset(objects[i]->getBounds().Centroid()).x;
break;
case 1:
b=nBuckets*bounds.Offset(objects[i]->getBounds().Centroid()).y;
break;
case 2:
b=nBuckets*bounds.Offset(objects[i]->getBounds().Centroid()).z;
break;
}
if(b==nBuckets) b=nBuckets-1;
numbuckets[b]= numbuckets[b]+1;
buckets[b]=Union(buckets[b],objects[i]->getBounds());
}
float cost[nBuckets-1];
for(int i=0;i<nBuckets-1;++i){
Bounds3 b0,b1;
int count0=0,count1=0;
for(int j=0;j<=i;++j){
b0=Union(b0,buckets[j]);
count0+=numbuckets[j];
}
for(int j=i+1;j<nBuckets;++j){
b1=Union(b1,buckets[j]);
count1+=numbuckets[j];
}
cost[i]=1+(count0*b0.SurfaceArea()+count1*b1.SurfaceArea())/bounds.SurfaceArea();
}
float minCost=cost[0];
int minCostSplitBucket=0;
for(int i=1;i<nBuckets-1;++i){
if(cost[i]<minCost){
minCost=cost[i];
minCostSplitBucket=i;
}
}
int split=0;
for(int m=0;m<=minCostSplitBucket;++m)
split+=numbuckets[m];
auto beginning = objects.begin();
auto middling = objects.begin() + (split);
auto ending = objects.end();
auto leftshapes = std::vector<Object*>(beginning, middling);
auto rightshapes = std::vector<Object*>(middling, ending);
assert(objects.size() == (leftshapes.size() + rightshapes.size()));
node->left = recursiveSAHBuild(leftshapes);
node->right = recursiveSAHBuild(rightshapes);
node->bounds = Union(node->left->bounds, node->right->bounds);
}
return node;
}
实现基于SAH的BVH构建。
①基于表面积的启发式评估划分方法(Surface Area Heuristic,SAH),
这种方法通过对求交代价和遍历代价进行评估,给出了每一种划分的代价(Cost),
而我们的目的便是去寻找代价最小的划分。
Cost=Ctrav+(S(A)*a+S(B)*b)/S(C);
其中S(A),S(B)分别为当前划分方式下左右两个包围盒的面积,S(C)为当前结点包含所有图元的包围盒面积;
a,b分别为左右两个包围盒分别包含的图元数;
Ctrav为遍历树状结构的代价,此处设置为1
②在实现的时候,相比于计算可能划分的代价然后寻找代价最小的划分,
一种更好的办法是将节点 所包围的空间沿着跨度最长的那个坐标轴的方向将空间均等的划分为若干个桶(Buckets),
划分只会出现在桶与桶之间的位置上。本算法中设置的桶的个数为12.