star-vx

Structuring voxels for ray-tracing
git clone git://git.meso-star.fr/star-vx.git
Log | Files | Refs | README | LICENSE

commit 912c1e6946146693e3ecbf622a8e63573903db0c
parent e3b3bd37057cdac1e232ad86ce94d2b7e8c6a3ad
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri,  9 Mar 2018 10:54:05 +0100

Add the support of `challenge' and `filter' funcs to the RT

Diffstat:
Msrc/svx.h | 1+
Msrc/svx_octree.c | 7++++++-
Msrc/svx_octree.h | 1+
Msrc/svx_octree_trace_ray.c | 233++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
4 files changed, 163 insertions(+), 79 deletions(-)

diff --git a/src/svx.h b/src/svx.h @@ -129,6 +129,7 @@ typedef int (const struct svx_hit* hit, const double ray_org[3], const double ray_dir[3], + const double ray_range[2], void* context); /* User data submitted on trace ray invocation */ /* Hit filter function data type. One can define such function to discard diff --git a/src/svx_octree.c b/src/svx_octree.c @@ -572,10 +572,15 @@ svx_octree_create vox_sz[2] = (upper[2] - lower[2]) / (double)nvoxels[2]; /* Compute the octree AABB in world space */ - d3_set(oct->oclow, lower); + oct->oclow[0] = lower[0]; + oct->oclow[1] = lower[1]; + oct->oclow[2] = lower[2]; oct->ocupp[0] = (double)oct->definition * vox_sz[0]; oct->ocupp[1] = (double)oct->definition * vox_sz[1]; oct->ocupp[2] = (double)oct->definition * vox_sz[2]; + oct->ocsize[0] = oct->ocupp[0] - oct->oclow[0]; + oct->ocsize[1] = oct->ocupp[1] - oct->oclow[1]; + oct->ocsize[2] = oct->ocupp[2] - oct->oclow[2]; /* Adjust the octree definition with respect to the octree depth since finest * levels might be removed by the build process due to the merging process */ diff --git a/src/svx_octree.h b/src/svx_octree.h @@ -24,6 +24,7 @@ struct svx_octree { double lower[3], upper[3]; /* Submitted World space AABB of the octree */ double oclow[3], ocupp[3]; /* Adjusted World space AABB of the octree */ + double ocsize[3]; /* World space size of the octree */ struct octree_buffer buffer; struct octree_index root; /* Index toward the children of the root */ diff --git a/src/svx_octree_trace_ray.c b/src/svx_octree_trace_ray.c @@ -17,10 +17,22 @@ #include "svx_scene.h" struct ray { + /* Ray parameters in the normalized octree space [1, 2]^3 whose ray direction + * is assumed to be always negative. octant_mask defines which dimension was + * reverted to ensure the negative ray direction: the bit 1, 2 and 3 are set if + * the Z, Y and X dimension was reverted, respectively. */ double org[3]; double range[2]; double ts[3]; /* 1 / -abs(dir) */ uint32_t octant_mask; + + /* Ray origin and direction in world space. Note that the ray range is + * independent of the octree space. Let a ray `r' in world space defined as + * `r = O + tD'; one can transform the ray in another space by the Matrix M + * as `r' = M.O + t*(M.D)' without any impact on the t parameter and thus on + * its range. Only the norm of ray direction might be updated. */ + double orgws[3]; + double dirws[3]; }; /******************************************************************************* @@ -42,52 +54,96 @@ uitof(const uint32_t ui) return ucast.f; } +static FINLINE void +setup_hit + (const struct svx_octree* oct, + const struct octree_index iattr, /* Index toward the voxel attributes */ + const double distance, /* Hit distance */ + const double corner[3], /* Corner of the current voxel in [1, 2]^3 space */ + const double scale_exp2, /* Size of the voxel in [1, 2]^3] space */ + const size_t depth, /* Depth of the voxel in the octree hierarchy */ + const int is_leaf, /* Define if the voxel is a leaf or not */ + const uint32_t octant_mask, /* bitmask of reverted dimensions */ + struct svx_hit* hit) +{ + double low[3], upp[3]; + ASSERT(oct && corner && voxsz && hit); + ASSERT(distance > 0); + + hit->distance = distance; + hit->voxel.data = octree_buffer_get_attr(&oct->buffer, &iattr); + hit->voxel.depth = depth, + hit->voxel.id = absolute_attr_index(&oct->buffer, iattr); + hit->voxel.is_leaf = is_leaf; + + /* Transform the voxel aabb in the [0, 1]^3 normalized space and flip it wrt + * to the octant mask of the ray */ + low[0] = corner[0] - 1; + low[1] = corner[1] - 1; + low[2] = corner[2] - 1; + if(octant_mask & 4) low[0] = 1 - low[0] - scale_exp2; + if(octant_mask & 2) low[1] = 1 - low[1] - scale_exp2; + if(octant_mask & 1) low[2] = 1 - low[2] - scale_exp2; + upp[0] = low[0] + scale_exp2; + upp[1] = low[1] + scale_exp2; + upp[2] = low[2] + scale_exp2; + + /* Transform the voxel AABB in world space */ + hit->voxel.lower[0] = low[0] * oct->ocsize[0] + oct->oclow[0]; + hit->voxel.lower[1] = low[1] * oct->ocsize[1] + oct->oclow[1]; + hit->voxel.lower[2] = low[2] * oct->ocsize[2] + oct->oclow[2]; + hit->voxel.upper[0] = upp[0] * oct->ocsize[0] + oct->oclow[0]; + hit->voxel.upper[1] = upp[1] * oct->ocsize[1] + oct->oclow[1]; + hit->voxel.upper[2] = upp[2] * oct->ocsize[2] + oct->oclow[2]; +} + /* Return RES_BAD_OP if the ray cannot intersect the scene */ static res_T setup_ray - (const struct scene* scn, - const double org[3], + (const struct svx_octree* oct, + const double org[3] const double dir[3], const double range[2] struct ray* ray) { double rcp_ocsz[3]; /* Reciprocal size of the World space octree AABB */ - ASSERT(scn); + double dir_adjusted[3]; + ASSERT(oct); if(range[0] >= range[1]) return RES_BAD_OP; /* Disabled ray */ /* Ray paralelle to an axis and that does not intersect the scene AABB */ - if((!dir[0] && (org[0] < scn->lower[0] || org[0] > scn->upper[0])) - || (!dir[1] && (org[1] < scn->lower[1] || org[1] > scn->upper[1])) - || (!dir[2] && (org[2] < scn->lower[2] || org[2] > scn->upper[2]))) { + if((!dir[0] && (org[0] < oct->lower[0] || org[0] > oct->upper[0])) + || (!dir[1] && (org[1] < oct->lower[1] || org[1] > oct->upper[1])) + || (!dir[2] && (org[2] < oct->lower[2] || org[2] > oct->upper[2]))) { return RES_BAD_OP; } /* Compute reciprocal size of the world space octree AABB */ - rcp_ocsz[0] = 1.0 / (scn->ocupp[0] - scn->oclow[0]); - rcp_ocsz[1] = 1.0 / (scn->ocupp[1] - scn->oclow[1]); - rcp_ocsz[2] = 1.0 / (scn->ocupp[2] - scn->oclow[2]); + rcp_ocsz[0] = 1.0 / oct->ocsize[0]; + rcp_ocsz[1] = 1.0 / oct->ocsize[1]; + rcp_ocsz[2] = 1.0 / oct->ocsize[2]; /* Transform the ray origin in the [1, 2] space */ - ray->org[0] = (org[0] - scn->oclow[0]) * rcp_ocsz[0] + 1.0; - ray->org[1] = (org[1] - scn->oclow[1]) * rcp_ocsz[1] + 1.0; - ray->org[2] = (org[2] - scn->oclow[2]) * rcp_ocsz[2] + 1.0; + ray->org[0] = (org[0] - oct->oclow[0]) * rcp_ocsz[0] + 1.0; + ray->org[1] = (org[1] - oct->oclow[1]) * rcp_ocsz[1] + 1.0; + ray->org[2] = (org[2] - oct->oclow[2]) * rcp_ocsz[2] + 1.0; - /* Transform the range in the normalized octree space */ - ray->range[0] = range[0] * rcp_ocsz[0]; - ray->range[1] = range[1] * rcp_ocsz[1]; + /* Setup the ray range */ + ray->range[0] = range[0]; + ray->range[1] = range[1]; /* Transform the direction in the normalized octree space */ - ray->dir[0] = dir[0] * rcp_ocsz[0]; - ray->dir[1] = dir[1] * rcp_ocsz[1]; - ray->dir[2] = dir[2] * rcp_ocsz[2]; + dir_adjusted[0] = dir[0] * rcp_ocsz[0]; + dir_adjusted[1] = dir[1] * rcp_ocsz[1]; + dir_adjusted[2] = dir[2] * rcp_ocsz[2]; /* Let a ray defines as org + t*dir and X the coordinate of an axis aligned * plane. The ray intersects X at t = (X - org)/dir = (X - org) * ts; with ts * = 1/dir. Note that one assume that dir is always negative. */ - ray->ts[0] = 1.0 / -abs(dir[0]); - ray->ts[1] = 1.0 / -abs(dir[1]); - ray->ts[2] = 1.0 / -abs(dir[2]); + ray->ts[0] = 1.0 / -abs(dir_adjusted[0]); + ray->ts[1] = 1.0 / -abs(dir_adjusted[1]); + ray->ts[2] = 1.0 / -abs(dir_adjusted[2]); /* Mirror rays with position directions */ ray->octant_mask = 0; @@ -95,11 +151,26 @@ setup_ray if(ray_dir[1] > 0) { ray->octant_mask ^= 2; ray->org[0] = 3.0 - ray->org[1]; } if(ray_dir[2] > 0) { ray->octant_mask ^= 1; ray->org[0] = 3.0 - ray->org[2]; } + /* Save the world space ray origin */ + ray->orgws[0] = org[0]; + ray->orgws[1] = org[1]; + ray->orgws[2] = org[2]; + + /* Save the world space ray direction */ + ray->dirws[0] = dir[0]; + ray->dirws[1] = dir[1]; + ray->dirws[2] = dir[2]; + return RES_OK; } static res_T -trace_ray(const struct svx_octree* oct, const struct* ray, struct svx_hit* hit) +trace_ray + (const struct svx_octree* oct, + const svx_challenge_voxel_T challenge, + const svx_hit_filter_function_T filter, + const struct ray* ray, + struct svx_hit* hit) { #define SCALE_MAX 23 /* #mantisse bits */ struct stack_entry { @@ -168,39 +239,62 @@ trace_ray(const struct svx_octree* oct, const struct* ray, struct svx_hit* hit) /* Traverse the current child */ if((node->is_valid & ichild_flag) && t_min <= (t_max_child = MMIN(t_max, t_max_corner))) { - double t_max_parent; - double t_center[3]; - float scale_exp2_child; - - t_max_parent = t_max; - t_max = t_max_child; - - if(node->is_leaf & ichild_flag) break; /* Leaf node */ - /* TODO invoke filter function if necessary */ - - scale_exp2_child = scale_exp2 * 0.5f; - - /* center = corner - scale_exp2_child => - * t_center = ts*(corner + scale_exp2_child - org) - * t_center = t_corner + ts*scale_exp2_child - * Anyway we perforrm the whole computation to avoid numerical issues */ - t_center[0] = (corner[0] - ray->org[0] + scale_exp2_child) * ray->ts[0]; - t_center[1] = (corner[1] - ray->org[1] + scale_exp2_child) * ray->ts[1]; - t_center[2] = (corner[2] - ray->org[2] + scale_exp2_child) * ray->ts[2]; - - /* Push the parent node */ - stack[scale].t_max = t_max_parent; - stack[scale].inode = inode; - - /* Define the id and the lower left corner of the first child */ - ichild = 0; - if(t_center[0] > t_min) { ichild ^= 4; corner[0] += scale_exp2_child; } - if(t_center[1] > t_min) { ichild ^= 2; corner[1] += scale_exp2_child; } - if(t_center[2] > t_min) { ichild ^= 1; corner[2] += scale_exp2_child; } - - --scale; - scale_exp2 = scale_exp2_child; - continue; + const int is_leaf = is_leaf = (node->is_leaf & ichild_flag) != 0; + int go_deeper = 1; + + /* If the current voxel is a leaf or if a challenge function is set, + * check the current hit */ + if(is_leaf || challenge) { + const double dst = t_min < ray->range[0] ? t_max_child : t_min; + const size_t depth = SCALE_MAX - scale; + const struct octree_index iattr = octree_buffer_get_child_attr_index + (&oct->buffer, &inode, ichild_adjusted); + + setup_hit(oct, iattr, dst, corner, scale_exp2, depth, is_leaf, + ray->octant_mask, hit); + + if(is_leaf || challenge(hit, context)) { + go_deeper = 0; + /* Stop the traversal if no filter is defined or if the filter + * function returns 0 */ + if(!filter /* By default, i.e. with no filter, stop the traversal */ + || !filter(hit, ray->orgws, ray->dirws, ray->range, context)) + break; + } + } + + if(go_deeper) { + double t_max_parent; + double t_center[3]; + float scale_exp2_child; + + t_max_parent = t_max; + t_max = t_max_child; + + scale_exp2_child = scale_exp2 * 0.5f; + + /* center = corner - scale_exp2_child => + * t_center = ts*(corner + scale_exp2_child - org) + * t_center = t_corner + ts*scale_exp2_child + * Anyway we perforrm the whole computation to avoid numerical issues */ + t_center[0] = (corner[0] - ray->org[0] + scale_exp2_child) * ray->ts[0]; + t_center[1] = (corner[1] - ray->org[1] + scale_exp2_child) * ray->ts[1]; + t_center[2] = (corner[2] - ray->org[2] + scale_exp2_child) * ray->ts[2]; + + /* Push the parent node */ + stack[scale].t_max = t_max_parent; + stack[scale].inode = inode; + + /* Define the id and the lower left corner of the first child */ + ichild = 0; + if(t_center[0] > t_min) { ichild ^= 4; corner[0] += scale_exp2_child; } + if(t_center[1] > t_min) { ichild ^= 2; corner[1] += scale_exp2_child; } + if(t_center[2] > t_min) { ichild ^= 1; corner[2] += scale_exp2_child; } + + --scale; + scale_exp2 = scale_exp2_child; + continue; + } } /* Define the id and the lower left corner of the next child */ @@ -270,27 +364,6 @@ trace_ray(const struct svx_octree* oct, const struct* ray, struct svx_hit* hit) } } - if(scale < scale_max) { - const uint8_t ichild_adjusted = (uint8_t)(ichild ^ ray->octant_mask); - const struct octree_xnode* node = octree_buffer_get_node(&oct->buffer, inode); - const struct octree_index iattr; - - /* Retrieve the node attribs */ - iattr = octree_buffer_get_child_attr_index(&oct->buffer, &inode, ichild_adjusted); - - /* Setu the hit */ - hit->distance = t_min < ray->range[0] ? t_max : t_min; - hit->voxel.data = octree_buffer_get_attr(&oct->buffer, &iattr); - hit->voxel.depth = SCALE_MAX - scale; - hit->voxel.id = absolute_attr_index(&oct->buffer, iattr); - hit->voxel.is_leaf = (node->is_leaf & ichild_adjusted) != 0; - hit->voxel.lower[0] = corner[0]; - hit->voxel.lower[1] = corner[1]; - hit->voxel.lower[2] = corner[2]; - hit->voxel.upper[0] = corner[0] + scale_exp2; - hit->voxel.upper[1] = corner[1] + scale_exp2; - hit->voxel.upper[2] = corner[2] + scale_exp2; - } return RES_OK; } @@ -303,6 +376,7 @@ svx_octree_trace_ray const double org[3], const double dir[3], const double range[2], + svx_challenge_voxel_T challenge, svx_hit_filter_function_T filter, void* context, struct svx_hit* hit) @@ -310,19 +384,22 @@ svx_octree_trace_ray struct ray ray; res_T res = RES_OK; - if(!scn || !org || !range || !hit) { + if(!oct || !org || !range || !hit) { res = RES_BAD_ARG; goto error; } *hit = SVX_HIT_NULL; - res = setup_ray(scn, org, dir, range, &ray); + res = setup_ray(oct, org, dir, range, &ray); if(res == RES_BAD_OP) { /* The ray cannot intersect the scene. */ res = RES_OK; goto exit; } + res = trace_ray(oct, challenge, filter, ray, hit); + if(res != RES_OK) goto error; + exit: return res; error: