Incorrect intersection results when using RTC_SCENE_FLAG_ROBUST
SallyLam opened this issue · comments
The intersection results are occasionally incorrect, even when using the flag RTC_SCENE_FLAG_ROBUST
. It reports a hit when there are actually no intersection between the ray and the reported triangle, and the tfar returned is some large number that doesn't make sense.
An example failure case is shown in the code below. The scene contains one triangle.
Embree returns an Intersection at t = 12291.2
.
In reality, there are no intersection between the ray and the triangle. Also, the distance between the ray origin and any vertices is less than 600, which is significantly different from the returned tfar.
It is very confusing why it returns a hit, and how it computes such a tfar.
I see this kind of errors quite often. From my observation, it tends to happen when the triangle is somewhat skinny. (In the example below, the triangle has edge lengths of around 11.159, 9.721, and 1.438, which is... quite skinny. However, it is still weird that we're getting a false positive hit.)
Could you please help investigate why this is happening and how to fix it? Thanks!
// The following code is adapted from Embree's example code on https://www.embree.org/api.html.
// The only differences are the scene build quality, scene flags, triangle position, and ray origin and direction.
#include <embree3/rtcore.h>
#include <limits>
#include <iostream>
int main()
{
RTCDevice device = rtcNewDevice(NULL);
RTCScene scene = rtcNewScene(device);
rtcSetSceneBuildQuality(scene, RTC_BUILD_QUALITY_HIGH);
rtcSetSceneFlags(scene, RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION | RTC_SCENE_FLAG_ROBUST);
RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE);
float *vb = (float *)rtcSetNewGeometryBuffer(geom,
RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * sizeof(float), 3);
vb[0] = -1.454641f; vb[1] = 5.068154f; vb[2] = -4.543064f; // 1st vertex
vb[3] = 0.877591f; vb[4] = -3.057641f; vb[5] = 2.740851f; // 2nd vertex
vb[6] = 0.577049f; vb[7] = -2.010513f; vb[8] = 1.802213f; // 3rd vertex
unsigned *ib = (unsigned *)rtcSetNewGeometryBuffer(geom,
RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * sizeof(unsigned), 1);
ib[0] = 0; ib[1] = 1; ib[2] = 2;
rtcCommitGeometry(geom);
rtcAttachGeometry(scene, geom);
rtcReleaseGeometry(geom);
rtcCommitScene(scene);
RTCRayHit rayhit;
rayhit.ray.org_x = 119.973877f; rayhit.ray.org_y = -438.062073f; rayhit.ray.org_z = 377.931061f;
rayhit.ray.dir_x = -0.208998f; rayhit.ray.dir_y = 0.728189f; rayhit.ray.dir_z = -0.652733f;
rayhit.ray.tnear = 0.f;
rayhit.ray.tfar = std::numeric_limits<float>::infinity();
rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
RTCIntersectContext context;
rtcInitIntersectContext(&context);
rtcIntersect1(scene, &context, &rayhit);
if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) {
std::cout << "Intersection at t = " << rayhit.ray.tfar << std::endl;
} else {
std::cout << "No Intersection" << std::endl;
}
rtcReleaseScene(scene);
rtcReleaseDevice(device);
}
FYI, I'm using the latest Embree 3.13.4 on macOS.
After a closer inspection of the data, I realized that the failing "triangle" actually has vertices that are basically collinear. The weird intersection result is less surprising now that the input geometry is not very reasonable.
That said, I found that if I tweak the ray, it doesn't give such false positive results, so it's not consistently failing when given collinear "triangles".
It will be nice if Embree can handle such cases more elegantly. :)
E.g., do not report an unreasonable intersection/tfar, or specify in the documentation that invalid triangles can lead to undefined behaviors.
This robustness problem has been with embree for some time. Even with 100% water tight geometry and the ROBUST flag on the ray will sometimes report "a miss" in some cases when it intersects an edge. I suppose no-one will notice this when used for visualization but for other applications it is frustrating. Sadly, if you posted such a question on the Optix forum you would get an answer instantly and a follow up from an nVidia dev. Yet here we are nearly a year later with no comment.
This robustness problem has been with embree for some time. Even with 100% water tight geometry and the ROBUST flag on the ray will sometimes report "a miss" in some cases when it intersects an edge. I suppose no-one will notice this when used for visualization but for other applications it is frustrating. Sadly, if you posted such a question on the Optix forum you would get an answer instantly and a follow up from an nVidia dev. Yet here we are nearly a year later with no comment.
The ray-triangle intersection algorithm used by embree is not perfectly watertight. I don't know why the developers do not choose Woop algorithm as suggested by DXR, even it has been implemented in embree. https://github.com/embree/embree/blob/0fcb306c9176221219dd15e27fe0527ed334948f/kernels/bvh/bvh8_factory.cpp#L497-L507
Could you please explain about this? Thanks very much! @svenwoop
Could you please try out if that experimental BVH8Triangle4vIntersector1Woop works better for you? The ray/triangle intersection of it should be watertight. Just enable the ENABLE_WOOP_TEST in code block above and enable RTC_SCENE_FLAG_ROBUST for your scenes? You need to compile and run on AVX enabled machine for this codepath to be selected, but if that help we could fix that and give you a compile time option to enable this.
Dear Dr Woop @svenwoop ,
I read your paper on watertight intersections, very cool work! And then I found of course there are open source implementations of it ...
I have been testing some scenes that have lots of ray-to-vertex and ray-to-edge direct hits and I am seeing lots of holes in the results returned back from embree. I have tried two wrappers thus far:
- Open3D which uses embree3 and the (now deprecated)
rtcIntersect1M
API ( https://github.com/isl-org/Open3D/blob/179886dfd57797b2b0d379062387c60313a58b2b/cpp/open3d/t/geometry/RaycastingScene.cpp#L463 ) - raybender (a semi-abandoned project) which uses embree3 and the
rtcIntersect1
API ( https://github.com/cvg/raybender/blob/2648060a688cb206b9675d503a23855c9cf432c7/lib/ray.cc#L60 )
Interestingly, both wrappers give me similar holes (i.e. lack of ray returns) but NOT identical outputs for the exact same bit-for-bit rays.
Based upon your comment above, it seems you suggest trying embree version 4 (i.e. HEAD in this repo) and to toggle the BVH config ... do you think this could help resolve the issue I'm seeing? In particular:
- Do you know, briefly, what may have changed between embree v3 and v4 with respect to ray-to-edge and ray-to-vertex intersections? (Could it be nothing changed and the projects I cite above are not using embree 3 correctly?)
- Your most recent comment here is with regards to the BVH but I thought the point of the BVH was to cull and not explicitly test intersections... is the bvh in embree v4 dramatically different than v3 ?
Thanks for any advice you can provide!
Embree 3 and Embree 4 will behave identical regarding consistency along edges. Please try out enabling the RTC_SCENE_FLAG_ROBUST scene mode if that gives you sufficient quality. If that is not enough please try to enable that ENABLE_WOOP_TEST define described above. Thus should increase quality further.
Thank you @svenwoop ! It seems both of the implementations I cited use RTC_SCENE_FLAG_ROBUST
. I tried installing libembree-dev
and somehow that seems to change the issue, though I would be surprised if that package were built using ENABLE_WOOP_TEST
. Is ENABLE_WOOP_TEST
disabled by default because it makes intersection tests slower? How come it's not a standard build arg e.g. the AVX flags or a RTC scene arg?
The ENABLE_WOOP_TEST is just an experimental feature, this is why it cannot get directly enabled in cmake. You have to patch the code to try this out. Embree is designed for rendering where 100% watertightness is not required.