Cycles: Detect volume attribute nodes that can use stochastic sampling
Detect which volume attributes nodes have a linear mapping to their usage as density / color / temperature in volume shader nodes, and use stochastic sampling for them. Pull Request: https://projects.blender.org/blender/blender/pulls/132908
This commit is contained in:
@@ -657,7 +657,7 @@ inline bool get_object_attribute_impl(const ThreadKernelGlobalsCPU *kg,
|
||||
T dy = make_zero<T>();
|
||||
#ifdef __VOLUME__
|
||||
if (primitive_is_volume_attribute(sd)) {
|
||||
v = primitive_volume_attribute<T>(kg, sd, desc, false);
|
||||
v = primitive_volume_attribute<T>(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) {
|
||||
|
||||
@@ -764,7 +764,7 @@ ccl_device_inline bool get_object_attribute_impl(KernelGlobals kg,
|
||||
T dy = make_zero<T>();
|
||||
#ifdef __VOLUME__
|
||||
if (primitive_is_volume_attribute(sd)) {
|
||||
v = primitive_volume_attribute<T>(kg, sd, desc, false);
|
||||
v = primitive_volume_attribute<T>(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<ccl_private ShaderData *>(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;
|
||||
}
|
||||
|
||||
@@ -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<float>(value);
|
||||
|
||||
@@ -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<ShaderNode *> traverse_queue;
|
||||
|
||||
using ShaderNodeAndNonLinear = std::pair<ShaderNode *, bool>;
|
||||
set<ShaderNodeAndNonLinear, ShaderNodeIDAndBoolComparator> scheduled;
|
||||
queue<ShaderNodeAndNonLinear> 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<AttributeNode *>(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<AttributeNode *>(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
|
||||
|
||||
@@ -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<ShaderInput> inputs;
|
||||
unique_ptr_vector<ShaderOutput> outputs;
|
||||
|
||||
@@ -283,6 +289,15 @@ class ShaderNodeIDComparator {
|
||||
}
|
||||
};
|
||||
|
||||
class ShaderNodeIDAndBoolComparator {
|
||||
public:
|
||||
bool operator()(const std::pair<ShaderNode *, bool> p1,
|
||||
const std::pair<ShaderNode *, bool> p2) const
|
||||
{
|
||||
return p1.first->id < p2.first->id || p1.second < p2.second;
|
||||
}
|
||||
};
|
||||
|
||||
using ShaderNodeSet = set<ShaderNode *, ShaderNodeIDComparator>;
|
||||
using ShaderNodeMap = map<ShaderNode *, ShaderNode *, ShaderNodeIDComparator>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<MathNode>(graph, "MathMultiply")
|
||||
.set_param("math_type", NODE_MATH_MULTIPLY))
|
||||
.add_node(ShaderNodeBuilder<ScatterVolumeNode>(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<MathNode>(graph, "MathPower").set_param("math_type", NODE_MATH_POWER))
|
||||
.add_node(ShaderNodeBuilder<ScatterVolumeNode>(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<MapRangeNode>(graph, "MapRange"))
|
||||
.add_node(ShaderNodeBuilder<MixClosureNode>(graph, "MixClosure").set("Fac", 0.5f))
|
||||
.add_node(ShaderNodeBuilder<PrincipledVolumeNode>(graph, "PrincipledVolume1"))
|
||||
.add_node(ShaderNodeBuilder<PrincipledVolumeNode>(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
|
||||
|
||||
BIN
tests/files/render/openvdb/cycles_renders/fire.png
(Stored with Git LFS)
BIN
tests/files/render/openvdb/cycles_renders/fire.png
(Stored with Git LFS)
Binary file not shown.
BIN
tests/files/render/openvdb/cycles_renders/smoke_color.png
(Stored with Git LFS)
BIN
tests/files/render/openvdb/cycles_renders/smoke_color.png
(Stored with Git LFS)
Binary file not shown.
BIN
tests/files/render/openvdb/cycles_renders/smoke_fire.png
(Stored with Git LFS)
BIN
tests/files/render/openvdb/cycles_renders/smoke_fire.png
(Stored with Git LFS)
Binary file not shown.
Reference in New Issue
Block a user