diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 3b649be9f1c..a458aa2993e 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -657,7 +657,7 @@ inline bool get_object_attribute_impl(const ThreadKernelGlobalsCPU *kg, T dy = make_zero(); #ifdef __VOLUME__ if (primitive_is_volume_attribute(sd)) { - v = primitive_volume_attribute(kg, sd, desc, false); + v = primitive_volume_attribute(kg, sd, desc, true); } else #endif @@ -1345,11 +1345,10 @@ bool OSLRenderServices::texture3d(OSLUStringHash filename, switch (texture_type) { case OSLTextureHandle::SVM: { /* Packed texture. */ - ShaderData *sd = globals->sd; const int slot = handle->svm_slots[0].y; const float3 P_float3 = make_float3(P.x, P.y, P.z); float4 rgba = kernel_tex_image_interp_3d( - kernel_globals, slot, P_float3, INTERPOLATION_NONE, lcg_step_float(&sd->lcg_state)); + kernel_globals, slot, P_float3, INTERPOLATION_NONE, -1.0f); result[0] = rgba[0]; if (nchannels > 1) { diff --git a/intern/cycles/kernel/osl/services_gpu.h b/intern/cycles/kernel/osl/services_gpu.h index 41c9d57caea..df5cf278a67 100644 --- a/intern/cycles/kernel/osl/services_gpu.h +++ b/intern/cycles/kernel/osl/services_gpu.h @@ -764,7 +764,7 @@ ccl_device_inline bool get_object_attribute_impl(KernelGlobals kg, T dy = make_zero(); #ifdef __VOLUME__ if (primitive_is_volume_attribute(sd)) { - v = primitive_volume_attribute(kg, sd, desc, false); + v = primitive_volume_attribute(kg, sd, desc, true); } else #endif @@ -1147,9 +1147,7 @@ ccl_device_extern bool rs_texture3d(ccl_private ShaderGlobals *sg, switch (type) { case OSL_TEXTURE_HANDLE_TYPE_SVM: { - ccl_private ShaderData *const sd = static_cast(sg->renderstate); - const float4 rgba = kernel_tex_image_interp_3d( - nullptr, slot, *P, INTERPOLATION_NONE, lcg_step_float(&sd->lcg_state)); + const float4 rgba = kernel_tex_image_interp_3d(nullptr, slot, *P, INTERPOLATION_NONE, -1.0f); if (nchannels > 0) { result[0] = rgba.x; } diff --git a/intern/cycles/kernel/svm/attribute.h b/intern/cycles/kernel/svm/attribute.h index 01192d98ea8..7917ecfee00 100644 --- a/intern/cycles/kernel/svm/attribute.h +++ b/intern/cycles/kernel/svm/attribute.h @@ -65,7 +65,8 @@ ccl_device_noinline void svm_node_attr(KernelGlobals kg, /* Volumes * NOTE: moving this into its own node type might help improve performance. */ if (primitive_is_volume_attribute(sd)) { - const float4 value = volume_attribute_float4(kg, sd, desc, false); + const bool stochastic_sample = node.w; + const float4 value = volume_attribute_float4(kg, sd, desc, stochastic_sample); if (type == NODE_ATTR_OUTPUT_FLOAT) { const float f = volume_attribute_value(value); diff --git a/intern/cycles/scene/shader_graph.cpp b/intern/cycles/scene/shader_graph.cpp index 8eefea34904..89670c20623 100644 --- a/intern/cycles/scene/shader_graph.cpp +++ b/intern/cycles/scene/shader_graph.cpp @@ -663,43 +663,68 @@ void ShaderGraph::deduplicate_nodes() } } -/* Check whether volume output has meaningful nodes, otherwise - * disconnect the output. - */ -void ShaderGraph::verify_volume_output() +/* Does two optimizations: + * - Check whether volume output has meaningful nodes, otherwise disconnect the output. + * - Tag volume attribute nodes as supporting stochastic sampling. */ +void ShaderGraph::optimize_volume_output() { - /* Check whether we can optimize the whole volume graph out. */ ShaderInput *volume_in = output()->input("Volume"); if (volume_in->link == nullptr) { return; } + bool has_valid_volume = false; - ShaderNodeSet scheduled; - queue traverse_queue; + + using ShaderNodeAndNonLinear = std::pair; + set scheduled; + queue traverse_queue; + /* Schedule volume output. */ - traverse_queue.push(volume_in->link->parent); - scheduled.insert(volume_in->link->parent); + traverse_queue.emplace(volume_in->link->parent, false); + scheduled.insert({volume_in->link->parent, false}); + /* Traverse down the tree. */ while (!traverse_queue.empty()) { - ShaderNode *node = traverse_queue.front(); + auto [node, nonlinear] = traverse_queue.front(); traverse_queue.pop(); - /* Node is fully valid for volume, can't optimize anything out. */ + + /* Disable stochastic sampling on node if its contribution is nonlinear. + * This defaults to true in the class, so we only need to disable it. */ + if (nonlinear && node->type == AttributeNode::get_node_type()) { + static_cast(node)->stochastic_sample = false; + } + nonlinear = nonlinear || !node->is_linear_operation(); + + /* Node is fully valid for volume, won't be able to optimize it out. */ if (node->has_volume_support()) { has_valid_volume = true; - break; } + for (ShaderInput *input : node->inputs) { if (input->link == nullptr) { continue; } - if (scheduled.find(input->link->parent) != scheduled.end()) { + ShaderNode *input_node = input->link->parent; + if (scheduled.find({input_node, nonlinear}) != scheduled.end()) { continue; } - traverse_queue.push(input->link->parent); - scheduled.insert(input->link->parent); + traverse_queue.emplace(input_node, nonlinear); + scheduled.insert({input_node, nonlinear}); } } + + if (LOG_IS_ON(DEBUG)) { + for (ShaderNode *node : nodes) { + if (node->type == AttributeNode::get_node_type() && + static_cast(node)->stochastic_sample) + { + LOG(DEBUG) << "Volume attribute node " << node->name << " uses stochastic sampling"; + } + } + } + if (!has_valid_volume) { + /* We can remove the entire volume shader. */ LOG(DEBUG) << "Disconnect meaningless volume output."; disconnect(volume_in->link); } @@ -771,7 +796,7 @@ void ShaderGraph::clean(Scene *scene) constant_fold(scene); simplify_settings(scene); deduplicate_nodes(); - verify_volume_output(); + optimize_volume_output(); /* we do two things here: find cycles and break them, and remove unused * nodes that don't feed into the output. how cycles are broken is diff --git a/intern/cycles/scene/shader_graph.h b/intern/cycles/scene/shader_graph.h index 90b45c86a9f..06f11d2f84a 100644 --- a/intern/cycles/scene/shader_graph.h +++ b/intern/cycles/scene/shader_graph.h @@ -205,6 +205,12 @@ class ShaderNode : public Node { { return false; } + /* True if the node only multiplies or adds a constant values. */ + virtual bool is_linear_operation() + { + return false; + } + unique_ptr_vector inputs; unique_ptr_vector outputs; @@ -283,6 +289,15 @@ class ShaderNodeIDComparator { } }; +class ShaderNodeIDAndBoolComparator { + public: + bool operator()(const std::pair p1, + const std::pair p2) const + { + return p1.first->id < p2.first->id || p1.second < p2.second; + } +}; + using ShaderNodeSet = set; using ShaderNodeMap = map; @@ -369,7 +384,7 @@ class ShaderGraph : public NodeOwner { void constant_fold(Scene *scene); void simplify_settings(Scene *scene); void deduplicate_nodes(); - void verify_volume_output(); + void optimize_volume_output(); }; CCL_NAMESPACE_END diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 8398a43b814..f9b90de0337 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -5063,6 +5063,20 @@ void MixNode::constant_fold(const ConstantFolder &folder) } } +bool MixNode::is_linear_operation() +{ + switch (mix_type) { + case NODE_MIX_BLEND: + case NODE_MIX_ADD: + case NODE_MIX_MUL: + case NODE_MIX_SUB: + break; + default: + return false; + } + return use_clamp == false && input("Factor")->link == nullptr; +} + /* Mix Color */ NODE_DEFINE(MixColorNode) @@ -5143,6 +5157,20 @@ void MixColorNode::constant_fold(const ConstantFolder &folder) } } +bool MixColorNode::is_linear_operation() +{ + switch (blend_type) { + case NODE_MIX_BLEND: + case NODE_MIX_ADD: + case NODE_MIX_MUL: + case NODE_MIX_SUB: + break; + default: + return false; + } + return use_clamp == false && use_clamp_result == false && input("Factor")->link == nullptr; +} + /* Mix Float */ NODE_DEFINE(MixFloatNode) @@ -5197,6 +5225,11 @@ void MixFloatNode::constant_fold(const ConstantFolder &folder) } } +bool MixFloatNode::is_linear_operation() +{ + return use_clamp == false && input("Factor")->link == nullptr; +} + /* Mix Vector */ NODE_DEFINE(MixVectorNode) @@ -5251,6 +5284,11 @@ void MixVectorNode::constant_fold(const ConstantFolder &folder) } } +bool MixVectorNode::is_linear_operation() +{ + return use_clamp == false && input("Factor")->link == nullptr; +} + /* Mix Vector Non Uniform */ NODE_DEFINE(MixVectorNonUniformNode) @@ -5302,6 +5340,11 @@ void MixVectorNonUniformNode::constant_fold(const ConstantFolder &folder) } } +bool MixVectorNonUniformNode::is_linear_operation() +{ + return use_clamp == false && input("Factor")->link == nullptr; +} + /* Combine Color */ NODE_DEFINE(CombineColorNode) @@ -5699,6 +5742,9 @@ void AttributeNode::compile(SVMCompiler &compiler) ShaderOutput *alpha_out = output("Alpha"); ShaderNodeType attr_node = NODE_ATTR; const int attr = compiler.attribute_standard(attribute); + const uint bump_filter_or_stochastic = (compiler.output_type() == SHADER_TYPE_VOLUME) ? + stochastic_sample : + __float_as_uint(bump_filter_width); if (bump == SHADER_BUMP_DX) { attr_node = NODE_ATTR_BUMP_DX; @@ -5713,14 +5759,14 @@ void AttributeNode::compile(SVMCompiler &compiler) attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(color_out), NODE_ATTR_OUTPUT_FLOAT3), - __float_as_uint(bump_filter_width)); + bump_filter_or_stochastic); } if (!vector_out->links.empty()) { compiler.add_node( attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(vector_out), NODE_ATTR_OUTPUT_FLOAT3), - __float_as_uint(bump_filter_width)); + bump_filter_or_stochastic); } } @@ -5729,7 +5775,7 @@ void AttributeNode::compile(SVMCompiler &compiler) attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(fac_out), NODE_ATTR_OUTPUT_FLOAT), - __float_as_uint(bump_filter_width)); + bump_filter_or_stochastic); } if (!alpha_out->links.empty()) { @@ -5737,7 +5783,7 @@ void AttributeNode::compile(SVMCompiler &compiler) attr_node, attr, compiler.encode_uchar4(compiler.stack_assign(alpha_out), NODE_ATTR_OUTPUT_FLOAT_ALPHA), - __float_as_uint(bump_filter_width)); + bump_filter_or_stochastic); } } @@ -6091,6 +6137,20 @@ void MapRangeNode::expand(ShaderGraph *graph) } } +bool MapRangeNode::is_linear_operation() +{ + if (range_type != NODE_MAP_RANGE_LINEAR) { + return false; + } + + ShaderInput *from_min_in = input("To Min"); + ShaderInput *from_max_in = input("To Max"); + ShaderInput *to_min_in = input("To Min"); + ShaderInput *to_max_in = input("To Max"); + return from_min_in->link == nullptr && from_max_in->link == nullptr && + to_min_in->link == nullptr && to_max_in->link == nullptr; +} + void MapRangeNode::compile(SVMCompiler &compiler) { ShaderInput *value_in = input("Value"); @@ -6159,6 +6219,20 @@ VectorMapRangeNode::VectorMapRangeNode() : ShaderNode(get_node_type()) {} void VectorMapRangeNode::expand(ShaderGraph * /*graph*/) {} +bool VectorMapRangeNode::is_linear_operation() +{ + if (range_type != NODE_MAP_RANGE_LINEAR) { + return false; + } + + ShaderInput *from_min_in = input("From_Min_FLOAT3"); + ShaderInput *from_max_in = input("From_Max_FLOAT3"); + ShaderInput *to_min_in = input("To_Min_FLOAT3"); + ShaderInput *to_max_in = input("To_Max_FLOAT3"); + return from_min_in->link == nullptr && from_max_in->link == nullptr && + to_min_in->link == nullptr && to_max_in->link == nullptr; +} + void VectorMapRangeNode::compile(SVMCompiler &compiler) { ShaderInput *vector_in = input("Vector"); @@ -6391,6 +6465,27 @@ void MathNode::constant_fold(const ConstantFolder &folder) } } +bool MathNode::is_linear_operation() +{ + switch (math_type) { + case NODE_MATH_ADD: + case NODE_MATH_SUBTRACT: + case NODE_MATH_MULTIPLY: + case NODE_MATH_MULTIPLY_ADD: + break; + case NODE_MATH_DIVIDE: + return input("Value2")->link == nullptr; + default: + return false; + } + + int num_variable_inputs = 0; + for (ShaderInput *input : inputs) { + num_variable_inputs += (input->link) ? 1 : 0; + } + return num_variable_inputs <= 1; +} + void MathNode::compile(SVMCompiler &compiler) { ShaderInput *value1_in = input("Value1"); @@ -6491,6 +6586,27 @@ void VectorMathNode::constant_fold(const ConstantFolder &folder) } } +bool VectorMathNode::is_linear_operation() +{ + switch (math_type) { + case NODE_VECTOR_MATH_ADD: + case NODE_VECTOR_MATH_SUBTRACT: + case NODE_VECTOR_MATH_MULTIPLY: + case NODE_VECTOR_MATH_MULTIPLY_ADD: + break; + case NODE_VECTOR_MATH_DIVIDE: + return input("Vector2")->link == nullptr; + default: + return false; + } + + int num_variable_inputs = 0; + for (ShaderInput *input : inputs) { + num_variable_inputs += (input->link) ? 1 : 0; + } + return num_variable_inputs <= 1; +} + void VectorMathNode::compile(SVMCompiler &compiler) { ShaderInput *vector1_in = input("Vector1"); diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 3e981854ca6..dc3354f591e 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -5,6 +5,7 @@ #pragma once #include "graph/node.h" +#include "kernel/svm/types.h" #include "scene/image.h" #include "scene/shader_graph.h" @@ -190,6 +191,11 @@ class OutputNode : public ShaderNode { { return false; } + + bool is_linear_operation() override + { + return true; + } }; class OutputAOVNode : public ShaderNode { @@ -208,6 +214,11 @@ class OutputAOVNode : public ShaderNode { return false; } + bool is_linear_operation() override + { + return true; + } + int offset; bool is_color; }; @@ -386,6 +397,10 @@ class RGBToBWNode : public ShaderNode { public: SHADER_NODE_CLASS(RGBToBWNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float3, color) }; @@ -398,6 +413,11 @@ class ConvertNode : public ShaderNode { void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } + private: SocketType::Type from, to; @@ -422,6 +442,10 @@ class BsdfBaseNode : public ShaderNode { public: BsdfBaseNode(const NodeType *node_type); + bool is_linear_operation() override + { + return true; + } bool has_spatial_varying() override { return true; @@ -467,6 +491,10 @@ class BsdfNode : public BsdfBaseNode { class DiffuseBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(DiffuseBsdfNode) + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float, roughness) }; @@ -690,6 +718,10 @@ class EmissionNode : public ShaderNode { { return true; } + bool is_linear_operation() override + { + return true; + } int get_feature() override { @@ -708,6 +740,10 @@ class BackgroundNode : public ShaderNode { public: SHADER_NODE_CLASS(BackgroundNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } int get_feature() override { @@ -726,6 +762,10 @@ class HoldoutNode : public ShaderNode { { return CLOSURE_HOLDOUT_ID; } + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float, surface_mix_weight) NODE_SOCKET_API(float, volume_mix_weight) @@ -757,6 +797,10 @@ class VolumeNode : public ShaderNode { public: VolumeNode(const NodeType *node_type); SHADER_NODE_BASE_CLASS(VolumeNode) + bool is_linear_operation() override + { + return true; + } void compile(SVMCompiler &compiler, ShaderInput *density, @@ -1058,6 +1102,10 @@ class ValueNode : public ShaderNode { SHADER_NODE_CLASS(ValueNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float, value) }; @@ -1067,6 +1115,10 @@ class ColorNode : public ShaderNode { SHADER_NODE_CLASS(ColorNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float3, value) }; @@ -1075,12 +1127,20 @@ class AddClosureNode : public ShaderNode { public: SHADER_NODE_CLASS(AddClosureNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } }; class MixClosureNode : public ShaderNode { public: SHADER_NODE_CLASS(MixClosureNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float, fac) }; @@ -1088,6 +1148,10 @@ class MixClosureNode : public ShaderNode { class MixClosureWeightNode : public ShaderNode { public: SHADER_NODE_CLASS(MixClosureWeightNode) + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float, weight) NODE_SOCKET_API(float, fac) @@ -1100,12 +1164,18 @@ class InvertNode : public ShaderNode { NODE_SOCKET_API(float, fac) NODE_SOCKET_API(float3, color) + + bool is_linear_operation() override + { + return true; + } }; class MixNode : public ShaderNode { public: SHADER_NODE_CLASS(MixNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override; NODE_SOCKET_API(NodeMix, mix_type) NODE_SOCKET_API(bool, use_clamp) @@ -1118,6 +1188,7 @@ class MixColorNode : public ShaderNode { public: SHADER_NODE_CLASS(MixColorNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override; NODE_SOCKET_API(float3, a) NODE_SOCKET_API(float3, b) @@ -1131,6 +1202,7 @@ class MixFloatNode : public ShaderNode { public: SHADER_NODE_CLASS(MixFloatNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override; NODE_SOCKET_API(float, a) NODE_SOCKET_API(float, b) @@ -1142,6 +1214,7 @@ class MixVectorNode : public ShaderNode { public: SHADER_NODE_CLASS(MixVectorNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override; NODE_SOCKET_API(float3, a) NODE_SOCKET_API(float3, b) @@ -1153,6 +1226,7 @@ class MixVectorNonUniformNode : public ShaderNode { public: SHADER_NODE_CLASS(MixVectorNonUniformNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override; NODE_SOCKET_API(float3, a) NODE_SOCKET_API(float3, b) @@ -1164,6 +1238,10 @@ class CombineColorNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineColorNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(NodeCombSepColorType, color_type) NODE_SOCKET_API(float, r) @@ -1175,6 +1253,10 @@ class CombineXYZNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineXYZNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float, x) NODE_SOCKET_API(float, y) @@ -1204,6 +1286,10 @@ class SeparateColorNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateColorNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(NodeCombSepColorType, color_type) NODE_SOCKET_API(float3, color) @@ -1213,6 +1299,10 @@ class SeparateXYZNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateXYZNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float3, vector) }; @@ -1242,6 +1332,8 @@ class AttributeNode : public ShaderNode { } NODE_SOCKET_API(ustring, attribute) + + bool stochastic_sample = true; }; class CameraNode : public ShaderNode { @@ -1308,6 +1400,7 @@ class VectorMapRangeNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorMapRangeNode) void expand(ShaderGraph *graph) override; + bool is_linear_operation() override; NODE_SOCKET_API(float3, vector) NODE_SOCKET_API(float3, from_min) @@ -1323,6 +1416,7 @@ class MapRangeNode : public ShaderNode { public: SHADER_NODE_CLASS(MapRangeNode) void expand(ShaderGraph *graph) override; + bool is_linear_operation() override; NODE_SOCKET_API(float, value) NODE_SOCKET_API(float, from_min) @@ -1349,6 +1443,7 @@ class MathNode : public ShaderNode { SHADER_NODE_CLASS(MathNode) void expand(ShaderGraph *graph) override; void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override; NODE_SOCKET_API(float, value1) NODE_SOCKET_API(float, value2) @@ -1360,6 +1455,10 @@ class MathNode : public ShaderNode { class NormalNode : public ShaderNode { public: SHADER_NODE_CLASS(NormalNode) + bool is_linear_operation() override + { + return true; + } NODE_SOCKET_API(float3, direction) NODE_SOCKET_API(float3, normal) @@ -1369,6 +1468,7 @@ class VectorMathNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorMathNode) void constant_fold(const ConstantFolder &folder) override; + bool is_linear_operation() override; NODE_SOCKET_API(float3, vector1) NODE_SOCKET_API(float3, vector2) diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 3f1bfbf0705..9da12949264 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -116,6 +116,11 @@ class ShaderGraphBuilder { return (*this).add_connection(from, "Output::Surface"); } + ShaderGraphBuilder &output_volume_closure(const string &from) + { + return (*this).add_connection(from, "Output::Volume"); + } + ShaderGraphBuilder &output_color(const string &from) { return (*this) @@ -1518,4 +1523,65 @@ TEST_F(RenderGraph, constant_fold_convert_color_float_color) log.invalid_info_message("Folding convert_float_to_color::"); } +/* + * Tests: + * - Stochastic sampling with math multiply node. + */ +TEST_F(RenderGraph, stochastic_sample_math_multiply) +{ + builder.add_attribute("Attribute") + .add_node(ShaderNodeBuilder(graph, "MathMultiply") + .set_param("math_type", NODE_MATH_MULTIPLY)) + .add_node(ShaderNodeBuilder(graph, "ScatterVolume")) + .add_connection("Attribute::Fac", "MathMultiply::Value1") + .add_connection("MathMultiply::Value", "ScatterVolume::Density") + .output_volume_closure("ScatterVolume::Volume"); + + graph.finalize(scene.get()); + + log.correct_info_message("Volume attribute node Attribute uses stochastic sampling"); +} + +/* + * Tests: + * - No stochastic sampling with math power node. + */ +TEST_F(RenderGraph, not_stochastic_sample_math_power) +{ + builder.add_attribute("Attribute") + .add_node( + ShaderNodeBuilder(graph, "MathPower").set_param("math_type", NODE_MATH_POWER)) + .add_node(ShaderNodeBuilder(graph, "ScatterVolume")) + .add_connection("Attribute::Fac", "MathPower::Value1") + .add_connection("MathPower::Value", "ScatterVolume::Density") + .output_volume_closure("ScatterVolume::Volume"); + + graph.finalize(scene.get()); + + log.invalid_info_message("Volume attribute node Attribute uses stochastic sampling"); +} + +/* + * Tests: + * - Stochastic sampling temperature with map range, principled volume and mix closure. + */ +TEST_F(RenderGraph, stochastic_sample_principled_volume_mix) +{ + builder.add_attribute("Attribute") + .add_node(ShaderNodeBuilder(graph, "MapRange")) + .add_node(ShaderNodeBuilder(graph, "MixClosure").set("Fac", 0.5f)) + .add_node(ShaderNodeBuilder(graph, "PrincipledVolume1")) + .add_node(ShaderNodeBuilder(graph, "PrincipledVolume2")) + .add_connection("Attribute::Color", "MapRange::Value") + .add_connection("MapRange::Result", "PrincipledVolume1::Temperature") + .add_connection("Attribute::Fac", "PrincipledVolume2::Density") + .add_connection("PrincipledVolume1::Volume", "MixClosure::Closure1") + .add_connection("PrincipledVolume2::Volume", "MixClosure::Closure2") + .output_volume_closure("MixClosure::Closure"); + + graph.finalize(scene.get()); + + log.correct_info_message("Volume attribute node Attribute uses stochastic sampling"); +} + CCL_NAMESPACE_END diff --git a/tests/files/render/openvdb/cycles_renders/fire.png b/tests/files/render/openvdb/cycles_renders/fire.png index 6e9c9f0081e..85db3076515 100644 --- a/tests/files/render/openvdb/cycles_renders/fire.png +++ b/tests/files/render/openvdb/cycles_renders/fire.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66ef9339c46ddc5d3bf0aa61c7437610a02a95974b73abc7e10ce1f567eda734 +oid sha256:88fda82e74f2e3a129d6ba74cfbb9048199ea9ac138443de511896ed262fa9b6 size 39043 diff --git a/tests/files/render/openvdb/cycles_renders/smoke_color.png b/tests/files/render/openvdb/cycles_renders/smoke_color.png index 6e5d35d3165..aeef9302e9e 100644 --- a/tests/files/render/openvdb/cycles_renders/smoke_color.png +++ b/tests/files/render/openvdb/cycles_renders/smoke_color.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dff2bee0a44eb316e1eb9ccf90de929237d88f5b9e1e09d8b4be49224266e92 -size 325 +oid sha256:dd8b988364dd9bbf9f902844c62aaefb8f8651d49395a9ad60719811221b6076 +size 28027 diff --git a/tests/files/render/openvdb/cycles_renders/smoke_fire.png b/tests/files/render/openvdb/cycles_renders/smoke_fire.png index 705ca530336..453a510248f 100644 --- a/tests/files/render/openvdb/cycles_renders/smoke_fire.png +++ b/tests/files/render/openvdb/cycles_renders/smoke_fire.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e81e1670742c07ceee4736b9828b7284f735a4b26e8dcd441a7ef511560e1c4 -size 325 +oid sha256:349e6612373aeff0438ffe03c510f1885644dc088963ac517d0cb7d2d01c9a82 +size 38818