Bounding Volume Hierarchies 加速结构
背景
光线与物体求交是光线追踪的主要时间瓶颈。
如果不进行优化,则对每条光线,我们都需要遍历场景中的全部物体并求交。而现在想建模一个小物体的表面,往往要几千甚至几万个三角形,一个商业级产品,屏幕内甚至可能同时存在几亿个三角形。
想要提升效率,我们需要加速结构的帮助。
类似于二分查找的思想,我们先判断光线是否与较大的集合相交,如果不相交,则该集合内的所有物体都无需求交,减少了原有的计算量。
加速结构一般有两种:
一种是划分空间:如K-D Tree
一种是划分物体:如BVH
下面我们将主要介绍BVH。
BVH(Bounding Volume Hierarchies)
Bounding Volume
由于和不规则物体求交,计算量较大,我们通常给每个物体创建一个Bounding Volume,先与Bounding Volume求交,通过了Bounding Volume的测试,才与物体求交,这样减少了平均的时间开销。
那么bounding volume具体是什么样的结构呢,可以肯定的是,与该结构求交的速度一定需要非常快。通常我们会选用axis-aligned bounding boxes(AABB)作为具体的求交结构,顾名思义,即棱边分别平行于坐标轴的盒子。在实际使用中,它被证明,比大多数的结构更快。
Hierachy
hierachy体现在,整个BVH结构是一棵树,树的每个节点,都是一个bounding box,其中的object的box都为叶子节点,中间节点为构造出的BVH Node用于更高层级的求交。只有和树高层的bounding box相交,才能进入树的更深层。
实现思路
首先,每个物体都需要有各自的aabb用于求交,因此我们需要先创建一个aabb类,其中包括求交的函数。
再在每个object中加入求其aabb的具体方法。这样我们就有了物体的bounding box,并有了与它们求交的方法。
其次,我们需要构建BVH层级,建立层级不仅需要上述的叶子节点,也需要为了实现层级加速的中间结点,我们会创建中间节点的类,并在其中具体实现层级求交。
创建aabb类
创建一个box很简单,关键是如何判断是否与box相交呢?
将直线和box的六个平面相交,会有6个交点,即便box与ray相交,这些交点也并非全部落在box的xyz范围之内,如何根据这些交点,判断ray和box的相交情况呢?
这里的技巧是:ray与box的x,y,z方向的平面,都分别会有两个交点,记录他们的t值,如x方向的平面tx0
,tx1
。y,z方向同理。
则,若[tx0,tx1]
,[ty0,ty1]
,[tz0,tz1]
三个区间有交集,则ray和box相交。
.
关于这个技巧,https://www.cnblogs.com/lv-anchoret/p/10284085.html 已经有了非常详细的说明,本文便不再赘述。
class aabb {
public:
aabb() {}
//三个方向上的左边界 和 三个方向上的右边界
aabb(const vec3& a, const vec3& b) { _min = a; _max = b; }
vec3 min() const { return _min; }
vec3 max() const { return _max; }
bool hit(const ray& r, float tmin, float tmax) const {
//三个维度,不断缩小可能的交集的范围
for (int a = 0; a < 3; a++) {
float invD = 1.0f / r.direction()[a];
float t0 = (min()[a] - r.origin()[a])*invD;
float t1 = (max()[a] - r.origin()[a])*invD;
//确保t0<t1,在射线朝某轴负方向射出时t0>t1
if (invD < 0.0f)
std::swap(t0, t1);
tmin = t0 > tmin ? t0 : tmin;
tmax = t1 < tmax ? t1 : tmax;
if (tmax <= tmin)
return false;
}
return true;
}
vec3 _min;
vec3 _max;
};
求具体类型object的bounding box
球:aabb的中心为球心,边长为球直径。
bool sphere::bounding_box(float t0, float t1, aabb& box) const
{
box = aabb(center - vec3(radius, radius, radius), center + vec3(radius, radius, radius));
return true;
}
运动的球:其开始位置的box1,结束位置的box2,对box1,box2组合后求得的box3即为结果。
bool moving_sphere::bounding_box(float t0, float t1, aabb& box) const {
aabb box0(center(t0) - vec3(radius, radius, radius), center(t0) + vec3(radius, radius, radius));
aabb box1(center(t1) - vec3(radius, radius, radius), center(t1) + vec3(radius, radius, radius));
box = surrounding_box(box0, box1);
return true;
}
aabb surrounding_box(aabb box0, aabb box1)
{
vec3 small(fmin(box0.min().x(), box1.min().x()),
fmin(box0.min().y(), box1.min().y()),
fmin(box0.min().z(), box1.min().z()));
vec3 big(fmax(box0.max().x(), box1.max().x()),
fmax(box0.max().y(), box1.max().y()),
fmax(box0.max().z(), box1.max().z()));
return aabb(small, big);
}
bool hitable_list::bounding_box(float t0, float t1, aabb& box) const {
if (list_size < 1) return false;
aabb temp_box;
bool first_true = list[0]->bounding_box(t0, t1, temp_box);
if (!first_true)
return false;
else
box = temp_box;
for (int i = 1; i < list_size; i++) {
if (list[i]->bounding_box(t0, t1, temp_box)) {
box = surrounding_box(box, temp_box);
}
else
return false;
}
return true;
}
建立bounding box的 hierarchy
这里仅采用最简单的按物体数均匀划分的方式,先建立最高层级的node,再对两个子树进行递归调用。
在三个分量上对物体排序,以便就近划分。
class bvh_node : public hitable {
public:
bvh_node() {}
bvh_node(hitable **l, int n, float time0, float time1);
virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
virtual bool bounding_box(float t0, float t1, aabb& box) const;
hitable *left;
hitable *right;
aabb box;
};
//需要输入全部object,才能建立加速结构
bvh_node::bvh_node(hitable **l, int n, float time0, float time1) {
int axis = int(3 * random_double());
if (axis == 0)
qsort(l, n, sizeof(hitable *), box_x_compare);
else if (axis == 1)
qsort(l, n, sizeof(hitable *), box_y_compare);
else
qsort(l, n, sizeof(hitable *), box_z_compare);
if (n == 1) {
left = right = l[0];
}
else if (n == 2) {
left = l[0];
right = l[1];
}
else {
//按物体数量均匀划分
left = new bvh_node(l, n / 2, time0, time1);
right = new bvh_node(l + n / 2, n - n / 2, time0, time1);
}
//根据子物体的aabb创建本bvh_node的aabb
aabb box_left, box_right;
if (!left->bounding_box(time0, time1, box_left) || !right->bounding_box(time0, time1, box_right))
std::cerr << "no bounding box in bvh_node constructor\n";
box = surrounding_box(box_left, box_right);
}
在random_scene()中有:
return new bvh_node(list, i, 0.0, 1.0);
在main()中有:
hitable *world = random_scene();
ray r = cam.get_ray(u, v);
col += color(r, world,0);
求交流程
先判断是否与自己的box相交,若相交,则分别与子节点测试,递归调用hit
bool bvh_node::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
//如果和自己的box交,则测试子节点的box
if (box.hit(r, t_min, t_max)) {
hit_record left_rec, right_rec;
//递归调用,与子物体交
bool hit_left = left->hit(r, t_min, t_max, left_rec);
bool hit_right = right->hit(r, t_min, t_max, right_rec);
if (hit_left && hit_right) {
//两边都交,判断哪边在先
if (left_rec.t < right_rec.t)
rec = left_rec;
else
rec = right_rec;
return true;
}
else if (hit_left) {
rec = left_rec;
return true;
}
else if (hit_right) {
rec = right_rec;
return true;
}
else
return false;
}
else return false;
}
至此就建立好整个BVH加速结构啦。
Bounding Volume Hierarchies 加速结构的更多相关文章
- Bounding Volume Hierarchy BVH in OpenCASCADE
Bounding Volume Hierarchy BVH in OpenCASCADE eryar@163.com Abstract. Bounding Volume Hierarchy(BVH) ...
- BVH with SAH (Bounding Volume Hierarchy with Surface Area Heuristic)
- BVH with SAH (Bounding Volume Hierarchy with Surface Area Heuristic) - 0. Overview 包围层次盒(B ...
- 3D游戏引擎中常见的三维场景管理方法
对于一个有很多物体的3D场景来说,渲染这个场景最简单的方式就是用一个List将这些物体进行存储,并送入GPU进行渲染.当然,这种做法在效率上来说是相当低下的,因为真正需要渲染的物体应该是视椎体内的物体 ...
- 如何用WebGPU流畅渲染百万级2D物体?
大家好~本文使用WebGPU和光线追踪算法,从0开始实现和逐步优化Demo,展示了从渲染500个2D物体都吃力到流畅渲染4百万个2D物体的优化过程和思路 目录 需求 成果 1.选择渲染的算法 2.实现 ...
- Peter Shirley-Ray Tracing The Next Week
Peter Shirley-Ray Tracing The Next Week(2016) 原著:Peter Shirley 英文原著地址 密码: urji 第二本书主要介绍了运动模糊,BVH(层次包 ...
- Peter Shirley Ray Tracing in One Weekend(下篇)
Peter Shirley-Ray Tracing in One Weekend (2016) 原著:Peter Shirley 下篇主要对本书的后5章节进行学习,包括材质球的Metal,和Diele ...
- 图形学3D渲染管线学习
图形学3D渲染管线 DX和OpenGL左右手坐标系不同,会有一些差距,得出的矩阵会不一样; OpenGL的投影平面不是视景体的近截面: 顶点(vertexs) 顶点坐标,颜色,法线,纹理坐标(UV), ...
- PBRT笔记(2)——BVH
BVH 构建BVH树分三步: 计算每个图元的边界信息并且存储在数组中 使用指定的方法构建树 优化树,使得树更加紧凑 //BVH边界信息,存储了图元号,包围盒以及中心点 struct BVHPrimit ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-2
Chapter 2:Bounding Volume Hierarchies 今天我们来讲层次包围盒,乍一看比较难,篇幅也多,但是咱们一步一步来,相信大家应该都能听懂 BVH 和 Perlin text ...
随机推荐
- 一条sql语句的执行过程
一条select语句执行流程 第一步:连接器 连接器负责跟客户端建立连接.获取权限.维持和管理连接.如果用户名密码验证通过后,连接器会到权限表里面查出你拥有的权限.之后该连接的权限验证都依赖于刚查出来 ...
- hadoop的lzo支持
目录 1.下载相关文件: 2.Configure LZO to build a shared library (required) and use a package-specific prefix ...
- Dubbo与Zookeeper开发
1.Dubbo 1.1RPC RPC全称是remote procedure call,即远程过程调用.比如有两台服务器A和B,它们上面分别部署了一个服务.此时B服务器想调用A服务器上提供的方法,由于不 ...
- 基于docker快速搭建hbase集群
一.概述 HBase是一个分布式的.面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文"Bigtable:一个结构化数据的分布式存储系统".就像Bigt ...
- InnoDB存储引擎——页和记录(行)
一.InnoDB页 InnoDB是一个将表中的数据存储到磁盘上的存储引擎,所以即使关机后重启我们的数据还是存在的.而真正处理数据的过程是发生在内存中的,所以需要把磁盘中的数据加载到内存中,如果是处理写 ...
- 后端程序员之路 24、Redis hiredis
Redishttps://redis.io/ Redis快速入门 - Redis教程http://www.yiibai.com/redis/redis_quick_guide.html wget ht ...
- 运营好帮手| 华为DTM助电商类应用实现营销数据快速跟踪
对于电商来说,销售额就是生命线,业务运营人员需要实时关注订单量,交易额,支付转化率等,并从各种维度对比分析,无论增幅或降幅,都需要马上找到原因,落地运营手段进行干预.快速准确的得到各种营销数据就显得格 ...
- 在scanf函数中占位符使用错误而产生的一些错误
出现的问题 在做编程题的的时候,遇到了一个很奇怪的错误,出问题的代码如下: 1 #include <cstdio> 2 using namespace std; 3 4 int main( ...
- AtCoder Beginner Contest 190
A Very Very Primitive Game int main() { int a, b, c; cin >> a >> b >> c; if(c == 0 ...
- FreeBSD 12.2 vmware 虚拟机镜像 bt 种子
安装了 KDE5 火狐浏览器 Fcitx 输入法 并进行了中文设置 替换软件源为国内可用. VirtualBox虚拟机也可以用 magnet:?xt=urn:btih:E88885631B57426 ...