123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- /*
- * Copyright 2011-2013 Blender Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /* BVH
- *
- * Bounding volume hierarchy for ray tracing. We compile different variations
- * of the same BVH traversal function for faster rendering when some types of
- * primitives are not needed, using #includes to work around the lack of
- * C++ templates in OpenCL.
- *
- * Originally based on "Understanding the Efficiency of Ray Traversal on GPUs",
- * the code has been extended and modified to support more primitives and work
- * with CPU/CUDA/OpenCL. */
- #ifdef __EMBREE__
- # include "kernel/bvh/bvh_embree.h"
- #endif
- CCL_NAMESPACE_BEGIN
- #include "kernel/bvh/bvh_types.h"
- /* Common QBVH functions. */
- #ifdef __QBVH__
- # include "kernel/bvh/qbvh_nodes.h"
- # ifdef __KERNEL_AVX2__
- # include "kernel/bvh/obvh_nodes.h"
- # endif
- #endif
- /* Regular BVH traversal */
- #include "kernel/bvh/bvh_nodes.h"
- #define BVH_FUNCTION_NAME bvh_intersect
- #define BVH_FUNCTION_FEATURES 0
- #include "kernel/bvh/bvh_traversal.h"
- #if defined(__INSTANCING__)
- # define BVH_FUNCTION_NAME bvh_intersect_instancing
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING
- # include "kernel/bvh/bvh_traversal.h"
- #endif
- #if defined(__HAIR__)
- # define BVH_FUNCTION_NAME bvh_intersect_hair
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
- # include "kernel/bvh/bvh_traversal.h"
- #endif
- #if defined(__OBJECT_MOTION__)
- # define BVH_FUNCTION_NAME bvh_intersect_motion
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION
- # include "kernel/bvh/bvh_traversal.h"
- #endif
- #if defined(__HAIR__) && defined(__OBJECT_MOTION__)
- # define BVH_FUNCTION_NAME bvh_intersect_hair_motion
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR | BVH_MOTION
- # include "kernel/bvh/bvh_traversal.h"
- #endif
- /* Subsurface scattering BVH traversal */
- #if defined(__BVH_LOCAL__)
- # define BVH_FUNCTION_NAME bvh_intersect_local
- # define BVH_FUNCTION_FEATURES BVH_HAIR
- # include "kernel/bvh/bvh_local.h"
- # if defined(__OBJECT_MOTION__)
- # define BVH_FUNCTION_NAME bvh_intersect_local_motion
- # define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR
- # include "kernel/bvh/bvh_local.h"
- # endif
- #endif /* __BVH_LOCAL__ */
- /* Volume BVH traversal */
- #if defined(__VOLUME__)
- # define BVH_FUNCTION_NAME bvh_intersect_volume
- # define BVH_FUNCTION_FEATURES BVH_HAIR
- # include "kernel/bvh/bvh_volume.h"
- # if defined(__INSTANCING__)
- # define BVH_FUNCTION_NAME bvh_intersect_volume_instancing
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
- # include "kernel/bvh/bvh_volume.h"
- # endif
- # if defined(__OBJECT_MOTION__)
- # define BVH_FUNCTION_NAME bvh_intersect_volume_motion
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION | BVH_HAIR
- # include "kernel/bvh/bvh_volume.h"
- # endif
- #endif /* __VOLUME__ */
- /* Record all intersections - Shadow BVH traversal */
- #if defined(__SHADOW_RECORD_ALL__)
- # define BVH_FUNCTION_NAME bvh_intersect_shadow_all
- # define BVH_FUNCTION_FEATURES 0
- # include "kernel/bvh/bvh_shadow_all.h"
- # if defined(__INSTANCING__)
- # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_instancing
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING
- # include "kernel/bvh/bvh_shadow_all.h"
- # endif
- # if defined(__HAIR__)
- # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
- # include "kernel/bvh/bvh_shadow_all.h"
- # endif
- # if defined(__OBJECT_MOTION__)
- # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_motion
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION
- # include "kernel/bvh/bvh_shadow_all.h"
- # endif
- # if defined(__HAIR__) && defined(__OBJECT_MOTION__)
- # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair_motion
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR | BVH_MOTION
- # include "kernel/bvh/bvh_shadow_all.h"
- # endif
- #endif /* __SHADOW_RECORD_ALL__ */
- /* Record all intersections - Volume BVH traversal */
- #if defined(__VOLUME_RECORD_ALL__)
- # define BVH_FUNCTION_NAME bvh_intersect_volume_all
- # define BVH_FUNCTION_FEATURES BVH_HAIR
- # include "kernel/bvh/bvh_volume_all.h"
- # if defined(__INSTANCING__)
- # define BVH_FUNCTION_NAME bvh_intersect_volume_all_instancing
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_HAIR
- # include "kernel/bvh/bvh_volume_all.h"
- # endif
- # if defined(__OBJECT_MOTION__)
- # define BVH_FUNCTION_NAME bvh_intersect_volume_all_motion
- # define BVH_FUNCTION_FEATURES BVH_INSTANCING | BVH_MOTION | BVH_HAIR
- # include "kernel/bvh/bvh_volume_all.h"
- # endif
- #endif /* __VOLUME_RECORD_ALL__ */
- #undef BVH_FEATURE
- #undef BVH_NAME_JOIN
- #undef BVH_NAME_EVAL
- #undef BVH_FUNCTION_FULL_NAME
- ccl_device_inline bool scene_intersect_valid(const Ray *ray)
- {
- /* NOTE: Due to some vectorization code non-finite origin point might
- * cause lots of false-positive intersections which will overflow traversal
- * stack.
- * This code is a quick way to perform early output, to avoid crashes in
- * such cases.
- * From production scenes so far it seems it's enough to test first element
- * only.
- */
- return isfinite_safe(ray->P.x) && isfinite_safe(ray->D.x);
- }
- /* Note: ray is passed by value to work around a possible CUDA compiler bug. */
- ccl_device_intersect bool scene_intersect(KernelGlobals *kg,
- const Ray ray,
- const uint visibility,
- Intersection *isect)
- {
- PROFILING_INIT(kg, PROFILING_INTERSECT);
- if (!scene_intersect_valid(&ray)) {
- return false;
- }
- #ifdef __EMBREE__
- if (kernel_data.bvh.scene) {
- isect->t = ray.t;
- CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR);
- IntersectContext rtc_ctx(&ctx);
- RTCRayHit ray_hit;
- kernel_embree_setup_rayhit(ray, ray_hit, visibility);
- rtcIntersect1(kernel_data.bvh.scene, &rtc_ctx.context, &ray_hit);
- if (ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID &&
- ray_hit.hit.primID != RTC_INVALID_GEOMETRY_ID) {
- kernel_embree_convert_hit(kg, &ray_hit.ray, &ray_hit.hit, isect);
- return true;
- }
- return false;
- }
- #endif /* __EMBREE__ */
- #ifdef __OBJECT_MOTION__
- if (kernel_data.bvh.have_motion) {
- # ifdef __HAIR__
- if (kernel_data.bvh.have_curves)
- return bvh_intersect_hair_motion(kg, &ray, isect, visibility);
- # endif /* __HAIR__ */
- return bvh_intersect_motion(kg, &ray, isect, visibility);
- }
- #endif /* __OBJECT_MOTION__ */
- #ifdef __HAIR__
- if (kernel_data.bvh.have_curves)
- return bvh_intersect_hair(kg, &ray, isect, visibility);
- #endif /* __HAIR__ */
- #ifdef __KERNEL_CPU__
- # ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing)
- return bvh_intersect_instancing(kg, &ray, isect, visibility);
- # endif /* __INSTANCING__ */
- return bvh_intersect(kg, &ray, isect, visibility);
- #else /* __KERNEL_CPU__ */
- # ifdef __INSTANCING__
- return bvh_intersect_instancing(kg, &ray, isect, visibility);
- # else
- return bvh_intersect(kg, &ray, isect, visibility);
- # endif /* __INSTANCING__ */
- #endif /* __KERNEL_CPU__ */
- }
- #ifdef __BVH_LOCAL__
- /* Note: ray is passed by value to work around a possible CUDA compiler bug. */
- ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg,
- const Ray ray,
- LocalIntersection *local_isect,
- int local_object,
- uint *lcg_state,
- int max_hits)
- {
- PROFILING_INIT(kg, PROFILING_INTERSECT_LOCAL);
- if (!scene_intersect_valid(&ray)) {
- local_isect->num_hits = 0;
- return false;
- }
- # ifdef __EMBREE__
- if (kernel_data.bvh.scene) {
- CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SSS);
- ctx.lcg_state = lcg_state;
- ctx.max_hits = max_hits;
- ctx.ss_isect = local_isect;
- local_isect->num_hits = 0;
- ctx.sss_object_id = local_object;
- IntersectContext rtc_ctx(&ctx);
- RTCRay rtc_ray;
- kernel_embree_setup_ray(ray, rtc_ray, PATH_RAY_ALL_VISIBILITY);
- /* Get the Embree scene for this intersection. */
- RTCGeometry geom = rtcGetGeometry(kernel_data.bvh.scene, local_object * 2);
- if (geom) {
- float3 P = ray.P;
- float3 dir = ray.D;
- float3 idir = ray.D;
- const int object_flag = kernel_tex_fetch(__object_flag, local_object);
- if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
- Transform ob_itfm;
- rtc_ray.tfar = bvh_instance_motion_push(
- kg, local_object, &ray, &P, &dir, &idir, ray.t, &ob_itfm);
- /* bvh_instance_motion_push() returns the inverse transform but
- * it's not needed here. */
- (void)ob_itfm;
- rtc_ray.org_x = P.x;
- rtc_ray.org_y = P.y;
- rtc_ray.org_z = P.z;
- rtc_ray.dir_x = dir.x;
- rtc_ray.dir_y = dir.y;
- rtc_ray.dir_z = dir.z;
- }
- RTCScene scene = (RTCScene)rtcGetGeometryUserData(geom);
- if (scene) {
- rtcOccluded1(scene, &rtc_ctx.context, &rtc_ray);
- }
- }
- return local_isect->num_hits > 0;
- }
- # endif /* __EMBREE__ */
- # ifdef __OBJECT_MOTION__
- if (kernel_data.bvh.have_motion) {
- return bvh_intersect_local_motion(kg, &ray, local_isect, local_object, lcg_state, max_hits);
- }
- # endif /* __OBJECT_MOTION__ */
- return bvh_intersect_local(kg, &ray, local_isect, local_object, lcg_state, max_hits);
- }
- #endif
- #ifdef __SHADOW_RECORD_ALL__
- ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- uint visibility,
- uint max_hits,
- uint *num_hits)
- {
- PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW_ALL);
- if (!scene_intersect_valid(ray)) {
- *num_hits = 0;
- return false;
- }
- # ifdef __EMBREE__
- if (kernel_data.bvh.scene) {
- CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SHADOW_ALL);
- ctx.isect_s = isect;
- ctx.max_hits = max_hits;
- ctx.num_hits = 0;
- IntersectContext rtc_ctx(&ctx);
- RTCRay rtc_ray;
- kernel_embree_setup_ray(*ray, rtc_ray, PATH_RAY_SHADOW);
- rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray);
- if (ctx.num_hits > max_hits) {
- return true;
- }
- *num_hits = ctx.num_hits;
- return rtc_ray.tfar == -INFINITY;
- }
- # endif
- # ifdef __OBJECT_MOTION__
- if (kernel_data.bvh.have_motion) {
- # ifdef __HAIR__
- if (kernel_data.bvh.have_curves) {
- return bvh_intersect_shadow_all_hair_motion(kg, ray, isect, visibility, max_hits, num_hits);
- }
- # endif /* __HAIR__ */
- return bvh_intersect_shadow_all_motion(kg, ray, isect, visibility, max_hits, num_hits);
- }
- # endif /* __OBJECT_MOTION__ */
- # ifdef __HAIR__
- if (kernel_data.bvh.have_curves) {
- return bvh_intersect_shadow_all_hair(kg, ray, isect, visibility, max_hits, num_hits);
- }
- # endif /* __HAIR__ */
- # ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing) {
- return bvh_intersect_shadow_all_instancing(kg, ray, isect, visibility, max_hits, num_hits);
- }
- # endif /* __INSTANCING__ */
- return bvh_intersect_shadow_all(kg, ray, isect, visibility, max_hits, num_hits);
- }
- #endif /* __SHADOW_RECORD_ALL__ */
- #ifdef __VOLUME__
- ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint visibility)
- {
- PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME);
- if (!scene_intersect_valid(ray)) {
- return false;
- }
- # ifdef __OBJECT_MOTION__
- if (kernel_data.bvh.have_motion) {
- return bvh_intersect_volume_motion(kg, ray, isect, visibility);
- }
- # endif /* __OBJECT_MOTION__ */
- # ifdef __KERNEL_CPU__
- # ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing)
- return bvh_intersect_volume_instancing(kg, ray, isect, visibility);
- # endif /* __INSTANCING__ */
- return bvh_intersect_volume(kg, ray, isect, visibility);
- # else /* __KERNEL_CPU__ */
- # ifdef __INSTANCING__
- return bvh_intersect_volume_instancing(kg, ray, isect, visibility);
- # else
- return bvh_intersect_volume(kg, ray, isect, visibility);
- # endif /* __INSTANCING__ */
- # endif /* __KERNEL_CPU__ */
- }
- #endif /* __VOLUME__ */
- #ifdef __VOLUME_RECORD_ALL__
- ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg,
- const Ray *ray,
- Intersection *isect,
- const uint max_hits,
- const uint visibility)
- {
- PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_ALL);
- if (!scene_intersect_valid(ray)) {
- return false;
- }
- # ifdef __EMBREE__
- if (kernel_data.bvh.scene) {
- CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_VOLUME_ALL);
- ctx.isect_s = isect;
- ctx.max_hits = max_hits;
- ctx.num_hits = 0;
- IntersectContext rtc_ctx(&ctx);
- RTCRay rtc_ray;
- kernel_embree_setup_ray(*ray, rtc_ray, visibility);
- rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray);
- return rtc_ray.tfar == -INFINITY;
- }
- # endif
- # ifdef __OBJECT_MOTION__
- if (kernel_data.bvh.have_motion) {
- return bvh_intersect_volume_all_motion(kg, ray, isect, max_hits, visibility);
- }
- # endif /* __OBJECT_MOTION__ */
- # ifdef __INSTANCING__
- if (kernel_data.bvh.have_instancing)
- return bvh_intersect_volume_all_instancing(kg, ray, isect, max_hits, visibility);
- # endif /* __INSTANCING__ */
- return bvh_intersect_volume_all(kg, ray, isect, max_hits, visibility);
- }
- #endif /* __VOLUME_RECORD_ALL__ */
- /* Ray offset to avoid self intersection.
- *
- * This function should be used to compute a modified ray start position for
- * rays leaving from a surface. */
- ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
- {
- #ifdef __INTERSECTION_REFINE__
- const float epsilon_f = 1e-5f;
- /* ideally this should match epsilon_f, but instancing and motion blur
- * precision makes it problematic */
- const float epsilon_test = 1.0f;
- const int epsilon_i = 32;
- float3 res;
- /* x component */
- if (fabsf(P.x) < epsilon_test) {
- res.x = P.x + Ng.x * epsilon_f;
- }
- else {
- uint ix = __float_as_uint(P.x);
- ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
- res.x = __uint_as_float(ix);
- }
- /* y component */
- if (fabsf(P.y) < epsilon_test) {
- res.y = P.y + Ng.y * epsilon_f;
- }
- else {
- uint iy = __float_as_uint(P.y);
- iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
- res.y = __uint_as_float(iy);
- }
- /* z component */
- if (fabsf(P.z) < epsilon_test) {
- res.z = P.z + Ng.z * epsilon_f;
- }
- else {
- uint iz = __float_as_uint(P.z);
- iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
- res.z = __uint_as_float(iz);
- }
- return res;
- #else
- const float epsilon_f = 1e-4f;
- return P + epsilon_f * Ng;
- #endif
- }
- #if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
- /* ToDo: Move to another file? */
- ccl_device int intersections_compare(const void *a, const void *b)
- {
- const Intersection *isect_a = (const Intersection *)a;
- const Intersection *isect_b = (const Intersection *)b;
- if (isect_a->t < isect_b->t)
- return -1;
- else if (isect_a->t > isect_b->t)
- return 1;
- else
- return 0;
- }
- #endif
- #if defined(__SHADOW_RECORD_ALL__)
- ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
- {
- # ifdef __KERNEL_GPU__
- /* Use bubble sort which has more friendly memory pattern on GPU. */
- bool swapped;
- do {
- swapped = false;
- for (int j = 0; j < num_hits - 1; ++j) {
- if (hits[j].t > hits[j + 1].t) {
- struct Intersection tmp = hits[j];
- hits[j] = hits[j + 1];
- hits[j + 1] = tmp;
- swapped = true;
- }
- }
- --num_hits;
- } while (swapped);
- # else
- qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
- # endif
- }
- #endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
- CCL_NAMESPACE_END
|