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:
-rw-r--r--intern/cycles/bvh/bvh_embree.cpp45
-rw-r--r--intern/cycles/device/cpu/kernel.cpp1
-rw-r--r--intern/cycles/device/cpu/kernel.h1
-rw-r--r--intern/cycles/device/device_kernel.cpp2
-rw-r--r--intern/cycles/integrator/shader_eval.cpp6
-rw-r--r--intern/cycles/integrator/shader_eval.h1
-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
-rw-r--r--intern/cycles/kernel/device/cpu/kernel_arch.h5
-rw-r--r--intern/cycles/kernel/device/cpu/kernel_arch_impl.h13
-rw-r--r--intern/cycles/kernel/device/gpu/kernel.h16
-rw-r--r--intern/cycles/kernel/device/optix/kernel.cu38
-rw-r--r--intern/cycles/kernel/geom/geom_shader_data.h75
-rw-r--r--intern/cycles/kernel/integrator/integrator_intersect_shadow.h17
-rw-r--r--intern/cycles/kernel/integrator/integrator_shade_shadow.h10
-rw-r--r--intern/cycles/kernel/integrator/integrator_state.h13
-rw-r--r--intern/cycles/kernel/kernel_bake.h21
-rw-r--r--intern/cycles/kernel/kernel_types.h11
-rw-r--r--intern/cycles/render/attribute.cpp5
-rw-r--r--intern/cycles/render/geometry.cpp59
-rw-r--r--intern/cycles/render/hair.cpp115
-rw-r--r--intern/cycles/render/hair.h4
24 files changed, 499 insertions, 109 deletions
diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/bvh_embree.cpp
index 343d62dedf4..cd19e009bf3 100644
--- a/intern/cycles/bvh/bvh_embree.cpp
+++ b/intern/cycles/bvh/bvh_embree.cpp
@@ -80,31 +80,49 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
Intersection current_isect;
kernel_embree_convert_hit(kg, ray, hit, &current_isect);
- /* If no transparent shadows, all light is blocked. */
+ /* If no transparent shadows or max number of hits exceeded, all light is blocked. */
const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type);
- if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->max_hits == 0) {
+ if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->num_hits >= ctx->max_hits) {
ctx->opaque_hit = true;
return;
}
+ ++ctx->num_hits;
+
+ /* Always use baked shadow transparency for curves. */
+ if (current_isect.type & PRIMITIVE_ALL_CURVE) {
+ ctx->throughput *= intersection_curve_shadow_transparency(
+ kg, current_isect.object, current_isect.prim, current_isect.u);
+
+ if (ctx->throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
+ ctx->opaque_hit = true;
+ return;
+ }
+ else {
+ *args->valid = 0;
+ return;
+ }
+ }
+
/* Test if we need to record this transparent intersection. */
- if (ctx->num_hits < ctx->max_hits || ray->tfar < ctx->max_t) {
+ const uint max_record_hits = min(ctx->max_hits, INTEGRATOR_SHADOW_ISECT_SIZE);
+ if (ctx->num_recorded_hits < max_record_hits || ray->tfar < ctx->max_t) {
/* If maximum number of hits was reached, replace the intersection with the
* highest distance. We want to find the N closest intersections. */
- const int num_recorded_hits = min(ctx->num_hits, ctx->max_hits);
- int isect_index = num_recorded_hits;
- if (num_recorded_hits + 1 >= ctx->max_hits) {
+ const uint num_recorded_hits = min(ctx->num_recorded_hits, max_record_hits);
+ uint isect_index = num_recorded_hits;
+ if (num_recorded_hits + 1 >= max_record_hits) {
float max_t = ctx->isect_s[0].t;
- int max_recorded_hit = 0;
+ uint max_recorded_hit = 0;
- for (int i = 1; i < num_recorded_hits; ++i) {
+ for (uint i = 1; i < num_recorded_hits; ++i) {
if (ctx->isect_s[i].t > max_t) {
max_recorded_hit = i;
max_t = ctx->isect_s[i].t;
}
}
- if (num_recorded_hits >= ctx->max_hits) {
+ if (num_recorded_hits >= max_record_hits) {
isect_index = max_recorded_hit;
}
@@ -118,10 +136,9 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
ctx->isect_s[isect_index] = current_isect;
}
- /* Always increase the number of hits, even beyond ray.max_hits so that
- * the caller can detect this as and consider it opaque, or trace another
- * ray. */
- ++ctx->num_hits;
+ /* Always increase the number of recorded hits, even beyond the maximum,
+ * so that we can detect this and trace another ray if needed. */
+ ++ctx->num_recorded_hits;
/* This tells Embree to continue tracing. */
*args->valid = 0;
@@ -160,7 +177,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args)
if (ctx->lcg_state) {
/* See triangle_intersect_subsurface() for the native equivalent. */
- for (int i = min(ctx->max_hits, local_isect->num_hits) - 1; i >= 0; --i) {
+ for (int i = min((int)ctx->max_hits, local_isect->num_hits) - 1; i >= 0; --i) {
if (local_isect->hits[i].t == ray->tfar) {
/* This tells Embree to continue tracing. */
*args->valid = 0;
diff --git a/intern/cycles/device/cpu/kernel.cpp b/intern/cycles/device/cpu/kernel.cpp
index 91282390e27..bbad2f3147d 100644
--- a/intern/cycles/device/cpu/kernel.cpp
+++ b/intern/cycles/device/cpu/kernel.cpp
@@ -44,6 +44,7 @@ CPUKernels::CPUKernels()
/* Shader evaluation. */
REGISTER_KERNEL(shader_eval_displace),
REGISTER_KERNEL(shader_eval_background),
+ REGISTER_KERNEL(shader_eval_curve_shadow_transparency),
/* Adaptive sampling. */
REGISTER_KERNEL(adaptive_sampling_convergence_check),
REGISTER_KERNEL(adaptive_sampling_filter_x),
diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h
index 2db09057e44..3787fe37a33 100644
--- a/intern/cycles/device/cpu/kernel.h
+++ b/intern/cycles/device/cpu/kernel.h
@@ -58,6 +58,7 @@ class CPUKernels {
ShaderEvalFunction shader_eval_displace;
ShaderEvalFunction shader_eval_background;
+ ShaderEvalFunction shader_eval_curve_shadow_transparency;
/* Adaptive stopping. */
diff --git a/intern/cycles/device/device_kernel.cpp b/intern/cycles/device/device_kernel.cpp
index ceaddee4756..e0833331b77 100644
--- a/intern/cycles/device/device_kernel.cpp
+++ b/intern/cycles/device/device_kernel.cpp
@@ -74,6 +74,8 @@ const char *device_kernel_as_string(DeviceKernel kernel)
return "shader_eval_displace";
case DEVICE_KERNEL_SHADER_EVAL_BACKGROUND:
return "shader_eval_background";
+ case DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY:
+ return "shader_eval_curve_shadow_transparency";
/* Film. */
diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp
index cfc30056f7d..3de7bb6fd16 100644
--- a/intern/cycles/integrator/shader_eval.cpp
+++ b/intern/cycles/integrator/shader_eval.cpp
@@ -122,6 +122,9 @@ bool ShaderEval::eval_cpu(Device *device,
case SHADER_EVAL_BACKGROUND:
kernels.shader_eval_background(kg, input_data, output_data, work_index);
break;
+ case SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY:
+ kernels.shader_eval_curve_shadow_transparency(kg, input_data, output_data, work_index);
+ break;
}
});
});
@@ -144,6 +147,9 @@ bool ShaderEval::eval_gpu(Device *device,
case SHADER_EVAL_BACKGROUND:
kernel = DEVICE_KERNEL_SHADER_EVAL_BACKGROUND;
break;
+ case SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY:
+ kernel = DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY;
+ break;
};
/* Create device queue. */
diff --git a/intern/cycles/integrator/shader_eval.h b/intern/cycles/integrator/shader_eval.h
index 013fad17d4f..43b6b1bdd47 100644
--- a/intern/cycles/integrator/shader_eval.h
+++ b/intern/cycles/integrator/shader_eval.h
@@ -30,6 +30,7 @@ class Progress;
enum ShaderEvalType {
SHADER_EVAL_DISPLACE,
SHADER_EVAL_BACKGROUND,
+ SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY,
};
/* ShaderEval class performs shader evaluation for background light and displacement. */
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
diff --git a/intern/cycles/kernel/device/cpu/kernel_arch.h b/intern/cycles/kernel/device/cpu/kernel_arch.h
index ae7fab65100..7a438b58e73 100644
--- a/intern/cycles/kernel/device/cpu/kernel_arch.h
+++ b/intern/cycles/kernel/device/cpu/kernel_arch.h
@@ -64,6 +64,11 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobalsCPU *kg,
const KernelShaderEvalInput *input,
float *output,
const int offset);
+void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)(
+ const KernelGlobalsCPU *kg,
+ const KernelShaderEvalInput *input,
+ float *output,
+ const int offset);
/* --------------------------------------------------------------------
* Adaptive sampling.
diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h
index 2b0eea4fb61..ac606c768db 100644
--- a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h
+++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h
@@ -150,6 +150,19 @@ void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobalsCPU *k
#endif
}
+void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)(
+ const KernelGlobalsCPU *kg,
+ const KernelShaderEvalInput *input,
+ float *output,
+ const int offset)
+{
+#ifdef KERNEL_STUB
+ STUB_ASSERT(KERNEL_ARCH, shader_eval_curve_shadow_transparency);
+#else
+ kernel_curve_shadow_transparency_evaluate(kg, input, output, offset);
+#endif
+}
+
/* --------------------------------------------------------------------
* Adaptive sampling.
*/
diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h
index 6b4d79ed5b7..b6df74e835a 100644
--- a/intern/cycles/kernel/device/gpu/kernel.h
+++ b/intern/cycles/kernel/device/gpu/kernel.h
@@ -621,7 +621,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
}
}
-/* Background Shader Evaluation */
+/* Background */
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
kernel_gpu_shader_eval_background(KernelShaderEvalInput *input,
@@ -635,6 +635,20 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
}
}
+/* Curve Shadow Transparency */
+
+ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
+ kernel_gpu_shader_eval_curve_shadow_transparency(KernelShaderEvalInput *input,
+ float *output,
+ const int offset,
+ const int work_size)
+{
+ int i = ccl_gpu_global_id_x();
+ if (i < work_size) {
+ kernel_curve_shadow_transparency_evaluate(NULL, input, output, offset + i);
+ }
+}
+
/* --------------------------------------------------------------------
* Denoising.
*/
diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu
index 574f66ab708..a3bafb9846c 100644
--- a/intern/cycles/kernel/device/optix/kernel.cu
+++ b/intern/cycles/kernel/device/optix/kernel.cu
@@ -210,29 +210,50 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
optixSetPayload_5(true);
return optixTerminateRay();
# else
- const int max_hits = optixGetPayload_3();
+ const uint max_hits = optixGetPayload_3();
+ const uint num_hits_packed = optixGetPayload_2();
+ const uint num_recorded_hits = uint16_unpack_from_uint_0(num_hits_packed);
+ const uint num_hits = uint16_unpack_from_uint_1(num_hits_packed);
/* If no transparent shadows, all light is blocked and we can stop immediately. */
- if (max_hits == 0 ||
+ if (num_hits >= max_hits ||
!(intersection_get_shader_flags(NULL, prim, type) & SD_HAS_TRANSPARENT_SHADOW)) {
optixSetPayload_5(true);
return optixTerminateRay();
}
+ /* Always use baked shadow transparency for curves. */
+ if (type & PRIMITIVE_ALL_CURVE) {
+ float throughput = __uint_as_float(optixGetPayload_1());
+ throughput *= intersection_curve_shadow_transparency(nullptr, object, prim, u);
+ optixSetPayload_1(__float_as_uint(throughput));
+ optixSetPayload_2(uint16_pack_to_uint(num_recorded_hits, num_hits + 1));
+
+ if (throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) {
+ optixSetPayload_4(true);
+ return optixTerminateRay();
+ }
+ else {
+ /* Continue tracing. */
+ optixIgnoreIntersection();
+ return;
+ }
+ }
+
/* Record transparent intersection. */
- const int num_hits = optixGetPayload_2();
- int record_index = num_hits;
+ optixSetPayload_2(uint16_pack_to_uint(num_recorded_hits + 1, num_hits + 1));
- optixSetPayload_2(num_hits + 1);
+ uint record_index = num_recorded_hits;
const IntegratorShadowState state = optixGetPayload_0();
- if (record_index >= max_hits) {
+ const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE);
+ if (record_index >= max_record_hits) {
/* If maximum number of hits reached, find a hit to replace. */
float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t);
- int max_recorded_hit = 0;
+ uint max_recorded_hit = 0;
- for (int i = 1; i < max_hits; i++) {
+ for (int i = 1; i < max_record_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;
@@ -256,6 +277,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, object) = object;
INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, type) = type;
+ /* Continue tracing. */
optixIgnoreIntersection();
# endif /* __TRANSPARENT_SHADOWS__ */
#endif /* __SHADOW_RECORD_ALL__ */
diff --git a/intern/cycles/kernel/geom/geom_shader_data.h b/intern/cycles/kernel/geom/geom_shader_data.h
index e6a5b8f7923..46bda2b656c 100644
--- a/intern/cycles/kernel/geom/geom_shader_data.h
+++ b/intern/cycles/kernel/geom/geom_shader_data.h
@@ -279,6 +279,81 @@ ccl_device void shader_setup_from_displace(KernelGlobals kg,
LAMP_NONE);
}
+/* ShaderData setup for point on curve. */
+
+ccl_device void shader_setup_from_curve(KernelGlobals kg,
+ ccl_private ShaderData *ccl_restrict sd,
+ int object,
+ int prim,
+ int segment,
+ float u)
+{
+ /* Primitive */
+ sd->type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE_THICK, segment);
+ sd->lamp = LAMP_NONE;
+ sd->prim = prim;
+ sd->u = u;
+ sd->v = 0.0f;
+ sd->time = 0.5f;
+ sd->ray_length = 0.0f;
+
+ /* Shader */
+ sd->shader = kernel_tex_fetch(__curves, prim).shader_id;
+ sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags;
+
+ /* Object */
+ sd->object = object;
+ sd->object_flag = kernel_tex_fetch(__object_flag, sd->object);
+#ifdef __OBJECT_MOTION__
+ shader_setup_object_transforms(kg, sd, sd->time);
+#endif
+
+ /* Get control points. */
+ KernelCurve kcurve = kernel_tex_fetch(__curves, prim);
+
+ int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
+ int k1 = k0 + 1;
+ int ka = max(k0 - 1, kcurve.first_key);
+ int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
+
+ float4 P_curve[4];
+
+ P_curve[0] = kernel_tex_fetch(__curve_keys, ka);
+ P_curve[1] = kernel_tex_fetch(__curve_keys, k0);
+ P_curve[2] = kernel_tex_fetch(__curve_keys, k1);
+ P_curve[3] = kernel_tex_fetch(__curve_keys, kb);
+
+ /* Interpolate position and tangent. */
+ sd->P = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u));
+#ifdef __DPDU__
+ sd->dPdu = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u));
+#endif
+
+ /* Transform into world space */
+ if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
+ object_position_transform_auto(kg, sd, &sd->P);
+#ifdef __DPDU__
+ object_dir_transform_auto(kg, sd, &sd->dPdu);
+#endif
+ }
+
+ /* No view direction, normals or bitangent. */
+ sd->I = zero_float3();
+ sd->N = zero_float3();
+ sd->Ng = zero_float3();
+#ifdef __DPDU__
+ sd->dPdv = zero_float3();
+#endif
+
+ /* No ray differentials currently. */
+#ifdef __RAY_DIFFERENTIALS__
+ sd->dP = differential3_zero();
+ sd->dI = differential3_zero();
+ sd->du = differential_zero();
+ sd->dv = differential_zero();
+#endif
+}
+
/* ShaderData setup from ray into background */
ccl_device_inline void shader_setup_from_background(KernelGlobals kg,
diff --git a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h
index d5c6ec145f0..90422445fad 100644
--- a/intern/cycles/kernel/integrator/integrator_intersect_shadow.h
+++ b/intern/cycles/kernel/integrator/integrator_intersect_shadow.h
@@ -115,18 +115,25 @@ ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg,
{
/* Limit the number hits to the max transparent bounces allowed and the size that we
* have available in the integrator state. */
- const uint max_transparent_hits = integrate_shadow_max_transparent_hits(kg, state);
- const uint max_hits = min(max_transparent_hits, (uint)INTEGRATOR_SHADOW_ISECT_SIZE);
+ const uint max_hits = integrate_shadow_max_transparent_hits(kg, state);
uint num_hits = 0;
- bool opaque_hit = scene_intersect_shadow_all(kg, state, ray, visibility, max_hits, &num_hits);
+ float throughput = 1.0f;
+ bool opaque_hit = scene_intersect_shadow_all(
+ kg, state, ray, visibility, max_hits, &num_hits, &throughput);
+
+ /* Computed throughput from baked shadow transparency, where we can bypass recording
+ * intersections and shader evaluation. */
+ if (throughput != 1.0f) {
+ INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) *= throughput;
+ }
/* If number of hits exceed the transparent bounces limit, make opaque. */
- if (num_hits > max_transparent_hits) {
+ if (num_hits > max_hits) {
opaque_hit = true;
}
if (!opaque_hit) {
- uint num_recorded_hits = min(num_hits, max_hits);
+ const uint num_recorded_hits = min(num_hits, min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE));
if (num_recorded_hits > 0) {
sort_shadow_intersections(state, num_recorded_hits);
diff --git a/intern/cycles/kernel/integrator/integrator_shade_shadow.h b/intern/cycles/kernel/integrator/integrator_shade_shadow.h
index 94900754b76..2d056a0b76f 100644
--- a/intern/cycles/kernel/integrator/integrator_shade_shadow.h
+++ b/intern/cycles/kernel/integrator/integrator_shade_shadow.h
@@ -23,7 +23,7 @@
CCL_NAMESPACE_BEGIN
-ccl_device_inline bool shadow_intersections_has_remaining(const int num_hits)
+ccl_device_inline bool shadow_intersections_has_remaining(const uint num_hits)
{
return num_hits >= INTEGRATOR_SHADOW_ISECT_SIZE;
}
@@ -105,12 +105,12 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg,
ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg,
IntegratorShadowState state,
- const int num_hits)
+ const uint num_hits)
{
/* Accumulate shadow for transparent surfaces. */
- const int num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE);
+ const uint num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE);
- for (int hit = 0; hit < num_recorded_hits + 1; hit++) {
+ for (uint hit = 0; hit < num_recorded_hits + 1; hit++) {
/* Volume shaders. */
if (hit < num_recorded_hits || !shadow_intersections_has_remaining(num_hits)) {
# ifdef __VOLUME__
@@ -162,7 +162,7 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg,
ccl_global float *ccl_restrict render_buffer)
{
PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SETUP);
- const int num_hits = INTEGRATOR_STATE(state, shadow_path, num_hits);
+ const uint num_hits = INTEGRATOR_STATE(state, shadow_path, num_hits);
#ifdef __TRANSPARENT_SHADOWS__
/* Evaluate transparent shadows. */
diff --git a/intern/cycles/kernel/integrator/integrator_state.h b/intern/cycles/kernel/integrator/integrator_state.h
index 84f34c6b986..4f21ab35d1f 100644
--- a/intern/cycles/kernel/integrator/integrator_state.h
+++ b/intern/cycles/kernel/integrator/integrator_state.h
@@ -48,19 +48,6 @@
CCL_NAMESPACE_BEGIN
-/* Constants
- *
- * TODO: these could be made dynamic depending on the features used in the scene. */
-
-#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024
-#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4
-
-#ifdef __KERNEL_CPU__
-# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU
-#else
-# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_GPU
-#endif
-
/* Data structures */
/* Integrator State
diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h
index 1473e9ba8bf..30a41b9d3e3 100644
--- a/intern/cycles/kernel/kernel_bake.h
+++ b/intern/cycles/kernel/kernel_bake.h
@@ -96,4 +96,25 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg,
output[offset * 3 + 2] += color.z;
}
+ccl_device void kernel_curve_shadow_transparency_evaluate(
+ KernelGlobals kg,
+ ccl_global const KernelShaderEvalInput *input,
+ ccl_global float *output,
+ const int offset)
+{
+ /* Setup shader data. */
+ const KernelShaderEvalInput in = input[offset];
+
+ ShaderData sd;
+ shader_setup_from_curve(kg, &sd, in.object, in.prim, __float_as_int(in.v), in.u);
+
+ /* Evaluate transparency. */
+ shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW &
+ ~(KERNEL_FEATURE_NODE_RAYTRACE | KERNEL_FEATURE_NODE_LIGHT_PATH)>(
+ kg, INTEGRATOR_STATE_NULL, &sd, NULL, PATH_RAY_SHADOW);
+
+ /* Write output. */
+ output[offset] = clamp(average(shader_bsdf_transparency(kg, &sd)), 0.0f, 1.0f);
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index edae158f403..fa8453b99cb 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -61,6 +61,15 @@ CCL_NAMESPACE_BEGIN
#define ID_NONE (0.0f)
#define PASS_UNUSED (~0)
+#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024U
+#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4U
+
+#ifdef __KERNEL_CPU__
+# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU
+#else
+# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_GPU
+#endif
+
/* Kernel features */
#define __SOBOL__
#define __DPDU__
@@ -582,6 +591,7 @@ typedef enum AttributeStandard {
ATTR_STD_VOLUME_VELOCITY,
ATTR_STD_POINTINESS,
ATTR_STD_RANDOM_PER_ISLAND,
+ ATTR_STD_SHADOW_TRANSPARENCY,
ATTR_STD_NUM,
ATTR_STD_NOT_FOUND = ~0
@@ -1452,6 +1462,7 @@ typedef enum DeviceKernel {
DEVICE_KERNEL_SHADER_EVAL_DISPLACE,
DEVICE_KERNEL_SHADER_EVAL_BACKGROUND,
+ DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY,
#define DECLARE_FILM_CONVERT_KERNEL(variant) \
DEVICE_KERNEL_FILM_CONVERT_##variant, DEVICE_KERNEL_FILM_CONVERT_##variant##_HALF_RGBA
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index aaf21ad9fd2..d7e6939cd80 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -366,6 +366,8 @@ const char *Attribute::standard_name(AttributeStandard std)
return "pointiness";
case ATTR_STD_RANDOM_PER_ISLAND:
return "random_per_island";
+ case ATTR_STD_SHADOW_TRANSPARENCY:
+ return "shadow_transparency";
case ATTR_STD_NOT_FOUND:
case ATTR_STD_NONE:
case ATTR_STD_NUM:
@@ -603,6 +605,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
case ATTR_STD_RANDOM_PER_ISLAND:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE);
break;
+ case ATTR_STD_SHADOW_TRANSPARENCY:
+ attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY);
+ break;
default:
assert(0);
break;
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 5d89060c1a1..5cedab24ceb 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -734,6 +734,10 @@ void GeometryManager::device_update_attributes(Device *device,
Shader *shader = static_cast<Shader *>(node);
geom_attributes[i].add(shader->attributes);
}
+
+ if (geom->is_hair() && static_cast<Hair *>(geom)->need_shadow_transparency()) {
+ geom_attributes[i].add(ATTR_STD_SHADOW_TRANSPARENCY);
+ }
}
/* convert object attributes to use the same data structures as geometry ones */
@@ -1659,6 +1663,7 @@ void GeometryManager::device_update(Device *device,
VLOG(1) << "Total " << scene->geometry.size() << " meshes.";
bool true_displacement_used = false;
+ bool curve_shadow_transparency_used = false;
size_t total_tess_needed = 0;
{
@@ -1669,26 +1674,33 @@ void GeometryManager::device_update(Device *device,
});
foreach (Geometry *geom, scene->geometry) {
- if (geom->is_modified() &&
- (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) {
- Mesh *mesh = static_cast<Mesh *>(geom);
+ if (geom->is_modified()) {
+ if ((geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
- /* Update normals. */
- mesh->add_face_normals();
- mesh->add_vertex_normals();
+ /* Update normals. */
+ mesh->add_face_normals();
+ mesh->add_vertex_normals();
- if (mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) {
- mesh->add_undisplaced();
- }
+ if (mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) {
+ mesh->add_undisplaced();
+ }
- /* Test if we need tessellation. */
- if (mesh->need_tesselation()) {
- total_tess_needed++;
- }
+ /* Test if we need tessellation. */
+ if (mesh->need_tesselation()) {
+ total_tess_needed++;
+ }
- /* Test if we need displacement. */
- if (mesh->has_true_displacement()) {
- true_displacement_used = true;
+ /* Test if we need displacement. */
+ if (mesh->has_true_displacement()) {
+ true_displacement_used = true;
+ }
+ }
+ else if (geom->geometry_type == Geometry::HAIR) {
+ Hair *hair = static_cast<Hair *>(geom);
+ if (hair->need_shadow_transparency()) {
+ curve_shadow_transparency_used = true;
+ }
}
if (progress.get_cancel()) {
@@ -1752,7 +1764,7 @@ void GeometryManager::device_update(Device *device,
/* Update images needed for true displacement. */
bool old_need_object_flags_update = false;
- if (true_displacement_used) {
+ if (true_displacement_used || curve_shadow_transparency_used) {
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
scene->update_stats->geometry.times.add_entry(
@@ -1770,7 +1782,7 @@ void GeometryManager::device_update(Device *device,
const BVHLayout bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
device->get_bvh_layout_mask());
mesh_calc_offset(scene, bvh_layout);
- if (true_displacement_used) {
+ if (true_displacement_used || curve_shadow_transparency_used) {
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
scene->update_stats->geometry.times.add_entry(
@@ -1795,8 +1807,9 @@ void GeometryManager::device_update(Device *device,
}
}
- /* Update displacement. */
+ /* Update displacement and hair shadow transparency. */
bool displacement_done = false;
+ bool curve_shadow_transparency_done = false;
size_t num_bvh = 0;
{
@@ -1817,6 +1830,12 @@ void GeometryManager::device_update(Device *device,
displacement_done = true;
}
}
+ else if (geom->geometry_type == Geometry::HAIR) {
+ Hair *hair = static_cast<Hair *>(geom);
+ if (hair->update_shadow_transparency(device, scene, progress)) {
+ curve_shadow_transparency_done = true;
+ }
+ }
}
if (geom->is_modified() || geom->need_update_bvh_for_offset) {
@@ -1836,7 +1855,7 @@ void GeometryManager::device_update(Device *device,
}
/* Device re-update after displacement. */
- if (displacement_done) {
+ if (displacement_done || curve_shadow_transparency_done) {
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
scene->update_stats->geometry.times.add_entry(
diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp
index e757e3fd3e0..4656148119a 100644
--- a/intern/cycles/render/hair.cpp
+++ b/intern/cycles/render/hair.cpp
@@ -18,8 +18,13 @@
#include "render/curves.h"
#include "render/hair.h"
+#include "render/object.h"
#include "render/scene.h"
+#include "integrator/shader_eval.h"
+
+#include "util/util_progress.h"
+
CCL_NAMESPACE_BEGIN
/* Hair Curve */
@@ -514,4 +519,114 @@ PrimitiveType Hair::primitive_type() const
((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
}
+/* Fill in coordinates for curve transparency shader evaluation on device. */
+static int fill_shader_input(const Hair *hair,
+ const int object_index,
+ device_vector<KernelShaderEvalInput> &d_input)
+{
+ int d_input_size = 0;
+ KernelShaderEvalInput *d_input_data = d_input.data();
+
+ const int num_curves = hair->num_curves();
+ for (int i = 0; i < num_curves; i++) {
+ const Hair::Curve curve = hair->get_curve(i);
+ const int num_segments = curve.num_segments();
+
+ for (int j = 0; j < num_segments + 1; j++) {
+ KernelShaderEvalInput in;
+ in.object = object_index;
+ in.prim = hair->prim_offset + i;
+ in.u = (j < num_segments) ? 0.0f : 1.0f;
+ in.v = (j < num_segments) ? __int_as_float(j) : __int_as_float(j - 1);
+ d_input_data[d_input_size++] = in;
+ }
+ }
+
+ return d_input_size;
+}
+
+/* Read back curve transparency shader output. */
+static void read_shader_output(float *shadow_transparency,
+ bool &is_fully_opaque,
+ const device_vector<float> &d_output)
+{
+ const int num_keys = d_output.size();
+ const float *output_data = d_output.data();
+ bool is_opaque = true;
+
+ for (int i = 0; i < num_keys; i++) {
+ shadow_transparency[i] = output_data[i];
+ if (shadow_transparency[i] > 0.0f) {
+ is_opaque = false;
+ }
+ }
+
+ is_fully_opaque = is_opaque;
+}
+
+bool Hair::need_shadow_transparency()
+{
+ for (const Node *node : used_shaders) {
+ const Shader *shader = static_cast<const Shader *>(node);
+ if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Hair::update_shadow_transparency(Device *device, Scene *scene, Progress &progress)
+{
+ if (!need_shadow_transparency()) {
+ /* If no shaders with shadow transparency, remove attribute. */
+ Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY);
+ if (attr) {
+ attributes.remove(attr);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ string msg = string_printf("Computing Shadow Transparency %s", name.c_str());
+ progress.set_status("Updating Hair", msg);
+
+ /* Create shadow transparency attribute. */
+ Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY);
+ const bool attribute_exists = (attr != nullptr);
+ if (!attribute_exists) {
+ attr = attributes.add(ATTR_STD_SHADOW_TRANSPARENCY);
+ }
+
+ float *attr_data = attr->data_float();
+
+ /* Find object index. */
+ size_t object_index = OBJECT_NONE;
+
+ for (size_t i = 0; i < scene->objects.size(); i++) {
+ if (scene->objects[i]->get_geometry() == this) {
+ object_index = i;
+ break;
+ }
+ }
+
+ /* Evaluate shader on device. */
+ ShaderEval shader_eval(device, progress);
+ bool is_fully_opaque = false;
+ shader_eval.eval(SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY,
+ num_keys(),
+ 1,
+ function_bind(&fill_shader_input, this, object_index, _1),
+ function_bind(&read_shader_output, attr_data, is_fully_opaque, _1));
+
+ if (is_fully_opaque) {
+ attributes.remove(attr);
+ return attribute_exists;
+ }
+
+ return true;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h
index 920e9601b35..3e91fc3dcbb 100644
--- a/intern/cycles/render/hair.h
+++ b/intern/cycles/render/hair.h
@@ -153,6 +153,10 @@ class Hair : public Geometry {
KernelCurveSegment *curve_segments);
PrimitiveType primitive_type() const override;
+
+ /* Attributes */
+ bool need_shadow_transparency();
+ bool update_shadow_transparency(Device *device, Scene *scene, Progress &progress);
};
CCL_NAMESPACE_END