Embree 4自定义球体几何体无交点问题排查求助
Embree 4自定义球体几何体射线检测失败问题排查
在Embree 4中实现自定义球体几何体时,射线始终无法检测到交点,Embree未更新射线的tfar参数。以下是实现代码、测试场景及问题排查:
核心实现代码
void sphereBoundsFunc(const struct RTCBoundsFunctionArguments* args) { const Sphere* spheres = (const Sphere*)args->geometryUserPtr; RTCBounds* bounds = args->bounds_o; const Sphere& sphere = spheres[args->primID]; bounds->lower_x = sphere.p.x - sphere.r; bounds->lower_y = sphere.p.y - sphere.r; bounds->lower_z = sphere.p.z - sphere.r; bounds->upper_x = sphere.p.x + sphere.r; bounds->upper_y = sphere.p.y + sphere.r; bounds->upper_z = sphere.p.z + sphere.r; } RTC_SYCL_INDIRECTLY_CALLABLE void sphereIntersectFunc(const RTCIntersectFunctionNArguments* args) { int* valid = args->valid; void* ptr = args->geometryUserPtr; RTCRayHit *rayhit = (RTCRayHit*)args->rayhit; unsigned int primID = args->primID; assert(args->N == 1); const Sphere* spheres = (const Sphere*)ptr; const Sphere& sphere = spheres[primID]; if (!valid[0]) return; valid[0] = 0; // 错误:逗号表达式仅取最后一个值,无法正确构造Point const Point dir = (rayhit->ray.dir_x, rayhit->ray.dir_y, rayhit->ray.dir_z); const Point org = (rayhit->ray.org_x, rayhit->ray.org_y, rayhit->ray.org_z); const Point v = org - sphere.p; const float A = dir.dot(dir); const float B = 2.0f * v.dot(dir); const float C = v.dot(v) - sphere.r * sphere.r; const float D = B * B - 4.0f * A * C; if (D < 0.0f) return; const float Q = sqrt(D); const float t0 = 0.5f * (-B - Q) / A; const float t1 = 0.5f * (-B + Q) / A; RTCHit potentialHit; potentialHit.u = 0.0f; potentialHit.v = 0.0f; copyInstanceIdStack(args->context, potentialHit.instID); // 错误:sphere.geomID未正确赋值 potentialHit.geomID = sphere.geomID; potentialHit.primID = primID; if ((rayhit->ray.tnear < t0) && (t0 < rayhit->ray.tfar)) { int imask; bool mask = 1; imask = mask ? -1 : 0; const Point Ng = org + dir * t0 - sphere.p; potentialHit.Ng_x = Ng.x; potentialHit.Ng_y = Ng.y; potentialHit.Ng_z = Ng.z; RTCFilterFunctionNArguments fargs; fargs.valid = (int*)&imask; fargs.geometryUserPtr = ptr; fargs.context = args->context; fargs.ray = (RTCRayN*)args->rayhit; fargs.hit = (RTCHitN*)&potentialHit; fargs.N = 1; const float old_t = rayhit->ray.tfar; rayhit->ray.tfar = t0; #if !USE_ARGUMENT_CALLBACKS rtcInvokeIntersectFilterFromGeometry(args, &fargs); #endif if (imask == -1) { rayhit->hit = potentialHit; valid[0] = -1; } else rayhit->ray.tfar = old_t; } if ((rayhit->ray.tnear < t1) && (t1 < rayhit->ray.tfar)) { int imask; bool mask = 1; imask = mask ? -1 : 0; const Point Ng = org + dir * t1 - sphere.p; potentialHit.Ng_x = Ng.x; potentialHit.Ng_y = Ng.y; potentialHit.Ng_z = Ng.z; RTCFilterFunctionNArguments fargs; fargs.valid = (int*)&imask; fargs.geometryUserPtr = ptr; fargs.context = args->context; fargs.ray = (RTCRayN*)args->rayhit; fargs.hit = (RTCHitN*)&potentialHit; fargs.N = 1; const float old_t = rayhit->ray.tfar; rayhit->ray.tfar = t1; #if !USE_ARGUMENT_CALLBACKS rtcInvokeIntersectFilterFromGeometry(args, &fargs); #endif if (imask == -1) { rayhit->hit = potentialHit; valid[0] = -1; } else rayhit->ray.tfar = old_t; } } RTC_SYCL_INDIRECTLY_CALLABLE void sphereOccludedFunc(const RTCOccludedFunctionNArguments* args) { int* valid = args->valid; void* ptr = args->geometryUserPtr; RTCRayHit* rayhit = (RTCRayHit*)args->ray; unsigned int primID = args->primID; assert(args->N == 1); const Sphere* spheres = (const Sphere*)ptr; const Sphere& sphere = spheres[primID]; if (!valid[0]) return; valid[0] = 0; // 错误:逗号表达式仅取最后一个值,无法正确构造Point const Point dir = (rayhit->ray.dir_x, rayhit->ray.dir_y, rayhit->ray.dir_z); const Point org = (rayhit->ray.org_x, rayhit->ray.org_y, rayhit->ray.org_z); const Point v = org - sphere.p; const float A = dir.dot(dir); const float B = 2.0f * v.dot(dir); const float C = v.dot(v) - sphere.r * sphere.r; const float D = B * B - 4.0f * A * C; if (D < 0.0f) return; const float Q = sqrt(D); const float t0 = 0.5f * (-B - Q) / A; const float t1 = 0.5f * (-B + Q) / A; RTCHit potentialHit; potentialHit.u = 0.0f; potentialHit.v = 0.0f; copyInstanceIdStack(args->context, potentialHit.instID); potentialHit.geomID = sphere.geomID; potentialHit.primID = primID; if ((rayhit->ray.tnear < t0) && (t0 < rayhit->ray.tfar)) { int imask; bool mask = 1; imask = mask ? -1 : 0; const Point Ng = org + dir * t0 - sphere.p; potentialHit.Ng_x = Ng.x; potentialHit.Ng_y = Ng.y; potentialHit.Ng_z = Ng.z; RTCFilterFunctionNArguments fargs; fargs.valid = (int*)&imask; fargs.geometryUserPtr = ptr; fargs.context = args->context; fargs.ray = (RTCRayN*)args->ray; fargs.hit = (RTCHitN*)&potentialHit; fargs.N = 1; const float old_t = rayhit->ray.tfar; rayhit->ray.tfar = t0; #if !USE_ARGUMENT_CALLBACKS rtcInvokeOccludedFilterFromGeometry(args, &fargs); #endif if (imask == -1) { rayhit->hit = potentialHit; valid[0] = -1; } else rayhit->ray.tfar = old_t; } if ((rayhit->ray.tnear < t1) && (t1 < rayhit->ray.tfar)) { int imask; bool mask = 1; imask = mask ? -1 : 0; const Point Ng = org + dir * t1 - sphere.p; potentialHit.Ng_x = Ng.x; potentialHit.Ng_y = Ng.y; potentialHit.Ng_z = Ng.z; RTCFilterFunctionNArguments fargs; fargs.valid = (int*)&imask; fargs.geometryUserPtr = ptr; fargs.context = args->context; fargs.ray = (RTCRayN*)args->ray; fargs.hit = (RTCHitN*)&potentialHit; fargs.N = 1; const float old_t = rayhit->ray.tfar; rayhit->ray.tfar = t1; #if !USE_ARGUMENT_CALLBACKS rtcInvokeOccludedFilterFromGeometry(args, &fargs); #endif if (imask == -1) { rayhit->hit = potentialHit; valid[0] = -1; } else rayhit->ray.tfar = old_t; } } RTC_SYCL_INDIRECTLY_CALLABLE void sphereFilterFunction(const RTCFilterFunctionNArguments* args) { int* valid = args->valid; const RayQueryContext* context = (const RayQueryContext*)args->context; RTCRay* ray = (RTCRay*)args->ray; RTCHit* hit = (RTCHit*)args->hit; const unsigned int N = args->N; assert(N == 1); if (context == nullptr) return; if (valid[0] != -1) return; // 错误:此过滤逻辑会随机丢弃部分交点,测试阶段可注释 const Point h = (ray->org_x + ray->dir_x * ray->tfar, ray->org_y + ray->dir_y * ray->tfar, ray->org_z + ray->dir_z * ray->tfar); float v = abs(sin(10.0f * h.x) * cos(10.0f * h.y) * sin(10.0f * h.z)); float T = clamp((v - 0.1f) * 3.0f, 0.0f, 1.0f); if (T < 0.5f) valid[0] = 0; } RTCGeometry UDG::analyticalSphere(RTCDevice device, const Point o, float r) { RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_USER); // 错误:局部变量sphere在函数返回后销毁,导致用户数据指针变为野指针 Sphere sphere = Sphere(); sphere.type = USER_GEOMETRY_SPHERE; sphere.p = o; sphere.r = r; sphere.geometry = geom; rtcSetGeometryUserPrimitiveCount(geom, 1); rtcSetGeometryUserData(geom, &sphere); rtcSetGeometryBoundsFunction(geom, sphereBoundsFunc, nullptr); #if !USE_ARGUMENT_CALLBACKS rtcSetGeometryIntersectFunction(geom, sphereIntersectFunc); rtcSetGeometryOccludedFunction(geom, sphereOccludedFunc); #endif rtcCommitGeometry(geom); return geom; }
测试场景代码
Point center = (0.f, 0.f, 0.f); RTCGeometry geom = UDG::analyticalSphere(api.device, center, 2); string scene = "UDG"; api.newScene(scene); unsigned int geomID = api.attachGeometry(geom, scene); Point o; o.x = 0.0f; o.y = 0.0f; o.z = -10.0f; Point d; d.x = 0; d.y = 0; d.z = 1; RTCRayHit ray = api.newRay(o, d); api.getIntersection(ray, scene);
问题排查与修复方案
1. 野指针问题(核心原因)
在analyticalSphere函数中,sphere是局部栈变量,函数返回后该变量会被销毁,rtcSetGeometryUserData(geom, &sphere)设置的指针变为野指针,后续Bounds和Intersect函数访问时会读取无效内存。
修复:
使用动态分配的Sphere对象,确保生命周期与几何体一致:
RTCGeometry UDG::analyticalSphere(RTCDevice device, const Point o, float r) { RTCGeometry geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_USER); Sphere* sphere = new Sphere(); sphere->type = USER_GEOMETRY_SPHERE; sphere->p = o; sphere->r = r; sphere->geometry = geom; rtcSetGeometryUserPrimitiveCount(geom, 1); rtcSetGeometryUserData(geom, sphere); rtcSetGeometryBoundsFunction(geom, sphereBoundsFunc, nullptr); #if !USE_ARGUMENT_CALLBACKS rtcSetGeometryIntersectFunction(geom, sphereIntersectFunc); rtcSetGeometryOccludedFunction(geom, sphereOccludedFunc); #endif rtcCommitGeometry(geom); return geom; } // 注意:后续销毁几何体时需手动释放sphere内存
2. Point构造错误
代码中使用逗号表达式构造Point,例如const Point dir = (rayhit->ray.dir_x, rayhit->ray.dir_y, rayhit->ray.dir_z);,这只会取最后一个值(z分量),导致方向向量错误,射线与球体的交点计算完全失效。
修复:
使用正确的构造方式:
const Point dir(rayhit->ray.dir_x, rayhit->ray.dir_y, rayhit->ray.dir_z); const Point org(rayhit->ray.org_x, rayhit->ray.org_y, rayhit->ray.org_z);
3. GeomID未赋值
sphere.geomID未被正确设置,导致命中记录中的几何体ID无效,可能影响后续处理。
修复:
在几何体附加到场景后,将geomID赋值给sphere:
// 在测试场景中修改 unsigned int geomID = api.attachGeometry(geom, scene); Sphere* sphere = (Sphere*)rtcGetGeometryUserData(geom); sphere->geomID = geomID; // 重新提交几何体 rtcCommitGeometry(geom);
4. 过滤函数干扰测试
sphereFilterFunction中的逻辑会根据交点位置随机丢弃部分命中,测试阶段可以暂时注释该逻辑,确保射线能稳定检测到交点:
RTC_SYCL_INDIRECTLY_CALLABLE void sphereFilterFunction(const RTCFilterFunctionNArguments* args) { int* valid = args->valid; const RayQueryContext* context = (const RayQueryContext*)args->context; RTCRay* ray = (RTCRay*)args->ray; RTCHit* hit = (RTCHit*)args->hit; const unsigned int N = args->N; assert(N == 1); if (context == nullptr) return; if (valid[0] != -1) return; // 暂时注释过滤逻辑,确保命中有效 // const Point h = (ray->org_x + ray->dir_x * ray->tfar, // ray->org_y + ray->dir_y * ray->tfar, // ray->org_z + ray->dir_z * ray->tfar); // float v = abs(sin(10.0f * h.x) * cos(10.0f * h.y) * sin(10.0f * h.z)); // float T = clamp((v - 0.1f) * 3.0f, 0.0f, 1.0f); // if (T < 0.5f) valid[0] = 0; }
内容的提问来源于stack exchange,提问作者Arolan
相关产品推荐
相关产品推荐

