openvdb
openvdb copied to clipboard
Expected output of firstActive function in HDDA.h
Hello,
I am building a small voxel grid by building a nanovdb::Grid<nanovdb::FloatTree>
with 1s in places where voxels are non-empty (as far as I understand bool or mask grids are not yet supported in nanovdb).
To render, I then trace rays from the camera against the grid using the firstActive
function in nanovdb/nanovdb/util/HDDA.h
to get the index of the first active voxel intersected.
template<typename RayT, typename AccT>
inline __hostdev__ bool firstActive(RayT& ray, AccT& acc, Coord &ijk, float& t)
{
if (!ray.clip(acc.root().bbox()) || ray.t1() > 1e20) {// clip ray to bbox
return false;// missed or undefined bbox
}
static const float Delta = 1.0001f;// forward step-size along the ray to avoid getting stuck
t = ray.t0();// initiate time
ijk = RoundDown<Coord>(ray.start()); // first voxel inside bbox
for (HDDA<RayT, Coord> hdda(ray, acc.getDim(ijk, ray)); !acc.isActive(ijk); hdda.update(ray, acc.getDim(ijk, ray))) {
if (!hdda.step()) return false;// leap-frog HDDA and exit if ray bound is exceeded
t = hdda.time() + Delta;// update time
ijk = RoundDown<Coord>( ray(t) );// update ijk
}
return true;
}
If I output the normalized color of that outputed voxel's index in 3d space I obtain the following:
I found this other variant of first active intersection in an older github repo:
template<typename RayT, typename AccT>
inline __hostdev__ bool firstActiveVariant(RayT& iRay, AccT& acc, Coord &ijk, float& t)
{
using TreeT = nanovdb::NanoTree<float>;
TreeMarcher<TreeT::LeafNodeType, RayT, AccT> marcher(acc);
if (marcher.init(iRay)) {
const TreeT::LeafNodeType *node = nullptr;
float t0 = 0, t1 = 0;
while (marcher.step(&node, t0, t1)) {
DDA<RayT> dda;
dda.init(marcher.ray(), t0, t1);
do {
ijk = dda.voxel();
// CAVEAT:
// This is currently necessary becuse the voxel returned might not actually be innside the node!
// This is currently happening from time to time due to float precision issues,
// so we skip out of bounds voxels here...
auto localIjk = ijk - node->origin();
if (localIjk[0] < 0 || localIjk[1] < 0 || localIjk[2] < 0 || localIjk[0] >= 8 || localIjk[1] >= 8 || localIjk[2] >= 8)
continue;
const uint32_t offset = node->CoordToOffset(ijk);
if (node->isActive(offset)){
t = dda.time();
return true;
}
} while (dda.step());
}
}
return false;
}
This one gives me the expected result but seems to be quite expensive and has a CAVEAT note that doesn't seem very good either.
The input to both functions is exactly the same, namely, the world_ray transformed into index space using the ray's worldToIndexF
function and the device float grid accessor.
Is this a bug in the firstActive
function or am I using it the wrong way?
Let me know if you need any more details. Best, Philippe