Reputation:
I am learning the algorithm for voxelization of triangular meshes, which involves testing the intersection between triangles and AABB. I found the Separating Axis Theorem or SAT algorithm online, which requires the vertices of triangles and AABB to be projected onto 13 separation axes for judgment. Algorithms can work, but their efficiency is very low, and there may be a hundred times difference compared to Open3D. How should I improve them, or what better algorithms are available?
bool checkTriangleIntersectAABB(const std::vector<Eigen::Vector3d>& vTriangle, const Eigen::Vector3d& vCenter, const Eigen::Vector3d& vExtent)
{
std::vector<Eigen::Vector3d> Axis;
std::vector<Eigen::Vector3d> XYZNormal = { Eigen::Vector3d(1, 0, 0), Eigen::Vector3d(0, 1, 0), Eigen::Vector3d(0, 0, 1) };
std::vector<Eigen::Vector3d> Point = { vTriangle[0] - vCenter, vTriangle[1] - vCenter, vTriangle[2] - vCenter };
std::vector<Eigen::Vector3d> Edge = { Point[0] - Point[1], Point[1] - Point[2], Point[2] - Point[0] };
Axis.push_back(XYZNormal[0]);
Axis.push_back(XYZNormal[1]);
Axis.push_back(XYZNormal[2]);
for (int i = 0; i < XYZNormal.size(); i++)
{
for (int j = 0; j < Edge.size(); j++)
{
Axis.push_back(XYZNormal[i].cross(Edge[j]).normalized());
}
}
Axis.push_back(Edge[0].cross(Edge[1]).normalized());
for (int i = 0; i < Axis.size(); i++)
{
std::vector<double> PointProjection = { Point[0].dot(Axis[i]), Point[1].dot(Axis[i]), Point[2].dot(Axis[i]) };
double CubeProjection = vExtent.x() * abs(XYZNormal[0].dot(Axis[i]))
+ vExtent.y() * abs(XYZNormal[1].dot(Axis[i]))
+ vExtent.z() * abs(XYZNormal[2].dot(Axis[i]));
double MinPoint = std::min({ PointProjection[0], PointProjection[1], PointProjection[2] });
double MaxPoint = std::max({ PointProjection[0], PointProjection[1], PointProjection[2] });
if (std::max(-MaxPoint, MinPoint) > CubeProjection) return false;
}
return true;
}
std::shared_ptr<open3d::geometry::VoxelGrid> voxelizeInSurface(const open3d::geometry::TriangleMesh& vTriangleMesh, double vVoxelSize)
{
_ASSERTE(vVoxelSize > 0);
Eigen::AlignedBox3d BoundBox;
for (auto& Vertex : vTriangleMesh.vertices_) BoundBox.extend(Vertex);
Eigen::Vector3d MinBound = BoundBox.min(), BoundSize = BoundBox.max() - BoundBox.min();
auto VoxelGrid = std::make_shared<open3d::geometry::VoxelGrid>();
VoxelGrid->origin_ = MinBound;
VoxelGrid->voxel_size_ = vVoxelSize;
double HalfVoxelSize = vVoxelSize / 2;
for (auto& Indexs : vTriangleMesh.triangles_)
{
std::vector<Eigen::Vector3d> Triangle = { vTriangleMesh.vertices_[Indexs[0]],
vTriangleMesh.vertices_[Indexs[1]],
vTriangleMesh.vertices_[Indexs[2]] };
Eigen::AlignedBox3d TriangleBound;
TriangleBound.extend(Triangle[0]);
TriangleBound.extend(Triangle[1]);
TriangleBound.extend(Triangle[2]);
Eigen::Vector3i MinIndex = Eigen::floor((TriangleBound.min() - MinBound).array() / vVoxelSize).cast<int>();
Eigen::Vector3i MaxIndex = Eigen::floor((TriangleBound.max() - MinBound).array() / vVoxelSize).cast<int>();
for (int x = MinIndex[0]; x <= MaxIndex[0]; x++)
{
for (int y = MinIndex[1]; y <= MaxIndex[1]; y++)
{
for (int z = MinIndex[2]; z <= MaxIndex[2]; z++)
{
Eigen::Vector3i Index(x, y, z);
Eigen::Vector3d Center = (Index.cast<double>() * vVoxelSize + MinBound).array() + HalfVoxelSize;
Eigen::Vector3d Extent(HalfVoxelSize, HalfVoxelSize, HalfVoxelSize);
if (checkTriangleIntersectAABB(Triangle, Center, Extent))
{
Eigen::Vector3d Color = (Center - MinBound).array() / BoundSize.array();
VoxelGrid->voxels_[Index] = open3d::geometry::Voxel(Index, Color);
}
}
}
}
}
return VoxelGrid;
}
Is this a problem with algorithm logic, or is it caused by the use of std:: vector in the code, or the generation of copy constructors
Upvotes: 1
Views: 154
Reputation: 1450
The Akenine-Moller solution for triangle:AABB intersection using SAT is quite inefficient and also fails on degenerate triangles. But it's the solution that always comes up in searches so it's easy to focus on it.
An improvement can be found in the work by Schwarz and Siedel here: https://michael-schwarz.com/research/publ/files/vox-siga10.pdf
Their approach is quite elegant:
You might find the rest of the paper interesting too, since it covers the topic that you're inquiring about.
However this is still has the limitation that positive intersections are only detected after all the potential disjoint cases have been checked. There is no early out in the cases where the triangle and the AABB intersect. So I use another approach which is faster than both the Akenine-Moller and Schwarz-Siedel:
A Unity project with C# code for this solution as well as the other two can be found here: https://github.com/creijon/intersections
Upvotes: 0
Reputation: 77
In order to reduce complexity you can use a rasterization algorithm.
vTriangleMesh.vertices_[Indexs[0]]
to vTriangleMesh.vertices_[Indexs[1]]
vTriangleMesh.vertices_[Indexs[2]]
if the first is the longest).Upvotes: 2