commit 6e99b8586a6dbc78ede4f64a901ac012a28bad0e
parent bd205874d685ee877768ab632590db114e9b38c5
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Mon, 26 Apr 2021 18:50:11 +0200
Attach a ray-tracing filtering function to the geometry
Discard self hit and handle the inacurracy on the ray range
Diffstat:
| M | src/sgs_geometry.c | | | 90 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
1 file changed, 89 insertions(+), 1 deletion(-)
diff --git a/src/sgs_geometry.c b/src/sgs_geometry.c
@@ -25,6 +25,16 @@
#include <star/s3d.h>
#include <rsys/cstr.h>
+#include <rsys/float2.h>
+#include <rsys/float3.h>
+
+struct hit_filter_context {
+ float range[2];
+ struct s3d_hit hit_from;
+};
+#define HIT_FILTER_CONTEXT_NULL__ {{0,FLT_MAX}, S3D_HIT_NULL__}
+static const struct hit_filter_context HIT_FILTER_CONTEXT_NULL =
+ HIT_FILTER_CONTEXT_NULL__;
struct mesh {
const double* vertices;
@@ -37,6 +47,82 @@ static const struct mesh MESH_NULL = {NULL, NULL, 0, 0};
/*******************************************************************************
* Helper functions
******************************************************************************/
+/* Check that `hit' roughly lies on an edge. For triangular primitives, a
+ * simple but approximative way is to test that its position have at least one
+ * barycentric coordinate roughly equal to 0 or 1. */
+static FINLINE int
+hit_on_edge(const struct s3d_hit* hit)
+{
+ const float on_edge_eps = 1.e-4f;
+ float w;
+ ASSERT(hit && !S3D_HIT_NONE(hit));
+ w = 1.f - hit->uv[0] - hit->uv[1];
+ return eq_epsf(hit->uv[0], 0.f, on_edge_eps)
+ || eq_epsf(hit->uv[0], 1.f, on_edge_eps)
+ || eq_epsf(hit->uv[1], 0.f, on_edge_eps)
+ || eq_epsf(hit->uv[1], 1.f, on_edge_eps)
+ || eq_epsf(w, 0.f, on_edge_eps)
+ || eq_epsf(w, 1.f, on_edge_eps);
+}
+
+/* Return 1 if the submitted hit is actually a self hit, i.e. if the ray starts
+ * the primitive from which it starts */
+static INLINE int
+self_hit
+ (const struct s3d_hit* hit,
+ const float ray_org[3],
+ const float ray_dir[3],
+ const float ray_range[2],
+ const struct s3d_hit* hit_from)
+{
+ ASSERT(hit && hit_from);
+ (void)ray_org, (void)ray_dir, (void)ray_range;
+
+ /* The hit primitive is the one from which the the ray starts. Discard these
+ * hits */
+ if(S3D_PRIMITIVE_EQ(&hit->prim, &hit_from->prim))
+ return 1;
+
+ /* If the targeted point is near of the origin, check that it lies on an
+ * edge/vertex shared by the 2 primitives. In such situation, we assume that
+ * it is a self intersection and discard this hit */
+ if(!S3D_HIT_NONE(hit_from)
+ && eq_epsf(hit->distance, 0, 1.e-1f)
+ && hit_on_edge(hit_from)
+ && hit_on_edge(hit)) {
+ return 1;
+ }
+
+ /* No self hit */
+ return 0;
+}
+
+static int
+hit_filter
+ (const struct s3d_hit* hit,
+ const float ray_org[3],
+ const float ray_dir[3],
+ void* ray_data,
+ void* filter_data)
+{
+ const struct hit_filter_context* ctx = ray_data;
+ (void)filter_data;
+
+ if(!ctx) /* Nothing to do */
+ return 0;
+
+ /* Internally, Star-3D relies on Embree that, due to numerically inaccuracy,
+ * can find hits whose distances are not strictly included in the submitted
+ * ray range. Discard these hits. */
+ if(hit->distance <= ctx->range[0] || hit->distance >= ctx->range[1])
+ return 1;
+
+ if(self_hit(hit, ray_org, ray_dir, ctx->range, &ctx->hit_from))
+ return 1; /* Discard this hit */
+
+ return 0; /* No filtering */
+}
+
static void
mesh_get_vertex(const unsigned ivert, float pos[3], void* ctx)
{
@@ -81,6 +167,8 @@ mesh_create_view
res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)mesh->ntriangles,
mesh_get_triangle, (unsigned)mesh->nvertices, &vdata, 1, mesh);
if(res != RES_OK) goto error;
+ res = s3d_mesh_set_hit_filter_function(shape, hit_filter, NULL);
+ if(res != RES_OK) goto error;
res = s3d_scene_create(sgs_get_s3d_device(sgs), &scene);
if(res != RES_OK) goto error;
res = s3d_scene_attach_shape(scene, shape);
@@ -224,7 +312,7 @@ geometry_create
res_T res = RES_OK;
ASSERT(sgs && (unsigned)type < SGS_GEOMETRY_TYPES_COUNT__ && out_geom);
ASSERT(sampling_mask != 0);
-
+
allocator = sgs_get_allocator(sgs);
geom = MEM_CALLOC(allocator, 1, sizeof(struct sgs_geometry));
if(!geom) {