**
lecture14 光线追踪加速
**
原因:对于一条光线,我没有必要对场景所有的三角面求交,那样太慢。
想法:构建包围盒,如果没有命中,则没有必要取对包围盒内的三角面求交。
KD-Tree 划分空间,每次选一个轴,对空间进行划分。可能存在有一个三角面被多个包围盒记录。
BVH:划分物体的三角面。同样的,每次选一个轴,之后可以采用取包围盒中心,中位数等方法进行划分。分布较为均匀。
细节就不多说了,作业里有。
**
作业6
**
包围盒求交
inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,
const std::array<int, 3>& dirIsNeg) const
{
Vector3f ttop = (pMax - ray.origin) * invDir;
Vector3f tbot = (pMin - ray.origin) * invDir;
Vector3f tmin = Vector3f::Min(ttop,tbot);
Vector3f tmax = Vector3f::Max(ttop,tbot);
float t0 = std::max(tmin.x , std::max(tmin.y,tmin.z));
float t1 = std::min(tmax.x , std::min(tmax.y,tmax.z));
return t0 <= t1 && t1 > 0;
}
BVH求交
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
Intersection mIntersection;
if(node == nullptr || !node->bounds.IntersectP(ray,ray.direction_inv,{0,0,0}))
return mIntersection;
if(node->left == nullptr && node->right == nullptr)
{
mIntersection = node->object->getIntersection(ray);
return mIntersection;
}
Intersection hit1,hit2;
hit1 = getIntersection(node->left,ray);
hit2 = getIntersection(node->right,ray);
mIntersection = hit1.distance <= hit2.distance? hit1:hit2;
return mIntersection;
}
SVH构建
BVHBuildNode* BVHAccel::recursiveBuild(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());
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 = recursiveBuild(std::vector{objects[0]});
node->right = recursiveBuild(std::vector{objects[1]});
node->bounds = Union(node->left->bounds, node->right->bounds);
return node;
}
else {
const int bucketSize = 16;
if(objects.size() < bucketSize / 4){
Bounds3 centroidBounds;
for (int i = 0; i < objects.size(); ++i)
centroidBounds =
Union(centroidBounds, objects[i]->getBounds().Centroid());
int dim = centroidBounds.maxExtent();//max axis 0:x 1:y 2:z
switch (dim) {
case 0:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().x <
f2->getBounds().Centroid().x;
});
break;
case 1:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().y <
f2->getBounds().Centroid().y;
});
break;
case 2:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().z <
f2->getBounds().Centroid().z;
});
break;
}
auto beginning = objects.begin();
auto middling = objects.begin() + (objects.size() / 2);
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 = recursiveBuild(leftshapes);
node->right = recursiveBuild(rightshapes);
node->bounds = Union(node->left->bounds, node->right->bounds);
}
else
{
Bounds3 centroidBounds;
for (int i = 0; i < objects.size(); ++i)
centroidBounds = Union(centroidBounds, objects[i]->getBounds().Centroid());
int dim = centroidBounds.maxExtent();//max axis 0:x 1:y 2:z
float bucketMin, bucketMax;
switch (dim)
{
case 0:
{
bucketMin = centroidBounds.pMin.x;
bucketMax = centroidBounds.pMax.x;
break;
}
case 1:
{
bucketMin = centroidBounds.pMin.y;
bucketMax = centroidBounds.pMax.y;
break;
}
case 2:
{
bucketMin = centroidBounds.pMin.z;
bucketMax = centroidBounds.pMax.z;
break;
}
}
Bucket buckets[bucketSize];
for(int i = 0 ; i < objects.size();++i){
int index = caculateIndex(objects[i]->getBounds().Centroid().get(dim),bucketMin,bucketMax,bucketSize);
buckets[index].count ++;
buckets[index].bounds = Union(buckets[index].bounds,objects[i]->getBounds());
}
Bounds3 leftBounds; int leftCount = 0;
float minCost , UArea; int minLeftIndex;
minCost = std::numeric_limits<float>::max();
for(int left = 0;left < bucketSize - 1; ++left)
{
leftBounds = Union(leftBounds,buckets[left].bounds);
leftCount += buckets[left].count;
Bounds3 rightBounds; int rightCount = 0;
for(int right = left + 1; right < bucketSize ; ++right)
{
rightBounds = Union(rightBounds,buckets[right].bounds);
rightCount += buckets[right].count;
}
UArea = Union(leftBounds,rightBounds).SurfaceArea();
float currentCost = (leftCount * leftBounds.SurfaceArea() + rightCount * rightBounds.SurfaceArea()) / UArea;
if(currentCost < minCost){
minCost = currentCost;
minLeftIndex = left;
}
}
std::vector<Object*> leftshapes,rightshapes;
for(int i = 0 ; i < objects.size();++i)
{
int index = caculateIndex(objects[i]->getBounds().Centroid().get(dim),bucketMin,bucketMax,bucketSize);
if(index <= minLeftIndex)
leftshapes.emplace_back(objects[i]);
else
rightshapes.emplace_back(objects[i]);
}
//std::cout<<leftshapes.size() << " " << rightshapes.size()<<std::endl;
node->left = recursiveBuild(leftshapes);
node->right = recursiveBuild(rightshapes);
node->bounds = Union(node->left->bounds, node->right->bounds);
}
}
return node;
}
值得注意的是,在构建的时候划分时根据重心进行划分。SVH的思想就是在当前划分的时候,我构建一个Cost,这个cost的定义就是命中左侧的概率 * 左侧三角面个数+命中右侧的概率 * 右侧三角面个数。这个命中率用左侧包围盒的面积 / 总的包围盒面积表示。这里构建了Buckets这个数据结构,把需要进行划分的空间划分为多个桶。当包围盒内三角面个数较少时,采用之前的中位数划分的办法。
这个方法,运行时间是8s,BVH是9s,快了一点。。一开始纠结的一点是,这个cost是否需要递归,因为cost的定义还包括了访问到这个节点的cost,定义为一个常数。后来我认为这个方法是基于当前的最优解,因此也没有必要递归那样也太复杂了。。于是计算的时候把这个常数丢了。我不知道这个结果是不是最终解,如果有大神还请指点下。