Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brecht@blender.org>2021-09-20 17:16:11 +0300
committerBrecht Van Lommel <brecht@blender.org>2021-10-19 16:11:09 +0300
commitfd77a28031daff3122ded3a1cb37a7fb44feedf6 (patch)
treef54967b7f5f1175555aa21d613137fe436d7fc8c /intern/cycles/kernel/bvh
parentd06828f0b8ebb083de59fd2cb8c5f8fe6af1da22 (diff)
Cycles: bake transparent shadows for hair
These transparent shadows can be expansive to evaluate. Especially on the GPU they can lead to poor occupancy when only some pixels require many kernel launches to trace and evaluate many layers of transparency. Baked transparency allows tracing a single ray in many cases by accumulating the throughput directly in the intersection program without recording hits or evaluating shaders. Transparency is baked at curve vertices and interpolated, for most shaders this will look practically the same as actual shader evaluation. Fixes T91428, performance regression with spring demo file due to transparent hair, and makes it render significantly faster than Blender 2.93. Differential Revision: https://developer.blender.org/D12880
Diffstat (limited to 'intern/cycles/kernel/bvh')
-rw-r--r--intern/cycles/kernel/bvh/bvh.h29
-rw-r--r--intern/cycles/kernel/bvh/bvh_embree.h8
-rw-r--r--intern/cycles/kernel/bvh/bvh_shadow_all.h85
-rw-r--r--intern/cycles/kernel/bvh/bvh_util.h28
4 files changed, 107 insertions, 43 deletions
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index 0d9ba7e6369..813ac15711e 100644
--- a/intern/cycles/kernel/bvh/bvh.h
+++ b/intern/cycles/kernel/bvh/bvh.h
@@ -367,12 +367,13 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
ccl_private const Ray *ray,
uint visibility,
uint max_hits,
- ccl_private uint *num_hits)
+ ccl_private uint *num_recorded_hits,
+ ccl_private float *throughput)
{
# ifdef __KERNEL_OPTIX__
uint p0 = state;
- uint p1 = 0; /* Unused */
- uint p2 = 0; /* Number of hits. */
+ uint p1 = __float_as_uint(1.0f); /* Throughput. */
+ uint p2 = 0; /* Number of hits. */
uint p3 = max_hits;
uint p4 = visibility;
uint p5 = false;
@@ -382,7 +383,6 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
ray_mask = 0xFF;
}
- *num_hits = 0; /* Initialize hit count to zero. */
optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0,
ray->P,
ray->D,
@@ -402,12 +402,14 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
p4,
p5);
- *num_hits = p2;
+ *num_recorded_hits = uint16_unpack_from_uint_0(p2);
+ *throughput = __uint_as_float(p1);
return p5;
# else /* __KERNEL_OPTIX__ */
if (!scene_intersect_valid(ray)) {
- *num_hits = 0;
+ *num_recorded_hits = 0;
+ *throughput = 1.0f;
return false;
}
@@ -422,7 +424,8 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
kernel_embree_setup_ray(*ray, rtc_ray, visibility);
rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray);
- *num_hits = ctx.num_hits;
+ *num_recorded_hits = ctx.num_recorded_hits;
+ *throughput = ctx.throughput;
return ctx.opaque_hit;
}
# endif /* __EMBREE__ */
@@ -431,21 +434,25 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
if (kernel_data.bvh.have_motion) {
# ifdef __HAIR__
if (kernel_data.bvh.have_curves) {
- return bvh_intersect_shadow_all_hair_motion(kg, ray, state, visibility, max_hits, num_hits);
+ return bvh_intersect_shadow_all_hair_motion(
+ kg, ray, state, visibility, max_hits, num_recorded_hits, throughput);
}
# endif /* __HAIR__ */
- return bvh_intersect_shadow_all_motion(kg, ray, state, visibility, max_hits, num_hits);
+ return bvh_intersect_shadow_all_motion(
+ kg, ray, state, visibility, max_hits, num_recorded_hits, throughput);
}
# endif /* __OBJECT_MOTION__ */
# ifdef __HAIR__
if (kernel_data.bvh.have_curves) {
- return bvh_intersect_shadow_all_hair(kg, ray, state, visibility, max_hits, num_hits);
+ return bvh_intersect_shadow_all_hair(
+ kg, ray, state, visibility, max_hits, num_recorded_hits, throughput);
}
# endif /* __HAIR__ */
- return bvh_intersect_shadow_all(kg, ray, state, visibility, max_hits, num_hits);
+ return bvh_intersect_shadow_all(
+ kg, ray, state, visibility, max_hits, num_recorded_hits, throughput);
# endif /* __KERNEL_OPTIX__ */
}
#endif /* __SHADOW_RECORD_ALL__ */
diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/bvh_embree.h
index 4f85e8bee4b..321e0f28dae 100644
--- a/intern/cycles/kernel/bvh/bvh_embree.h
+++ b/intern/cycles/kernel/bvh/bvh_embree.h
@@ -40,8 +40,10 @@ struct CCLIntersectContext {
/* for shadow rays */
Intersection *isect_s;
- int max_hits;
- int num_hits;
+ uint max_hits;
+ uint num_hits;
+ uint num_recorded_hits;
+ float throughput;
float max_t;
bool opaque_hit;
@@ -56,6 +58,8 @@ struct CCLIntersectContext {
type = type_;
max_hits = 1;
num_hits = 0;
+ num_recorded_hits = 0;
+ throughput = 1.0f;
max_t = FLT_MAX;
opaque_hit = false;
isect_s = NULL;
diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h
index b997235b6e4..049c6a03fe0 100644
--- a/intern/cycles/kernel/bvh/bvh_shadow_all.h
+++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h
@@ -41,7 +41,8 @@ ccl_device_inline
IntegratorShadowState state,
const uint visibility,
const uint max_hits,
- ccl_private uint *num_hits)
+ ccl_private uint *num_recorded_hits,
+ ccl_private float *throughput)
{
/* todo:
* - likely and unlikely for if() statements
@@ -61,6 +62,7 @@ ccl_device_inline
float3 dir = bvh_clamp_direction(ray->D);
float3 idir = bvh_inverse_direction(dir);
int object = OBJECT_NONE;
+ uint num_hits = 0;
#if BVH_FEATURE(BVH_MOTION)
Transform ob_itfm;
@@ -77,7 +79,8 @@ ccl_device_inline
* otherwise. */
float t_world_to_instance = 1.0f;
- *num_hits = 0;
+ *num_recorded_hits = 0;
+ *throughput = 1.0f;
/* traversal loop */
do {
@@ -212,42 +215,62 @@ ccl_device_inline
* the primitive has a transparent shadow shader? */
const int flags = intersection_get_shader_flags(kg, isect.prim, isect.type);
- if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || max_hits == 0) {
+ if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || num_hits >= max_hits) {
/* If no transparent shadows, all light is blocked and we can
* stop immediately. */
return true;
}
- /* Increase the number of hits, possibly beyond max_hits, we will
- * simply not record those and only keep the max_hits closest. */
- uint record_index = (*num_hits)++;
-
- if (record_index >= max_hits - 1) {
- /* If maximum number of hits reached, find the intersection with
- * the largest distance to potentially replace when another hit
- * is found. */
- const int num_recorded_hits = min(max_hits, record_index);
- float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t);
- int max_recorded_hit = 0;
-
- for (int i = 1; i < num_recorded_hits; i++) {
- const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t);
- if (isect_t > max_recorded_t) {
- max_recorded_t = isect_t;
- max_recorded_hit = i;
- }
+ num_hits++;
+
+ bool record_intersection = true;
+
+ /* Always use baked shadow transparency for curves. */
+ if (isect.type & PRIMITIVE_ALL_CURVE) {
+ *throughput *= intersection_curve_shadow_transparency(
+ kg, isect.object, isect.prim, isect.u);
+
+ if (*throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
+ return true;
+ }
+ else {
+ record_intersection = false;
}
+ }
- if (record_index >= max_hits) {
- record_index = max_recorded_hit;
+ if (record_intersection) {
+ /* Increase the number of hits, possibly beyond max_hits, we will
+ * simply not record those and only keep the max_hits closest. */
+ uint record_index = (*num_recorded_hits)++;
+
+ const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE);
+ if (record_index >= max_record_hits - 1) {
+ /* If maximum number of hits reached, find the intersection with
+ * the largest distance to potentially replace when another hit
+ * is found. */
+ const int num_recorded_hits = min(max_record_hits, record_index);
+ float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t);
+ int max_recorded_hit = 0;
+
+ for (int i = 1; i < num_recorded_hits; i++) {
+ const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t);
+ if (isect_t > max_recorded_t) {
+ max_recorded_t = isect_t;
+ max_recorded_hit = i;
+ }
+ }
+
+ if (record_index >= max_record_hits) {
+ record_index = max_recorded_hit;
+ }
+
+ /* Limit the ray distance and stop counting hits beyond this. */
+ t_max_world = max(max_recorded_t, isect.t);
+ t_max_current = t_max_world * t_world_to_instance;
}
- /* Limit the ray distance and stop counting hits beyond this. */
- t_max_world = max(max_recorded_t, isect.t);
- t_max_current = t_max_world * t_world_to_instance;
+ integrator_state_write_shadow_isect(state, &isect, record_index);
}
-
- integrator_state_write_shadow_isect(state, &isect, record_index);
}
prim_addr++;
@@ -304,9 +327,11 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg,
IntegratorShadowState state,
const uint visibility,
const uint max_hits,
- ccl_private uint *num_hits)
+ ccl_private uint *num_recorded_hits,
+ ccl_private float *throughput)
{
- return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, state, visibility, max_hits, num_hits);
+ return BVH_FUNCTION_FULL_NAME(BVH)(
+ kg, ray, state, visibility, max_hits, num_recorded_hits, throughput);
}
#undef BVH_FUNCTION_NAME
diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h
index 869311b38e2..309f0eeb1e2 100644
--- a/intern/cycles/kernel/bvh/bvh_util.h
+++ b/intern/cycles/kernel/bvh/bvh_util.h
@@ -195,4 +195,32 @@ ccl_device_inline int intersection_find_attribute(KernelGlobals kg,
return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z;
}
+/* Transparent Shadows */
+
+/* Cut-off value to stop transparent shadow tracing when practically opaque. */
+#define CURVE_SHADOW_TRANSPARENCY_CUTOFF 0.001f
+
+ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg,
+ const int object,
+ const int prim,
+ const float u)
+{
+ /* Find attribute. */
+ const int offset = intersection_find_attribute(kg, object, ATTR_STD_SHADOW_TRANSPARENCY);
+ if (offset == ATTR_STD_NOT_FOUND) {
+ /* If no shadow transparency attribute, assume opaque. */
+ return 0.0f;
+ }
+
+ /* Interpolate transparency between curve keys. */
+ const KernelCurve kcurve = kernel_tex_fetch(__curves, prim);
+ const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(kcurve.type);
+ const int k1 = k0 + 1;
+
+ const float f0 = kernel_tex_fetch(__attributes_float, offset + k0);
+ const float f1 = kernel_tex_fetch(__attributes_float, offset + k1);
+
+ return (1.0f - u) * f0 + u * f1;
+}
+
CCL_NAMESPACE_END