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/render
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/render')
-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
4 files changed, 163 insertions, 20 deletions
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