commit eb0b5365e5a9ab5692e350700cf77610cf4fcc9b
parent 9091cf25808a0c164c1ea2555ddf5eef895f985f
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Tue, 12 Jan 2021 15:58:34 +0100
Add tests on raytrace and closest_point accuracy
Diffstat:
2 files changed, 246 insertions(+), 3 deletions(-)
diff --git a/src/test_s3d_closest_point.c b/src/test_s3d_closest_point.c
@@ -34,6 +34,7 @@
#include "test_s3d_cbox.h"
#include "test_s3d_utils.h"
+#include <rsys/float2.h>
#include <rsys/float3.h>
#include <rsys/float33.h>
#include <limits.h>
@@ -853,7 +854,7 @@ test_single_triangle(struct s3d_device* dev)
float closest_pos[3] = {0,0,0};
float low[3], upp[3], mid[3];
union { float f; uint32_t ui32; } ucast;
- size_t i;
+ size_t a, i;
f3(vertices+0, -0.5f, -0.3f, 0.1f);
f3(vertices+3, -0.4f, 0.2f, 0.3f);
@@ -950,6 +951,132 @@ test_single_triangle(struct s3d_device* dev)
CHK(s3d_shape_ref_put(msh) == RES_OK);
CHK(s3d_scene_view_ref_put(view) == RES_OK);
CHK(s3d_scene_ref_put(scn) == RES_OK);
+
+ /* Check accuracy on a configuration whose analytic distance is known */
+ FOR_EACH(a, 0, 16) {
+ const float amplitude = exp2f((float)a);
+ const float eps = 5e-6f * amplitude;
+ FOR_EACH(i, 0, 1000) {
+ float A[3], B[3], C[3], AB[3], AC[3], BC[3], N[3];
+ int j, n;
+
+ /* Randomly generate a triangle ABC */
+ FOR_EACH(n, 0, 3)
+ A[n] = (rand_canonic() - 0.5f) * amplitude;
+ do {
+ FOR_EACH(n, 0, 3) B[n] = (rand_canonic() - 0.5f) * amplitude;
+ } while (f3_eq_eps(A, B, eps));
+ do {
+ FOR_EACH(n, 0, 3) C[n] = (rand_canonic() - 0.5f) * amplitude;
+ } while (f3_eq_eps(A, C, eps) || f3_eq_eps(B, C, eps));
+
+ f3_sub(AB, B, A);
+ f3_sub(AC, C, A);
+ f3_sub(BC, C, B);
+ f3_cross(N, AB, AC);
+ f3_normalize(N, N);
+
+ f3_set(vertices + 0, A);
+ f3_set(vertices + 3, B);
+ f3_set(vertices + 6, C);
+
+ CHK(s3d_scene_create(dev, &scn) == RES_OK);
+ CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, msh) == RES_OK);
+
+ vdata.usage = S3D_POSITION;
+ vdata.type = S3D_FLOAT3;
+ vdata.get = triangle_get_pos;
+ CHK(s3d_mesh_setup_indexed_vertices
+ (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK);
+
+ CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK);
+
+ FOR_EACH(j, 0, 1000) {
+ float proj[3]; /* Projection of pos on the line */
+ float AP[3], BP[3], CP[3], closest[3], tmp[3];
+ float u, v, w, h, x, dist, d;
+
+ /* Randomly generate a pos not on the triangle
+ * with know position wrt the problem: pos = A + u.AB + v.AC + k.N */
+ u = 3 * rand_canonic() - 1;
+ v = 3 * rand_canonic() - 1;
+ w = 1 - u - v;
+ h = (2 * rand_canonic() - 1) * amplitude;
+ f3_add(proj, A, f3_add(proj, f3_mulf(proj, AB, u), f3_mulf(tmp, AC, v)));
+ f3_add(pos, proj, f3_mulf(pos, N, h));
+ f3_sub(AP, proj, A);
+ f3_sub(BP, proj, B);
+ f3_sub(CP, proj, C);
+
+ /* Compute closest point */
+ CHK(s3d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit)
+ == RES_OK);
+ CHK(!S3D_HIT_NONE(&hit));
+ CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr)
+ == RES_OK);
+
+ /* Check result
+ * Due to known uv lack of accuracy we mainly check distance */
+ if(u >= 0 && v >= 0 && w >= 0) {
+ /* proj is inside the triangle and is the closest point */
+ f3_set(closest, proj);
+ dist = fabsf(h);
+ } else {
+ /* proj is outside the triangle */
+ float lab2 = f3_dot(AB, AB);
+ float lac2 = f3_dot(AC, AC);
+ float lbc2 = f3_dot(BC, BC);
+ if(w >= 0 && u < 0) {
+ /* proj is closest to either AB or AC */
+ x = f3_dot(AP, AB);
+ if(v < 0 && x > 0) {
+ /* proj is closest to AB */
+ f3_add(closest, A, f3_mulf(tmp, AB, MMIN(1, x / lab2)));
+ } else {
+ /* proj is closest to AC */
+ f3_add(closest, A,
+ f3_mulf(tmp, AC, MMIN(1, MMAX(0, f3_dot(AP, AC) / lac2))));
+ }
+ }
+ else if(u >= 0 && v < 0) {
+ /* proj is closest to either BC or BA */
+ x = f3_dot(BP, BC);
+ if(w < 0 && x > 0) {
+ /* proj is closest to BC */
+ f3_add(closest, B, f3_mulf(tmp, BC, MMIN(1, x / lbc2)));
+ } else {
+ /* proj is closest to BA */
+ f3_add(closest, B,
+ f3_mulf(tmp, AB, -MMIN(1, MMAX(0, -f3_dot(BP, AB) / lab2))));
+ }
+ }
+ else if(v >= 0 && w < 0) {
+ /* proj is closest to either CA or CB */
+ x = -f3_dot(CP, AC);
+ if(u < 0 && x > 0) {
+ /* proj is closest to CA */
+ f3_add(closest, C, f3_mulf(tmp, AC, -MMIN(1, x / lac2)));
+ } else {
+ /* proj is closest to CB */
+ f3_add(closest, C,
+ f3_mulf(tmp, BC, -MMIN(1, MMAX(0, -f3_dot(CP, BC) / lbc2))));
+ }
+ }
+ else { ASSERT(0); }
+ dist = f3_len(f3_sub(tmp, pos, closest));
+ }
+ CHK(eq_epsf(hit.distance, dist, eps));
+ /* Intersection-point's position is less accurate than hit distance */
+ d = f3_len(f3_sub(tmp, closest, attr.value));
+ CHK(d <= 10 * eps);
+ }
+
+ CHK(s3d_shape_ref_put(msh) == RES_OK);
+ CHK(s3d_scene_view_ref_put(view) == RES_OK);
+ CHK(s3d_scene_ref_put(scn) == RES_OK);
+ }
+ }
}
static void
diff --git a/src/test_s3d_trace_ray.c b/src/test_s3d_trace_ray.c
@@ -60,6 +60,32 @@ filter_func
return hit->prim.prim_id % 2 == 0;
}
+static void
+triangle_get_ids(const unsigned itri, unsigned ids[3], void* ctx)
+{
+ (void)ctx;
+ CHK(itri == 0);
+ CHK(ids);
+ ids[0] = 0;
+ ids[1] = 1;
+ ids[2] = 2;
+}
+
+static void
+triangle_get_pos(const unsigned ivert, float pos[3], void* ctx)
+{
+ float* vertices = ctx;
+ CHK(ctx);
+ CHK(ivert < 3);
+ CHK(pos);
+ switch (ivert) { /* Setup a random triangle */
+ case 0: f3_set(pos, vertices + 0); break;
+ case 1: f3_set(pos, vertices + 3); break;
+ case 2: f3_set(pos, vertices + 6); break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+}
+
int
main(int argc, char** argv)
{
@@ -93,7 +119,7 @@ main(int argc, char** argv)
unsigned walls_id;
unsigned tall_block_id;
unsigned short_block_id;
- size_t i;
+ size_t a, i;
char filter = 0;
mem_init_proxy_allocator(&allocator, &mem_default_allocator);
@@ -354,7 +380,6 @@ main(int argc, char** argv)
CHK(image_write_ppm_stream(&img, 0, stdout) == RES_OK);
image_release(&img);
- CHK(s3d_device_ref_put(dev) == RES_OK);
CHK(s3d_shape_ref_put(inst) == RES_OK);
CHK(s3d_shape_ref_put(short_block) == RES_OK);
CHK(s3d_shape_ref_put(tall_block) == RES_OK);
@@ -362,6 +387,97 @@ main(int argc, char** argv)
CHK(s3d_scene_ref_put(scn) == RES_OK);
CHK(s3d_scene_ref_put(scn2) == RES_OK);
+ /* Check accuracy on a configuration whose analytic distance is known */
+ FOR_EACH(a, 0, 16) {
+ const float amplitude = exp2f((float)a);
+ const float eps = 5e-6f * amplitude;
+ float vertices[9];
+ struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL;
+ struct s3d_scene_view* view = NULL;
+ struct s3d_shape* msh = NULL;
+ FOR_EACH(i, 0, 1000) {
+ float A[3], B[3], C[3], AB[3], AC[3], N[3];
+ int j, n;
+
+ /* Randomly generate a triangle ABC */
+ FOR_EACH(n, 0, 3)
+ A[n] = (rand_canonic() - 0.5f) * amplitude;
+ do {
+ FOR_EACH(n, 0, 3) B[n] = (rand_canonic() - 0.5f) * amplitude;
+ } while (f3_eq_eps(A, B, eps));
+ do {
+ FOR_EACH(n, 0, 3) C[n] = (rand_canonic() - 0.5f) * amplitude;
+ } while (f3_eq_eps(A, C, eps) || f3_eq_eps(B, C, eps));
+
+ f3_sub(AB, B, A);
+ f3_sub(AC, C, A);
+ f3_cross(N, AB, AC);
+ f3_normalize(N, N);
+
+ f3_set(vertices + 0, A);
+ f3_set(vertices + 3, B);
+ f3_set(vertices + 6, C);
+
+ CHK(s3d_scene_create(dev, &scn) == RES_OK);
+ CHK(s3d_shape_create_mesh(dev, &msh) == RES_OK);
+ CHK(s3d_scene_attach_shape(scn, msh) == RES_OK);
+
+ vdata.usage = S3D_POSITION;
+ vdata.type = S3D_FLOAT3;
+ vdata.get = triangle_get_pos;
+ CHK(s3d_mesh_setup_indexed_vertices
+ (msh, 1, triangle_get_ids, 3, &vdata, 1, vertices) == RES_OK);
+
+ CHK(s3d_scene_view_create(scn, S3D_TRACE, &view) == RES_OK);
+
+ FOR_EACH(j, 0, 1000) {
+ float proj[3]; /* Projection of pos on the line */
+ float tmp[3];
+ float u, v, w, h;
+
+ /* Randomly generate a pos not on the triangle
+ * with know position wrt the problem: pos = A + u.AB + v.AC + k.N */
+ u = 3 * rand_canonic() - 1;
+ v = 3 * rand_canonic() - 1;
+ w = 1 - u - v;
+ h = (2 * rand_canonic() - 1) * amplitude;
+ f3_add(proj, A, f3_add(proj, f3_mulf(proj, AB, u), f3_mulf(tmp, AC, v)));
+ f3_add(pos, proj, f3_mulf(pos, N, h));
+
+ /* Raytrace from pos towards proj */
+ f3_mulf(dir, N, (h > 0 ? -1.f : 1.f));
+ f3_normalize(dir, dir);
+ CHK(s3d_scene_view_trace_ray(view, pos, dir, range, NULL, &hit)
+ == RES_OK);
+
+ /* Check result */
+ if(u < 0 || v < 0 || w < 0) {
+ if(!S3D_HIT_NONE(&hit))
+ CHK(u >= -FLT_EPSILON && v >= -FLT_EPSILON && w >= -FLT_EPSILON);
+ } else {
+ if(S3D_HIT_NONE(&hit))
+ CHK(u <= FLT_EPSILON || v <= FLT_EPSILON || w <= FLT_EPSILON);
+ }
+ if(!S3D_HIT_NONE(&hit)) {
+ struct s3d_attrib attr;
+ float d;
+ CHK(eq_epsf(hit.distance, fabsf(h), eps));
+ CHK(s3d_primitive_get_attrib(&hit.prim, S3D_POSITION, hit.uv, &attr)
+ == RES_OK);
+ /* Intersection-point's position is less accurate than hit distance */
+ d = f3_len(f3_sub(tmp, attr.value, proj));
+ CHK(d <= 10 * eps);
+ }
+ }
+
+ CHK(s3d_shape_ref_put(msh) == RES_OK);
+ CHK(s3d_scene_view_ref_put(view) == RES_OK);
+ CHK(s3d_scene_ref_put(scn) == RES_OK);
+ }
+ }
+
+ CHK(s3d_device_ref_put(dev) == RES_OK);
+
check_memory_allocator(&allocator);
mem_shutdown_proxy_allocator(&allocator);
CHK(mem_allocated_size() == 0);