-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathbvh_binary.cpp
More file actions
160 lines (132 loc) · 4.1 KB
/
bvh_binary.cpp
File metadata and controls
160 lines (132 loc) · 4.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "bvh_binary.h"
#include <chrono> // std::chrono
#include <execution> // std::execution::par
#include <iomanip> // std::setprecision
#include <iostream> // std::cout
thread_local glm::vec3 MiniRay::Inverse;
// delta function in sec3 of the paper
// "Fast and Simple Agglomerative LBVH Construction"
__forceinline uint32_t Delta(const std::vector<glm::uvec3> &leaves, const uint32_t id)
{
return leaves[id + 1].z ^ leaves[id].z;
}
void LBVH::Build()
{
const auto start = std::chrono::steady_clock::now();
// object bounding box
Box.Initialize();
for (const auto &p : Ps)
Box.Expand(p);
// number of triangles
const auto T = PIDs.size() / 3;
// allocate pair <reference, morton code>
std::vector<glm::uvec3> leaves(T);
// set order
for (auto i = 0; i < T; ++i)
leaves[i].x = i;
// set morton code
std::for_each(std::execution::par, leaves.begin(), leaves.end(), [&](glm::uvec3 &leaf)
{
const auto id0 = leaf.x * 3 + 0;
const auto id1 = leaf.x * 3 + 1;
const auto id2 = leaf.x * 3 + 2;
const auto centroid = (Ps[PIDs[id0]] + Ps[PIDs[id1]] + Ps[PIDs[id2]]) / 3.0f;
const auto unitcube = Box.Nomalize(centroid); // coord in unit cube
leaf.z = morton3D(unitcube.x, unitcube.y, unitcube.z); // morton code
});
// sort leaves by morton code in ascending order
std::sort(std::execution::par, std::begin(leaves), std::end(leaves), [&](const glm::uvec3 &l, const glm::uvec3 &r)
{
return (l.z < r.z);
});
// set order (because getting thread id in for_each is somewhat cumbersome...)
for (auto i = 0; i < T; ++i)
leaves[i].y = i;
// number of nodes
const auto N = T - 1;
// allocate inner nodes
Nodes.resize(N);
// otherBounds in algorithm 1 of the paper
// "Massively Parallel Construction of Radix Tree Forests for the Efficient Sampling of Discrete Probability Distributions"
// https://arxiv.org/pdf/1901.05423.pdf
std::vector<std::atomic<uint32_t>> other_bounds(N);
std::for_each(std::execution::par, other_bounds.begin(), other_bounds.end(), [&](std::atomic<uint32_t> &b)
{
b.store(invalid);
});
// for each leaf
std::for_each(std::execution::par, leaves.begin(), leaves.end(), [&](glm::uvec3 &leaf)
{
// current leaf/node id
auto current = leaf.y;
// range
uint32_t L = current;
uint32_t R = current;
// leaf aabb
const auto id = leaf.x * 3;
AABB aabb;
aabb.Initialize();
aabb.Expand(Ps[PIDs[id + 0]]);
aabb.Expand(Ps[PIDs[id + 1]]);
aabb.Expand(Ps[PIDs[id + 2]]);
aabb.Min -= Box.Min;
aabb.Max -= Box.Min;
// current is leaf or node?
auto is_leaf = true;
while (1)
{
// the whole range is covered
if (0 == L && R == N)
{
Root = current;
break;
}
// leaf/node index
const auto index = is_leaf ? leaves[current].x * 2 + 1 : current * 2;
// choose parent
uint32_t previous, parent;
if (0 == L || (R != N && Delta(leaves, R) < Delta(leaves, L - 1)))
{
// parent is right and "L" doesn't change
parent = R;
previous = other_bounds[parent].exchange(L);
if(invalid != previous)
R = previous;
Nodes[parent].L = index;
}
else
{
// parent is left and "R" doesn't change
parent = L - 1;
previous = other_bounds[parent].exchange(R);
if (invalid != previous)
L = previous;
Nodes[parent].R = index;
}
// expand aabb
Nodes[parent].Box.Expand(aabb);
// terminate this thread
if (invalid == previous)
break;
assert(L < R);
// ascend
current = parent;
aabb = Nodes[current].Box;
is_leaf = false;
}
});
std::for_each(std::execution::par, Nodes.begin(), Nodes.end(), [&](Node &node)
{
node.Box.Min += Box.Min;
node.Box.Max += Box.Min;
});
const auto end = std::chrono::steady_clock::now();
std::cout << "bvh: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
// debug
double sah = 0;
for (const auto &n : Nodes)
{
sah += n.Box.HalvedSurface();
}
std::cout << "sah: " << std::setprecision(16) << sah << std::endl;
}