背景

  光线与物体求交是光线追踪的主要时间瓶颈

  如果不进行优化,则对每条光线,我们都需要遍历场景中的全部物体并求交。而现在想建模一个小物体的表面,往往要几千甚至几万个三角形,一个商业级产品,屏幕内甚至可能同时存在几亿个三角形。

  想要提升效率,我们需要加速结构的帮助。

  类似于二分查找的思想,我们先判断光线是否与较大的集合相交,如果不相交,则该集合内的所有物体都无需求交,减少了原有的计算量。

  加速结构一般有两种

    一种是划分空间:如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 加速结构的更多相关文章

  1. Bounding Volume Hierarchy BVH in OpenCASCADE

    Bounding Volume Hierarchy BVH in OpenCASCADE eryar@163.com Abstract. Bounding Volume Hierarchy(BVH) ...

  2. BVH with SAH (Bounding Volume Hierarchy with Surface Area Heuristic)

      - BVH with SAH (Bounding Volume Hierarchy  with Surface Area Heuristic) -      0. Overview 包围层次盒(B ...

  3. 3D游戏引擎中常见的三维场景管理方法

    对于一个有很多物体的3D场景来说,渲染这个场景最简单的方式就是用一个List将这些物体进行存储,并送入GPU进行渲染.当然,这种做法在效率上来说是相当低下的,因为真正需要渲染的物体应该是视椎体内的物体 ...

  4. 如何用WebGPU流畅渲染百万级2D物体?

    大家好~本文使用WebGPU和光线追踪算法,从0开始实现和逐步优化Demo,展示了从渲染500个2D物体都吃力到流畅渲染4百万个2D物体的优化过程和思路 目录 需求 成果 1.选择渲染的算法 2.实现 ...

  5. Peter Shirley-Ray Tracing The Next Week

    Peter Shirley-Ray Tracing The Next Week(2016) 原著:Peter Shirley 英文原著地址 密码: urji 第二本书主要介绍了运动模糊,BVH(层次包 ...

  6. Peter Shirley Ray Tracing in One Weekend(下篇)

    Peter Shirley-Ray Tracing in One Weekend (2016) 原著:Peter Shirley 下篇主要对本书的后5章节进行学习,包括材质球的Metal,和Diele ...

  7. 图形学3D渲染管线学习

    图形学3D渲染管线 DX和OpenGL左右手坐标系不同,会有一些差距,得出的矩阵会不一样; OpenGL的投影平面不是视景体的近截面: 顶点(vertexs) 顶点坐标,颜色,法线,纹理坐标(UV), ...

  8. PBRT笔记(2)——BVH

    BVH 构建BVH树分三步: 计算每个图元的边界信息并且存储在数组中 使用指定的方法构建树 优化树,使得树更加紧凑 //BVH边界信息,存储了图元号,包围盒以及中心点 struct BVHPrimit ...

  9. 【Ray Tracing The Next Week 超详解】 光线追踪2-2

    Chapter 2:Bounding Volume Hierarchies 今天我们来讲层次包围盒,乍一看比较难,篇幅也多,但是咱们一步一步来,相信大家应该都能听懂 BVH 和 Perlin text ...

随机推荐

  1. c#winform主题实现的一个方法

    winform的主题实现没有bs里面那么舒服,下面做了一个简单实现,记录一下. 1.一个接口,需要做主题的控件.窗体都要实现这个接口 /// <summary> /// 使用主题的控件.窗 ...

  2. 微信小程序:添加全局的正在加载中图标效果

    在发送请求的时候,显示一个正在加载中的小图标.在加载下一页的时候也显示正在加载中.同时数据请求回来了,把加载中进行关闭. 开发----API-----界面 在哪里添加这两段代码会比较方便呢?一个项目有 ...

  3. shell编程基础二

    一.流程控制 while循环:只要条件满足一直循环 read -p "请输入一个数字:" white_data while [ ${white_data} -lt 20 ] do ...

  4. 第6章 for循环

    目标 掌握for循环的使用方法 理解for循环的嵌套 在第3章中,我们学习了times循环.times循环可以让一段代码重复执行指定的次数. 本章我们将学习另一种循环结构--for循环.它同样能让一段 ...

  5. 如果要是把标记为2的那一行Lable1.Text改为其他的Lable显示执行代码

    转: 如果要是把标记为2的那一行Lable1.Text改为其他的Lable显示执行代码 如图,程序很简单,文件路径也没问题,为什么会报错,百思不得其解?[url]https://book.douban ...

  6. 人脸检测数据源制作与基于caffe构架的ALEXNET神经网络训练

    本篇文章主要记录的是人脸检测数据源制作与ALEXNET网络训练实现检测到人脸(基于caffe). 1.数据获取 数据获取: ① benchmark是一个行业的基准(数据库.论文.源码.结果),例如WI ...

  7. AQS源码解读(ReentrankLock的公平锁和非公平锁)

    构建Debug代码: 1 package com.hl.interview.lock; 2 3 import java.util.Scanner; 4 import java.util.concurr ...

  8. 在 .NET Core 中应用六边形架构

    在本文中,您会看到一个Web API应用的模板,在.NET Core 中应用了六边形架构,并且里面包含了一些基础功能. 介绍 这是一个模板项目,里面集成了一些必备的基础功能,当我们需要开发一个新项目时 ...

  9. Vue.js 实现的 3D Tab菜单

    今天给大家带来一款基于VueJS的3D Tab菜单,它跟我们之前分享的许多CSS3 Tab菜单不同的是,它可以随着鼠标移动呈现出3D立体的视觉效果,每个tab页面还可以通过CSS自定义封面照片.它的核 ...

  10. windows跳转端口

    //将客户机端口内网33306转发到外网,在通过本地连接ssh -L 3306:10.0.0.208:3306 ttx@180.180.180.182--通过git bash执行命令--10.0.0. ...