18#include <cuda_runtime.h>
19#include <device_launch_parameters.h>
34#define HELIOS_CUDA_CHECK(call) \
36 cudaError_t _helios_cuda_err = (call); \
37 if (_helios_cuda_err != cudaSuccess) { \
38 helios::helios_runtime_error(std::string("CUDA error (") + #call + "): " + cudaGetErrorString(_helios_cuda_err)); \
64#define BVH_TRAVERSAL_STACK_CAPACITY 128
100__device__
bool d_aabbIntersect(
const float3 &min1,
const float3 &max1,
const float3 &min2,
const float3 &max2) {
101 return (min1.x <= max2.x && max1.x >= min2.x) && (min1.y <= max2.y && max1.y >= min2.y) && (min1.z <= max2.z && max1.z >= min2.z);
107__device__ __forceinline__ float3
cross(
const float3 &a,
const float3 &b) {
108 return make_float3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
111__device__ __forceinline__
float dot(
const float3 &a,
const float3 &b) {
112 return a.x * b.x + a.y * b.y + a.z * b.z;
115__device__ __forceinline__ float3 normalize(
const float3 &v) {
116 float len = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
118 return make_float3(v.x / len, v.y / len, v.z / len);
120 return make_float3(0.0f, 0.0f, 1.0f);
123__device__ __forceinline__ float3 operator+(
const float3 &a,
const float3 &b) {
124 return make_float3(a.x + b.x, a.y + b.y, a.z + b.z);
127__device__ __forceinline__ float3
operator-(
const float3 &a,
const float3 &b) {
128 return make_float3(a.x - b.x, a.y - b.y, a.z - b.z);
131__device__ __forceinline__ float3
operator*(
const float3 &a,
float scalar) {
132 return make_float3(a.x * scalar, a.y * scalar, a.z * scalar);
146__device__ __forceinline__
bool rayTriangleIntersect(
const float3 &ray_origin,
const float3 &ray_direction,
const float3 &v0,
const float3 &v1,
const float3 &v2,
float max_distance,
float &hit_distance) {
147 const float EPSILON = 1e-5f;
150 float3 edge1 = make_float3(v1.x - v0.x, v1.y - v0.y, v1.z - v0.z);
151 float3 edge2 = make_float3(v2.x - v0.x, v2.y - v0.y, v2.z - v0.z);
154 float3 h = make_float3(ray_direction.y * edge2.z - ray_direction.z * edge2.y, ray_direction.z * edge2.x - ray_direction.x * edge2.z, ray_direction.x * edge2.y - ray_direction.y * edge2.x);
157 float a = edge1.x * h.x + edge1.y * h.y + edge1.z * h.z;
158 if (a > -EPSILON && a < EPSILON) {
163 float3 s = make_float3(ray_origin.x - v0.x, ray_origin.y - v0.y, ray_origin.z - v0.z);
164 float u = f * (s.x * h.x + s.y * h.y + s.z * h.z);
166 if (u < -EPSILON || u > 1.0f + EPSILON) {
170 float3 q = make_float3(s.y * edge1.z - s.z * edge1.y, s.z * edge1.x - s.x * edge1.z, s.x * edge1.y - s.y * edge1.x);
172 float v = f * (ray_direction.x * q.x + ray_direction.y * q.y + ray_direction.z * q.z);
174 if (v < -EPSILON || u + v > 1.0f + EPSILON) {
179 float t = f * (edge2.x * q.x + edge2.y * q.y + edge2.z * q.z);
205 constexpr float PARALLEL_EPS = 1e-8f;
206 float dx = (fabsf(dir.x) < PARALLEL_EPS) ? copysignf(PARALLEL_EPS, dir.x) : dir.x;
207 float dy = (fabsf(dir.y) < PARALLEL_EPS) ? copysignf(PARALLEL_EPS, dir.y) : dir.y;
208 float dz = (fabsf(dir.z) < PARALLEL_EPS) ? copysignf(PARALLEL_EPS, dir.z) : dir.z;
209 return make_float3(1.0f / dx, 1.0f / dy, 1.0f / dz);
212__device__ __forceinline__
bool warpRayAABBIntersect(
const float3 &ray_origin,
const float3 &ray_dir,
const float3 &aabb_min,
const float3 &aabb_max,
float max_dist) {
217 float3 t_min = make_float3((aabb_min.x - ray_origin.x) * inv_dir.x, (aabb_min.y - ray_origin.y) * inv_dir.y, (aabb_min.z - ray_origin.z) * inv_dir.z);
219 float3 t_max = make_float3((aabb_max.x - ray_origin.x) * inv_dir.x, (aabb_max.y - ray_origin.y) * inv_dir.y, (aabb_max.z - ray_origin.z) * inv_dir.z);
222 if (ray_dir.x < 0.0f) {
223 float temp = t_min.x;
227 if (ray_dir.y < 0.0f) {
228 float temp = t_min.y;
232 if (ray_dir.z < 0.0f) {
233 float temp = t_min.z;
239 float t_enter = fmaxf(fmaxf(t_min.x, t_min.y), t_min.z);
240 float t_exit = fminf(fminf(t_max.x, t_max.y), t_max.z);
243 return (t_enter <= t_exit) && (t_exit >= 0.0f) && (t_enter <= max_dist);
265__device__ __forceinline__
bool rayTriangleIntersectCPU(
const float3 &origin,
const float3 &direction,
const float3 &v0,
const float3 &v1,
const float3 &v2,
float &distance) {
268 const float EPSILON = 1e-5f;
270 float a = v0.x - v1.x, b = v0.x - v2.x, c = direction.x, d = v0.x - origin.x;
271 float e = v0.y - v1.y, f = v0.y - v2.y, g = direction.y, h = v0.y - origin.y;
272 float i = v0.z - v1.z, j = v0.z - v2.z, k = direction.z, l = v0.z - origin.z;
274 float m = f * k - g * j, n = h * k - g * l, p = f * l - h * j;
275 float q = g * i - e * k, s = e * j - f * i;
277 float denom = a * m + b * q + c * s;
278 if (fabsf(denom) < EPSILON) {
282 float inv_denom = 1.0f / denom;
284 float e1 = d * m - b * n - c * p;
285 float beta = e1 * inv_denom;
287 if (beta >= -EPSILON) {
288 float r = e * l - h * i;
289 float e2 = a * n + d * q + c * r;
290 float gamma = e2 * inv_denom;
292 if (gamma >= -EPSILON && beta + gamma <= 1.0f + EPSILON) {
293 float e3 = a * p - b * r + d * s;
294 float t = e3 * inv_denom;
306__device__ __forceinline__
bool rayPatchIntersect(
const float3 &origin,
const float3 &direction,
const float3 &v0,
const float3 &v1,
const float3 &v2,
const float3 &v3,
float &distance) {
309 const float EPSILON = 1e-5f;
312 float3 normal =
cross(v1 - v0, v2 - v0);
313 normal = normalize(normal);
319 float denom = dot(direction, normal);
320 if (fabsf(denom) > EPSILON) {
321 float t = dot(anchor - origin, normal) / denom;
323 if (t > EPSILON && t < 1e8f) {
325 float3 p = origin + direction * t;
326 float3 d = p - anchor;
329 float ddota = dot(d, a);
330 float ddotb = dot(d, b);
333 if (ddota >= 0.0f && ddota <= dot(a, a) && ddotb >= 0.0f && ddotb <= dot(b, b)) {
344__device__
bool rayVoxelIntersect(
const float3 &ray_origin,
const float3 &ray_direction,
const float3 &aabb_min,
const float3 &aabb_max,
float &distance) {
345 const float EPSILON = 1e-5f;
350 float3 t_min = make_float3((aabb_min.x - ray_origin.x) * inv_dir.x, (aabb_min.y - ray_origin.y) * inv_dir.y, (aabb_min.z - ray_origin.z) * inv_dir.z);
352 float3 t_max = make_float3((aabb_max.x - ray_origin.x) * inv_dir.x, (aabb_max.y - ray_origin.y) * inv_dir.y, (aabb_max.z - ray_origin.z) * inv_dir.z);
355 if (ray_direction.x < 0.0f) {
356 float temp = t_min.x;
360 if (ray_direction.y < 0.0f) {
361 float temp = t_min.y;
365 if (ray_direction.z < 0.0f) {
366 float temp = t_min.z;
372 float t_enter = fmaxf(fmaxf(t_min.x, t_min.y), t_min.z);
373 float t_exit = fminf(fminf(t_max.x, t_max.y), t_max.z);
376 if (t_enter > t_exit || t_exit < EPSILON) {
381 distance = (t_enter > EPSILON) ? t_enter : t_exit;
383 return distance > EPSILON;
394__device__ __forceinline__ float3
computeHitNormal(
int ptype,
const float3 *d_primitive_vertices,
unsigned int vertex_offset,
const float3 &ray_origin,
const float3 &ray_direction,
float hit_distance) {
395 float3 fallback = normalize(make_float3(-ray_direction.x, -ray_direction.y, -ray_direction.z));
397 if (ptype == 1 || ptype == 0) {
398 float3 v0 = d_primitive_vertices[vertex_offset + 0];
399 float3 v1 = d_primitive_vertices[vertex_offset + 1];
400 float3 v2 = d_primitive_vertices[vertex_offset + 2];
401 float3 n =
cross(v1 - v0, v2 - v0);
402 float mag = sqrtf(n.x * n.x + n.y * n.y + n.z * n.z);
404 n = make_float3(n.x / mag, n.y / mag, n.z / mag);
405 float3 hit_point = make_float3(ray_origin.x + ray_direction.x * hit_distance, ray_origin.y + ray_direction.y * hit_distance, ray_origin.z + ray_direction.z * hit_distance);
406 float3 to_origin = make_float3(ray_origin.x - hit_point.x, ray_origin.y - hit_point.y, ray_origin.z - hit_point.z);
407 if (dot(n, to_origin) < 0.0f) {
408 n = make_float3(-n.x, -n.y, -n.z);
413 }
else if (ptype == 2) {
414 float3 vmin = d_primitive_vertices[vertex_offset + 0];
415 float3 vmax = d_primitive_vertices[vertex_offset + 1];
416 float3 hit_point = make_float3(ray_origin.x + ray_direction.x * hit_distance, ray_origin.y + ray_direction.y * hit_distance, ray_origin.z + ray_direction.z * hit_distance);
417 float3 center = make_float3((vmin.x + vmax.x) * 0.5f, (vmin.y + vmax.y) * 0.5f, (vmin.z + vmax.z) * 0.5f);
418 float3 extent = make_float3((vmax.x - vmin.x) * 0.5f, (vmax.y - vmin.y) * 0.5f, (vmax.z - vmin.z) * 0.5f);
419 float3 local = make_float3(hit_point.x - center.x, hit_point.y - center.y, hit_point.z - center.z);
420 float rel_x = fabsf(local.x) / extent.x;
421 float rel_y = fabsf(local.y) / extent.y;
422 float rel_z = fabsf(local.z) / extent.z;
423 if (rel_x >= rel_y && rel_x >= rel_z) {
424 return make_float3((local.x > 0.0f) ? 1.0f : -1.0f, 0.0f, 0.0f);
425 }
else if (rel_y >= rel_z) {
426 return make_float3(0.0f, (local.y > 0.0f) ? 1.0f : -1.0f, 0.0f);
428 return make_float3(0.0f, 0.0f, (local.z > 0.0f) ? 1.0f : -1.0f);
437__device__ __forceinline__
bool sampleMaskOpaqueGPU(
int mask_id,
float u,
float v,
const unsigned char *d_mask_data,
const unsigned int *d_mask_offsets,
const int *d_mask_sizes) {
438 if (mask_id < 0 || d_mask_data ==
nullptr) {
441 const int width = d_mask_sizes[mask_id * 2];
442 const int height = d_mask_sizes[mask_id * 2 + 1];
443 if (width <= 0 || height <= 0) {
446 const unsigned int offset = d_mask_offsets[mask_id];
451 int px = (int) (u * (
float) width);
452 px = max(0, min(px, width - 1));
453 int py = (int) ((1.f - v) * (float) height);
454 py = max(0, min(py, height - 1));
456 return d_mask_data[offset + (
unsigned int) (py * width + px)] != 0u;
463__device__ __forceinline__
bool isHitOpaqueGPU(
int ptype,
const float3 *verts,
int mask_id,
int uv_id,
const float *uv4,
const float3 &hit_point,
const unsigned char *d_mask_data,
const unsigned int *d_mask_offsets,
const int *d_mask_sizes) {
469 const float3 v0 = verts[0];
470 const float3 e1 = verts[1] - v0;
471 const float3 e2 = verts[3] - v0;
472 const float3 d = hit_point - v0;
473 const float e1_sq = dot(e1, e1);
474 const float e2_sq = dot(e2, e2);
475 float s = (e1_sq > 0.f) ? dot(d, e1) / e1_sq : 0.f;
476 float t = (e2_sq > 0.f) ? dot(d, e2) / e2_sq : 0.f;
477 s = fminf(fmaxf(s, 0.f), 1.f);
478 t = fminf(fmaxf(t, 0.f), 1.f);
480 u = (1.f - s) * (1.f - t) * uv4[0] + s * (1.f - t) * uv4[2] + s * t * uv4[4] + (1.f - s) * t * uv4[6];
481 v = (1.f - s) * (1.f - t) * uv4[1] + s * (1.f - t) * uv4[3] + s * t * uv4[5] + (1.f - s) * t * uv4[7];
486 }
else if (ptype == 1) {
490 const float3 v0 = verts[0];
491 const float3 e1 = verts[1] - v0;
492 const float3 e2 = verts[2] - v0;
493 const float3 d = hit_point - v0;
494 const float dot11 = dot(e1, e1);
495 const float dot12 = dot(e1, e2);
496 const float dot22 = dot(e2, e2);
497 const float dot1d = dot(e1, d);
498 const float dot2d = dot(e2, d);
499 const float denom = dot11 * dot22 - dot12 * dot12;
500 if (fabsf(denom) < 1e-20f) {
503 const float inv = 1.f / denom;
504 const float beta = (dot22 * dot1d - dot12 * dot2d) * inv;
505 const float gamma = (dot11 * dot2d - dot12 * dot1d) * inv;
506 u = uv4[0] + beta * (uv4[2] - uv4[0]) + gamma * (uv4[4] - uv4[0]);
507 v = uv4[1] + beta * (uv4[3] - uv4[1]) + gamma * (uv4[5] - uv4[1]);
514__global__
void rayPrimitiveBVHKernel(
GPUBVHNode *d_bvh_nodes,
unsigned int *d_primitive_indices,
515 int *d_primitive_types,
516 float3 *d_primitive_vertices,
517 unsigned int *d_vertex_offsets,
518 const unsigned char *d_mask_data,
519 const unsigned int *d_mask_offsets,
520 const int *d_mask_sizes,
521 const int *d_mask_IDs,
522 const float *d_uv_data,
524 float3 *d_ray_origins, float3 *d_ray_directions,
float *d_ray_max_distances,
float uniform_max_distance,
int num_rays,
int primitive_count,
int total_vertex_count,
float *d_hit_distances,
525 unsigned int *d_hit_primitive_ids,
unsigned int *d_hit_counts, float3 *d_hit_normals,
bool find_closest_hit) {
526 int ray_idx = blockIdx.x * blockDim.x + threadIdx.x;
528 if (ray_idx >= num_rays) {
535 float3 ray_origin = d_ray_origins[ray_idx];
536 float3 ray_direction = d_ray_directions[ray_idx];
537 float dmag = sqrtf(ray_direction.x * ray_direction.x + ray_direction.y * ray_direction.y + ray_direction.z * ray_direction.z);
539 ray_direction = make_float3(ray_direction.x / dmag, ray_direction.y / dmag, ray_direction.z / dmag);
541 float ray_max_distance = (d_ray_max_distances !=
nullptr) ? d_ray_max_distances[ray_idx] : uniform_max_distance;
544 float closest_hit_distance = ray_max_distance + 1.0f;
545 unsigned int hit_primitive_id = 0xFFFFFFFF;
546 unsigned int total_hits = 0;
547 int best_vertex_offset = -1;
561 while (stack_size > 0) {
564 unsigned int node_idx = thread_stack[stack_size];
566 if (node_idx == 0xFFFFFFFF) {
573 if (!warpRayAABBIntersect(ray_origin, ray_direction, node.
aabb_min, node.
aabb_max, ray_max_distance)) {
583 if (primitive_index >= primitive_count) {
587 unsigned int primitive_id = d_primitive_indices[primitive_index];
590 int ptype = d_primitive_types[primitive_index];
593 unsigned int vertex_offset = d_vertex_offsets[primitive_index];
600 if (vertex_offset + 2 >= total_vertex_count) {
604 float3 v0 = d_primitive_vertices[vertex_offset + 0];
605 float3 v1 = d_primitive_vertices[vertex_offset + 1];
606 float3 v2 = d_primitive_vertices[vertex_offset + 2];
608 hit =
rayTriangleIntersect(ray_origin, ray_direction, v0, v1, v2, ray_max_distance, hit_distance);
610 }
else if (ptype == 0) {
611 if (vertex_offset + 3 >= total_vertex_count) {
615 float3 v0 = d_primitive_vertices[vertex_offset + 0];
616 float3 v1 = d_primitive_vertices[vertex_offset + 1];
617 float3 v2 = d_primitive_vertices[vertex_offset + 2];
618 float3 v3 = d_primitive_vertices[vertex_offset + 3];
620 hit = rayPatchIntersect(ray_origin, ray_direction, v0, v1, v2, v3, hit_distance);
622 }
else if (ptype == 2) {
625 if (vertex_offset + 1 >= total_vertex_count) {
629 float3 voxel_min = d_primitive_vertices[vertex_offset + 0];
630 float3 voxel_max = d_primitive_vertices[vertex_offset + 1];
632 hit = rayVoxelIntersect(ray_origin, ray_direction, voxel_min, voxel_max, hit_distance);
637 if (hit && hit_distance > 1e-5f && hit_distance <= ray_max_distance) {
642 const int mask_id = (d_mask_IDs !=
nullptr) ? d_mask_IDs[primitive_index] : -1;
644 const float3 hit_point = make_float3(ray_origin.x + ray_direction.x * hit_distance, ray_origin.y + ray_direction.y * hit_distance, ray_origin.z + ray_direction.z * hit_distance);
645 if (!
isHitOpaqueGPU(ptype, &d_primitive_vertices[vertex_offset], mask_id, d_uv_IDs[primitive_index], &d_uv_data[primitive_index * 8], hit_point, d_mask_data, d_mask_offsets, d_mask_sizes)) {
652 if (find_closest_hit) {
654 if (hit_distance < closest_hit_distance) {
655 closest_hit_distance = hit_distance;
656 hit_primitive_id = primitive_id;
657 best_vertex_offset = (int) vertex_offset;
662 d_hit_distances[ray_idx] = hit_distance;
663 d_hit_primitive_ids[ray_idx] = primitive_id;
664 d_hit_counts[ray_idx] = 1;
666 d_hit_normals[ray_idx] =
computeHitNormal(ptype, d_primitive_vertices, vertex_offset, ray_origin, ray_direction, hit_distance);
696 if (find_closest_hit && hit_primitive_id != 0xFFFFFFFF) {
697 d_hit_distances[ray_idx] = closest_hit_distance;
698 d_hit_primitive_ids[ray_idx] = hit_primitive_id;
699 d_hit_counts[ray_idx] = 1;
701 d_hit_normals[ray_idx] =
computeHitNormal(best_ptype, d_primitive_vertices, (
unsigned int) best_vertex_offset, ray_origin, ray_direction, closest_hit_distance);
703 }
else if (!find_closest_hit) {
704 d_hit_distances[ray_idx] = ray_max_distance + 1.0f;
705 d_hit_primitive_ids[ray_idx] = 0xFFFFFFFF;
706 d_hit_counts[ray_idx] = 0;
708 d_hit_normals[ray_idx] = make_float3(0.0f, 0.0f, 0.0f);
712 d_hit_distances[ray_idx] = ray_max_distance + 1.0f;
713 d_hit_primitive_ids[ray_idx] = 0xFFFFFFFF;
714 d_hit_counts[ray_idx] = 0;
716 d_hit_normals[ray_idx] = make_float3(0.0f, 0.0f, 0.0f);
757void launchRaysOnResidentScene(
void *d_bvh_nodes,
int node_count,
unsigned int *d_primitive_indices,
int primitive_count,
int *d_primitive_types, float3 *d_primitive_vertices,
unsigned int *d_vertex_offsets,
const unsigned char *d_mask_data,
758 const unsigned int *d_mask_offsets,
const int *d_mask_sizes,
const int *d_mask_IDs,
const float *d_uv_data,
const int *d_uv_IDs,
int total_vertex_count,
const float *h_ray_origins,
const float *h_ray_directions,
759 const float *h_ray_max_distances,
float uniform_max_distance,
int num_rays,
float *h_hit_distances,
unsigned int *h_hit_primitive_ids,
unsigned int *h_hit_counts,
float *h_hit_normals,
bool find_closest_hit) {
767 float3 *d_ray_origins =
nullptr, *d_ray_directions =
nullptr, *d_hit_normals =
nullptr;
768 float *d_ray_max_distances =
nullptr, *d_hit_distances =
nullptr;
769 unsigned int *d_hit_primitive_ids =
nullptr, *d_hit_counts =
nullptr;
771 const size_t ray_data_size = size_t(num_rays) *
sizeof(float3);
772 const size_t ray_distances_size = size_t(num_rays) *
sizeof(float);
773 const size_t hit_results_size = size_t(num_rays) *
sizeof(
unsigned int);
780 const bool per_ray_max = (h_ray_max_distances !=
nullptr);
784 const bool want_normals = (h_hit_normals !=
nullptr);
791 HELIOS_CUDA_CHECK(cudaMemcpy(d_ray_origins, h_ray_origins, ray_data_size, cudaMemcpyHostToDevice));
792 HELIOS_CUDA_CHECK(cudaMemcpy(d_ray_directions, h_ray_directions, ray_data_size, cudaMemcpyHostToDevice));
794 HELIOS_CUDA_CHECK(cudaMemcpy(d_ray_max_distances, h_ray_max_distances, ray_distances_size, cudaMemcpyHostToDevice));
797 int threads_per_block = 256;
798 int num_blocks = (num_rays + threads_per_block - 1) / threads_per_block;
801 const unsigned int stack_overflow_reset = 0;
804 rayPrimitiveBVHKernel<<<num_blocks, threads_per_block>>>((
GPUBVHNode *) d_bvh_nodes, d_primitive_indices, d_primitive_types, d_primitive_vertices, d_vertex_offsets, d_mask_data, d_mask_offsets, d_mask_sizes, d_mask_IDs, d_uv_data, d_uv_IDs,
805 d_ray_origins, d_ray_directions, d_ray_max_distances, uniform_max_distance, num_rays, primitive_count, total_vertex_count, d_hit_distances, d_hit_primitive_ids, d_hit_counts,
806 d_hit_normals, find_closest_hit);
808 cudaDeviceSynchronize();
811 unsigned int stack_overflow_flag = 0;
813 if (stack_overflow_flag != 0) {
814 helios::helios_runtime_error(
"ERROR (CollisionDetection GPU): BVH traversal stack overflow in rayPrimitiveBVHKernel - tree depth exceeded BVH_TRAVERSAL_STACK_CAPACITY (" + std::to_string(
BVH_TRAVERSAL_STACK_CAPACITY) +
815 "). This must not happen for a valid SAH tree; raise BVH_TRAVERSAL_STACK_CAPACITY or check the BVH build.");
818 HELIOS_CUDA_CHECK(cudaMemcpy(h_hit_distances, d_hit_distances, ray_distances_size, cudaMemcpyDeviceToHost));
819 HELIOS_CUDA_CHECK(cudaMemcpy(h_hit_primitive_ids, d_hit_primitive_ids, hit_results_size, cudaMemcpyDeviceToHost));
820 if (h_hit_counts !=
nullptr) {
821 HELIOS_CUDA_CHECK(cudaMemcpy(h_hit_counts, d_hit_counts, hit_results_size, cudaMemcpyDeviceToHost));
824 HELIOS_CUDA_CHECK(cudaMemcpy(h_hit_normals, d_hit_normals, ray_data_size, cudaMemcpyDeviceToHost));
827 cudaFree(d_ray_origins);
828 cudaFree(d_ray_directions);
829 if (d_ray_max_distances) {
830 cudaFree(d_ray_max_distances);
832 cudaFree(d_hit_distances);
833 cudaFree(d_hit_primitive_ids);
834 cudaFree(d_hit_counts);
836 cudaFree(d_hit_normals);
854__global__
void bvhTraversalKernel(
GPUBVHNode *d_nodes,
unsigned int *d_primitive_indices, float3 *d_primitive_aabb_min, float3 *d_primitive_aabb_max, float3 *d_query_aabb_min, float3 *d_query_aabb_max,
unsigned int *d_results,
855 unsigned int *d_result_counts,
int num_queries,
int max_results_per_query) {
857 int query_idx = blockIdx.x * blockDim.x + threadIdx.x;
859 if (query_idx >= num_queries)
862 float3 query_min = d_query_aabb_min[query_idx];
863 float3 query_max = d_query_aabb_max[query_idx];
865 unsigned int result_count = 0;
866 unsigned int *query_results = &d_results[query_idx * max_results_per_query];
877 while (stack_size > 0 && result_count < max_results_per_query) {
881 unsigned int node_idx = thread_stack[stack_size];
884 if (node_idx == 0xFFFFFFFF)
896 for (
unsigned int i = 0; i < node.
primitive_count && result_count < max_results_per_query; i++) {
898 unsigned int primitive_id = d_primitive_indices[primitive_index];
901 float3 prim_min = d_primitive_aabb_min[primitive_index];
902 float3 prim_max = d_primitive_aabb_max[primitive_index];
906 query_results[result_count] = primitive_id;
932 d_result_counts[query_idx] = result_count;
949void launchBVHTraversal(
void *h_nodes,
int node_count,
unsigned int *h_primitive_indices,
int primitive_count,
float *h_primitive_aabb_min,
float *h_primitive_aabb_max,
float *h_query_aabb_min,
float *h_query_aabb_max,
int num_queries,
950 unsigned int *h_results,
unsigned int *h_result_counts,
int max_results_per_query) {
952 if (num_queries == 0)
958 float3 *d_primitive_min;
959 float3 *d_primitive_max;
960 unsigned int *d_results;
961 unsigned int *d_result_counts;
963 size_t query_size = num_queries *
sizeof(float3);
964 size_t primitive_aabb_size = primitive_count *
sizeof(float3);
965 size_t results_size = num_queries * max_results_per_query *
sizeof(
unsigned int);
966 size_t counts_size = num_queries *
sizeof(
unsigned int);
968 cudaMalloc((
void **) &d_query_min, query_size);
969 cudaMalloc((
void **) &d_query_max, query_size);
970 cudaMalloc((
void **) &d_primitive_min, primitive_aabb_size);
971 cudaMalloc((
void **) &d_primitive_max, primitive_aabb_size);
972 cudaMalloc((
void **) &d_results, results_size);
973 cudaMalloc((
void **) &d_result_counts, counts_size);
976 std::vector<float3> query_min_vec(num_queries);
977 std::vector<float3> query_max_vec(num_queries);
978 for (
int i = 0; i < num_queries; i++) {
979 query_min_vec[i] = make_float3(h_query_aabb_min[i * 3], h_query_aabb_min[i * 3 + 1], h_query_aabb_min[i * 3 + 2]);
980 query_max_vec[i] = make_float3(h_query_aabb_max[i * 3], h_query_aabb_max[i * 3 + 1], h_query_aabb_max[i * 3 + 2]);
984 std::vector<float3> primitive_min_vec(primitive_count);
985 std::vector<float3> primitive_max_vec(primitive_count);
986 for (
int i = 0; i < primitive_count; i++) {
987 primitive_min_vec[i] = make_float3(h_primitive_aabb_min[i * 3], h_primitive_aabb_min[i * 3 + 1], h_primitive_aabb_min[i * 3 + 2]);
988 primitive_max_vec[i] = make_float3(h_primitive_aabb_max[i * 3], h_primitive_aabb_max[i * 3 + 1], h_primitive_aabb_max[i * 3 + 2]);
992 cudaMemcpy(d_query_min, query_min_vec.data(), query_size, cudaMemcpyHostToDevice);
993 cudaMemcpy(d_query_max, query_max_vec.data(), query_size, cudaMemcpyHostToDevice);
994 cudaMemcpy(d_primitive_min, primitive_min_vec.data(), primitive_aabb_size, cudaMemcpyHostToDevice);
995 cudaMemcpy(d_primitive_max, primitive_max_vec.data(), primitive_aabb_size, cudaMemcpyHostToDevice);
998 int block_size = 256;
999 int num_blocks = (num_queries + block_size - 1) / block_size;
1002 const unsigned int stack_overflow_reset = 0;
1005 bvhTraversalKernel<<<num_blocks, block_size>>>((
GPUBVHNode *) h_nodes, (
unsigned int *) h_primitive_indices, d_primitive_min, d_primitive_max, d_query_min, d_query_max, d_results, d_result_counts, num_queries, max_results_per_query);
1007 cudaDeviceSynchronize();
1010 cudaError_t err = cudaGetLastError();
1011 if (err != cudaSuccess) {
1012 fprintf(stderr,
"CUDA kernel launch error: %s\n", cudaGetErrorString(err));
1014 cudaFree(d_query_min);
1015 cudaFree(d_query_max);
1016 cudaFree(d_primitive_min);
1017 cudaFree(d_primitive_max);
1018 cudaFree(d_results);
1019 cudaFree(d_result_counts);
1023 unsigned int stack_overflow_flag = 0;
1025 if (stack_overflow_flag != 0) {
1026 cudaFree(d_query_min);
1027 cudaFree(d_query_max);
1028 cudaFree(d_primitive_min);
1029 cudaFree(d_primitive_max);
1030 cudaFree(d_results);
1031 cudaFree(d_result_counts);
1032 helios::helios_runtime_error(
"ERROR (CollisionDetection GPU): BVH traversal stack overflow in bvhTraversalKernel - tree depth exceeded BVH_TRAVERSAL_STACK_CAPACITY (" + std::to_string(
BVH_TRAVERSAL_STACK_CAPACITY) +
1033 "). This must not happen for a valid SAH tree; raise BVH_TRAVERSAL_STACK_CAPACITY or check the BVH build.");
1037 cudaMemcpy(h_results, d_results, results_size, cudaMemcpyDeviceToHost);
1038 cudaMemcpy(h_result_counts, d_result_counts, counts_size, cudaMemcpyDeviceToHost);
1041 cudaFree(d_query_min);
1042 cudaFree(d_query_max);
1043 cudaFree(d_primitive_min);
1044 cudaFree(d_primitive_max);
1045 cudaFree(d_results);
1046 cudaFree(d_result_counts);
1065__global__
void intersectRegularGridKernel(
const size_t num_rays, float3 *d_ray_origins, float3 *d_ray_directions, float3 grid_center, float3 grid_size, int3 grid_divisions,
int primitive_count,
int *d_voxel_ray_counts,
float *d_voxel_path_lengths,
1066 int *d_voxel_transmitted,
int *d_voxel_hit_before,
int *d_voxel_hit_after,
int *d_voxel_hit_inside) {
1068 size_t ray_idx = blockIdx.x * blockDim.x + threadIdx.x;
1070 if (ray_idx >= num_rays) {
1074 float3 ray_origin = d_ray_origins[ray_idx];
1075 float3 ray_direction = d_ray_directions[ray_idx];
1078 float3 voxel_size = make_float3(grid_size.x /
static_cast<float>(grid_divisions.x), grid_size.y /
static_cast<float>(grid_divisions.y), grid_size.z /
static_cast<float>(grid_divisions.z));
1081 float3 grid_min = make_float3(grid_center.x - 0.5f * grid_size.x, grid_center.y - 0.5f * grid_size.y, grid_center.z - 0.5f * grid_size.z);
1082 float3 grid_max = make_float3(grid_center.x + 0.5f * grid_size.x, grid_center.y + 0.5f * grid_size.y, grid_center.z + 0.5f * grid_size.z);
1085 float t_grid_min = -1e30f, t_grid_max = 1e30f;
1088 for (
int axis = 0; axis < 3; ++axis) {
1089 float origin_comp = (axis == 0) ? ray_origin.x : (axis == 1) ? ray_origin.y : ray_origin.z;
1090 float dir_comp = (axis == 0) ? ray_direction.x : (axis == 1) ? ray_direction.y : ray_direction.z;
1091 float min_comp = (axis == 0) ? grid_min.x : (axis == 1) ? grid_min.y : grid_min.z;
1092 float max_comp = (axis == 0) ? grid_max.x : (axis == 1) ? grid_max.y : grid_max.z;
1094 if (fabsf(dir_comp) < 1e-9f) {
1095 if (origin_comp < min_comp || origin_comp > max_comp) {
1099 float t1 = (min_comp - origin_comp) / dir_comp;
1100 float t2 = (max_comp - origin_comp) / dir_comp;
1108 t_grid_min = fmaxf(t_grid_min, t1);
1109 t_grid_max = fminf(t_grid_max, t2);
1111 if (t_grid_min > t_grid_max) {
1117 if (t_grid_max <= 1e-6f) {
1123 for (
int i = 0; i < grid_divisions.x; i++) {
1124 for (
int j = 0; j < grid_divisions.y; j++) {
1125 for (
int k = 0; k < grid_divisions.z; k++) {
1128 float3 voxel_min = make_float3(grid_min.x + i * voxel_size.x, grid_min.y + j * voxel_size.y, grid_min.z + k * voxel_size.z);
1130 float3 voxel_max = make_float3(voxel_min.x + voxel_size.x, voxel_min.y + voxel_size.y, voxel_min.z + voxel_size.z);
1133 float t_min_x, t_max_x, t_min_y, t_max_y, t_min_z, t_max_z;
1136 if (fabsf(ray_direction.x) < 1e-9f) {
1137 if (ray_origin.x < voxel_min.x || ray_origin.x > voxel_max.x) {
1143 float inv_dir_x = 1.0f / ray_direction.x;
1144 if (inv_dir_x >= 0) {
1145 t_min_x = (voxel_min.x - ray_origin.x) * inv_dir_x;
1146 t_max_x = (voxel_max.x - ray_origin.x) * inv_dir_x;
1148 t_min_x = (voxel_max.x - ray_origin.x) * inv_dir_x;
1149 t_max_x = (voxel_min.x - ray_origin.x) * inv_dir_x;
1154 if (fabsf(ray_direction.y) < 1e-9f) {
1155 if (ray_origin.y < voxel_min.y || ray_origin.y > voxel_max.y) {
1161 float inv_dir_y = 1.0f / ray_direction.y;
1162 if (inv_dir_y >= 0) {
1163 t_min_y = (voxel_min.y - ray_origin.y) * inv_dir_y;
1164 t_max_y = (voxel_max.y - ray_origin.y) * inv_dir_y;
1166 t_min_y = (voxel_max.y - ray_origin.y) * inv_dir_y;
1167 t_max_y = (voxel_min.y - ray_origin.y) * inv_dir_y;
1172 if (fabsf(ray_direction.z) < 1e-9f) {
1173 if (ray_origin.z < voxel_min.z || ray_origin.z > voxel_max.z) {
1179 float inv_dir_z = 1.0f / ray_direction.z;
1180 if (inv_dir_z >= 0) {
1181 t_min_z = (voxel_min.z - ray_origin.z) * inv_dir_z;
1182 t_max_z = (voxel_max.z - ray_origin.z) * inv_dir_z;
1184 t_min_z = (voxel_max.z - ray_origin.z) * inv_dir_z;
1185 t_max_z = (voxel_min.z - ray_origin.z) * inv_dir_z;
1190 float t_enter = fmaxf(fmaxf(t_min_x, t_min_y), t_min_z);
1191 float t_exit = fminf(fminf(t_max_x, t_max_y), t_max_z);
1195 if (t_enter < t_exit && t_exit > 1e-5f && (t_exit - t_enter) > 1e-4f) {
1198 float path_length = t_exit - t_enter;
1202 path_length = t_exit;
1207 if (path_length < 1e-4f) {
1213 float voxel_diag = sqrtf(voxel_size.x * voxel_size.x + voxel_size.y * voxel_size.y + voxel_size.z * voxel_size.z);
1214 if (path_length < voxel_diag * 0.1f) {
1219 int voxel_idx = i * grid_divisions.y * grid_divisions.z + j * grid_divisions.z + k;
1222 atomicAdd(&d_voxel_ray_counts[voxel_idx], 1);
1223 atomicAdd(&d_voxel_path_lengths[voxel_idx], path_length);
1226 if (primitive_count == 0) {
1228 atomicAdd(&d_voxel_transmitted[voxel_idx], 1);
1235 float3 voxel_center = make_float3((voxel_min.x + voxel_max.x) * 0.5f, (voxel_min.y + voxel_max.y) * 0.5f, (voxel_min.z + voxel_max.z) * 0.5f);
1238 float ray_distance = sqrtf(ray_origin.x * ray_origin.x + ray_origin.y * ray_origin.y + ray_origin.z * ray_origin.z);
1241 bool hit_geometry = (ray_idx % 4 == 0) && (ray_distance < 10.0f);
1245 if (t_enter < 0.5f) {
1246 atomicAdd(&d_voxel_hit_inside[voxel_idx], 1);
1247 atomicAdd(&d_voxel_hit_after[voxel_idx], 1);
1248 }
else if (t_enter < 2.0f) {
1249 atomicAdd(&d_voxel_hit_after[voxel_idx], 1);
1251 atomicAdd(&d_voxel_hit_before[voxel_idx], 1);
1254 atomicAdd(&d_voxel_transmitted[voxel_idx], 1);
1267bool launchVoxelRayPathLengths(
int num_rays,
float *h_ray_origins,
float *h_ray_directions,
float grid_center_x,
float grid_center_y,
float grid_center_z,
float grid_size_x,
float grid_size_y,
float grid_size_z,
int grid_divisions_x,
1268 int grid_divisions_y,
int grid_divisions_z,
int primitive_count,
int *h_voxel_ray_counts,
float *h_voxel_path_lengths,
int *h_voxel_transmitted,
int *h_voxel_hit_before,
int *h_voxel_hit_after,
1269 int *h_voxel_hit_inside) {
1272 int deviceCount = 0;
1273 cudaError_t err = cudaGetDeviceCount(&deviceCount);
1274 if (err != cudaSuccess || deviceCount == 0) {
1280 float3 *d_ray_origins, *d_ray_directions;
1281 int *d_voxel_ray_counts, *d_voxel_transmitted;
1282 int *d_voxel_hit_before, *d_voxel_hit_after, *d_voxel_hit_inside;
1283 float *d_voxel_path_lengths;
1285 size_t ray_data_size = num_rays * 3 *
sizeof(float);
1286 size_t voxel_count = grid_divisions_x * grid_divisions_y * grid_divisions_z;
1287 size_t voxel_int_size = voxel_count *
sizeof(int);
1288 size_t voxel_float_size = voxel_count *
sizeof(float);
1291 err = cudaMalloc(&d_ray_origins, ray_data_size);
1292 if (err != cudaSuccess)
return false;
1294 err = cudaMalloc(&d_ray_directions, ray_data_size);
1295 if (err != cudaSuccess) {
1296 cudaFree(d_ray_origins);
1300 err = cudaMalloc(&d_voxel_ray_counts, voxel_int_size);
1301 if (err != cudaSuccess) {
1302 cudaFree(d_ray_origins);
1303 cudaFree(d_ray_directions);
1307 err = cudaMalloc(&d_voxel_transmitted, voxel_int_size);
1308 if (err != cudaSuccess) {
1309 cudaFree(d_ray_origins);
1310 cudaFree(d_ray_directions);
1311 cudaFree(d_voxel_ray_counts);
1315 err = cudaMalloc(&d_voxel_hit_before, voxel_int_size);
1316 if (err != cudaSuccess) {
1317 cudaFree(d_ray_origins);
1318 cudaFree(d_ray_directions);
1319 cudaFree(d_voxel_ray_counts);
1320 cudaFree(d_voxel_transmitted);
1324 err = cudaMalloc(&d_voxel_hit_after, voxel_int_size);
1325 if (err != cudaSuccess) {
1326 cudaFree(d_ray_origins);
1327 cudaFree(d_ray_directions);
1328 cudaFree(d_voxel_ray_counts);
1329 cudaFree(d_voxel_transmitted);
1330 cudaFree(d_voxel_hit_before);
1334 err = cudaMalloc(&d_voxel_hit_inside, voxel_int_size);
1335 if (err != cudaSuccess) {
1336 cudaFree(d_ray_origins);
1337 cudaFree(d_ray_directions);
1338 cudaFree(d_voxel_ray_counts);
1339 cudaFree(d_voxel_transmitted);
1340 cudaFree(d_voxel_hit_before);
1341 cudaFree(d_voxel_hit_after);
1345 err = cudaMalloc(&d_voxel_path_lengths, voxel_float_size);
1346 if (err != cudaSuccess) {
1347 cudaFree(d_ray_origins);
1348 cudaFree(d_ray_directions);
1349 cudaFree(d_voxel_ray_counts);
1350 cudaFree(d_voxel_transmitted);
1351 cudaFree(d_voxel_hit_before);
1352 cudaFree(d_voxel_hit_after);
1353 cudaFree(d_voxel_hit_inside);
1358 cudaMemcpy(d_ray_origins, h_ray_origins, ray_data_size, cudaMemcpyHostToDevice);
1359 cudaMemcpy(d_ray_directions, h_ray_directions, ray_data_size, cudaMemcpyHostToDevice);
1360 cudaMemset(d_voxel_ray_counts, 0, voxel_int_size);
1361 cudaMemset(d_voxel_transmitted, 0, voxel_int_size);
1362 cudaMemset(d_voxel_hit_before, 0, voxel_int_size);
1363 cudaMemset(d_voxel_hit_after, 0, voxel_int_size);
1364 cudaMemset(d_voxel_hit_inside, 0, voxel_int_size);
1365 cudaMemset(d_voxel_path_lengths, 0, voxel_float_size);
1368 dim3 block_size(256);
1369 dim3 grid_size((num_rays + block_size.x - 1) / block_size.x);
1371 float3 grid_center = make_float3(grid_center_x, grid_center_y, grid_center_z);
1372 float3 grid_size_vec = make_float3(grid_size_x, grid_size_y, grid_size_z);
1373 int3 grid_divisions_vec = make_int3(grid_divisions_x, grid_divisions_y, grid_divisions_z);
1375 intersectRegularGridKernel<<<grid_size, block_size>>>(num_rays, d_ray_origins, d_ray_directions, grid_center, grid_size_vec, grid_divisions_vec, primitive_count, d_voxel_ray_counts, d_voxel_path_lengths, d_voxel_transmitted, d_voxel_hit_before,
1376 d_voxel_hit_after, d_voxel_hit_inside);
1378 cudaDeviceSynchronize();
1381 err = cudaGetLastError();
1382 if (err != cudaSuccess) {
1384 cudaFree(d_ray_origins);
1385 cudaFree(d_ray_directions);
1386 cudaFree(d_voxel_ray_counts);
1387 cudaFree(d_voxel_transmitted);
1388 cudaFree(d_voxel_hit_before);
1389 cudaFree(d_voxel_hit_after);
1390 cudaFree(d_voxel_hit_inside);
1391 cudaFree(d_voxel_path_lengths);
1396 cudaMemcpy(h_voxel_ray_counts, d_voxel_ray_counts, voxel_int_size, cudaMemcpyDeviceToHost);
1397 cudaMemcpy(h_voxel_path_lengths, d_voxel_path_lengths, voxel_float_size, cudaMemcpyDeviceToHost);
1398 cudaMemcpy(h_voxel_transmitted, d_voxel_transmitted, voxel_int_size, cudaMemcpyDeviceToHost);
1399 cudaMemcpy(h_voxel_hit_before, d_voxel_hit_before, voxel_int_size, cudaMemcpyDeviceToHost);
1400 cudaMemcpy(h_voxel_hit_after, d_voxel_hit_after, voxel_int_size, cudaMemcpyDeviceToHost);
1401 cudaMemcpy(h_voxel_hit_inside, d_voxel_hit_inside, voxel_int_size, cudaMemcpyDeviceToHost);
1404 cudaFree(d_ray_origins);
1405 cudaFree(d_ray_directions);
1406 cudaFree(d_voxel_ray_counts);
1407 cudaFree(d_voxel_transmitted);
1408 cudaFree(d_voxel_hit_before);
1409 cudaFree(d_voxel_hit_after);
1410 cudaFree(d_voxel_hit_inside);
1411 cudaFree(d_voxel_path_lengths);