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 ...
随机推荐
- js 如何取消promise
1: 使用reject function hello() { let _res, _rej: any; const promise = new Promise((res, rej) => { _ ...
- NGK全球启动大会正式启动,资产上链的前景与机会在哪?
据彭博社报道,加州时间11月25日,NGK全球启动大会在美国硅谷圆满落幕,本次NGK全球启动大会为NGK全球化进程正式拉开了帷幕. 众多业界人士共襄盛举,共同进行探讨未来公链发展的去向和契机. 当前, ...
- 对DevOps的九大误解,是时候纠正了!
DevOps是开发和运维的结合,有助于集成和自动化测试过程以及部署存储库,还提供了透明度以及灵活性.DevOps的目标如下: ●更快的上市时间(TTM). ●减少各种修复之间的前置时间.●提高部署频率 ...
- 3. Vue语法--计算属性
一. 计算属性 1. 什么是计算属性? 通常, 我们是在模板中, 通过插值语法显示data的内容, 但有时候我们可能需要在{{}}里添加一些计算, 然后在展示出来数据. 这时我们可以使用到计算属性 先 ...
- call、apply和bind的实现
call方法 基础版, 只能修改指向,不能传参 Function.prototype.myCall = function(context) { // 获取调用者,这里为bar context.fn = ...
- 京东数科二面:常见的 IO 模型有哪些?Java 中的 BIO、NIO、AIO 有啥区别?
IO 模型这块确实挺难理解的,需要太多计算机底层知识.写这篇文章用了挺久,就非常希望能把我所知道的讲出来吧!希望朋友们能有收货!为了写这篇文章,还翻看了一下<UNIX 网络编程>这本书,太 ...
- 后台用JSONObject接收前端传过来的字符串数组,并转成集合(JSONObject---JSONArray---List)
前端传递数据: handleSubmit() {this.dialogVisible = false; const param = { 'bidSampleImgList': this.fileLis ...
- 免费报表工具 积木报表(JiMuReport)的安装
分享一b/s报表工具(服务),积木报表(JiMuReport),张代浩大佬出品. 官网:http://www.jimureport.com/ 离线版官方下载:https://github.com/zh ...
- Idea 报错 xxxx too long
问题:写单元测试,debug时,报错如下图 解决方法1: 在项目/.idea/workspace.xml文件中添加一行代码如下 <component name="PropertiesC ...
- freebsd root 登录 KDE SDDM
sddm.conf 文件现在默认不会自动生成了.需要自己创建:ee /usr/local/etc/sddm.conf写入MinimumUid=0MaximumUid=00就是root用户.然后更改/u ...