diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index ece2c38b243..a10919fe497 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -747,6 +747,28 @@ static ShaderNode *add_node(Scene *scene, else if (b_node.is_a(&RNA_ShaderNodeVolumeAbsorption)) { node = graph->create_node(); } + else if (b_node.is_a(&RNA_ShaderNodeVolumeCoefficients)) { + BL::ShaderNodeVolumeCoefficients b_coeffs_node(b_node); + VolumeCoefficientsNode *coeffs = graph->create_node(); + switch (b_coeffs_node.phase()) { + case BL::ShaderNodeVolumeCoefficients::phase_HENYEY_GREENSTEIN: + coeffs->set_phase(CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); + break; + case BL::ShaderNodeVolumeCoefficients::phase_FOURNIER_FORAND: + coeffs->set_phase(CLOSURE_VOLUME_FOURNIER_FORAND_ID); + break; + case BL::ShaderNodeVolumeCoefficients::phase_DRAINE: + coeffs->set_phase(CLOSURE_VOLUME_DRAINE_ID); + break; + case BL::ShaderNodeVolumeCoefficients::phase_RAYLEIGH: + coeffs->set_phase(CLOSURE_VOLUME_RAYLEIGH_ID); + break; + case BL::ShaderNodeVolumeCoefficients::phase_MIE: + coeffs->set_phase(CLOSURE_VOLUME_MIE_ID); + break; + } + node = coeffs; + } else if (b_node.is_a(&RNA_ShaderNodeVolumePrincipled)) { PrincipledVolumeNode *principled = graph->create_node(); node = principled; diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt index 51617f1fea3..8cacde4b7ad 100644 --- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC_OSL node_point_info.osl node_scatter_volume.osl node_absorption_volume.osl + node_volume_coefficients.osl node_principled_volume.osl node_holdout.osl node_hsv.osl diff --git a/intern/cycles/kernel/osl/shaders/node_scatter.h b/intern/cycles/kernel/osl/shaders/node_scatter.h new file mode 100644 index 00000000000..d256e6e6d58 --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_scatter.h @@ -0,0 +1,74 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: Apache-2.0 */ + +#include "stdcycles.h" + +struct MieParameters { + float g_HG; + float g_D; + float alpha; + float mixture; +}; + +MieParameters phase_mie_fitted_parameters(float Diameter) +{ + float d = max(Diameter, 0.0); + if (d <= 0.1) { + /* Eq (11 - 14). */ + return {13.8 * d * d, 1.1456 * d * sin(9.29044 * d), 250.0, 0.252977 - 312.983 * pow(d, 4.3)}; + } + + if (d < 1.5) { + /* Eq (15 - 18). */ + float log_d = log(d); + float a = (log_d - 0.238604) * (log_d + 1.00667); + float b = 0.507522 - 0.15677 * log_d; + float c = 1.19692 * cos(a / b) + 1.37932 * log_d + 0.0625835; + return {0.862 - 0.143 * log_d * log_d, + 0.379685 * cos(c) + 0.344213, + 250.0, + 0.146209 * cos(3.38707 * log_d + 2.11193) + 0.316072 + 0.0778917 * log_d}; + } + + if (d < 5.0) { + /* Eq (19 - 22). */ + float log_d = log(d); + float temp = cos(5.68947 * (log(log_d) - 0.0292149)); + return {0.0604931 * log(log_d) + 0.940256, + 0.500411 - (0.081287 / (-2.0 * log_d + tan(log_d) + 1.27551)), + 7.30354 * log_d + 6.31675, + 0.026914 * (log_d - temp) + 0.3764}; + } + + /* Eq (7 - 10). */ + return {exp(-0.0990567 / (d - 1.67154)), + exp(-2.20679 / (d + 3.91029) - 0.428934), + exp(3.62489 - 8.29288 / (d + 5.52825)), + exp(-0.599085 / (d - 0.641583) - 0.665888)}; +} + +closure color +scatter(string phase, float Anisotropy, float IOR, float Backscatter, float Alpha, float Diameter) +{ + closure color scatter = 0; + if (phase == "Fournier-Forand") { + scatter = fournier_forand(Backscatter, IOR); + } + else if (phase == "Draine") { + scatter = draine(Anisotropy, Alpha); + } + else if (phase == "Rayleigh") { + scatter = rayleigh(); + } + else if (phase == "Mie") { + /* Approximation of Mie phase function for water droplets using a mix of Draine and H-G. + * See `kernel/svm/closure.h` for details. */ + MieParameters param = phase_mie_fitted_parameters(Diameter); + scatter = mix(henyey_greenstein(param.g_HG), draine(param.g_D, param.alpha), param.mixture); + } + else { + scatter = henyey_greenstein(Anisotropy); + } + return scatter; +} diff --git a/intern/cycles/kernel/osl/shaders/node_scatter_volume.osl b/intern/cycles/kernel/osl/shaders/node_scatter_volume.osl index d5542b6f98a..61cadff06f5 100644 --- a/intern/cycles/kernel/osl/shaders/node_scatter_volume.osl +++ b/intern/cycles/kernel/osl/shaders/node_scatter_volume.osl @@ -2,51 +2,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#include "stdcycles.h" - -struct MieParameters { - float g_HG; - float g_D; - float alpha; - float mixture; -}; - -MieParameters phase_mie_fitted_parameters(float Diameter) -{ - float d = max(Diameter, 0.0); - if (d <= 0.1) { - /* Eq (11 - 14). */ - return {13.8 * d * d, 1.1456 * d * sin(9.29044 * d), 250.0, 0.252977 - 312.983 * pow(d, 4.3)}; - } - - if (d < 1.5) { - /* Eq (15 - 18). */ - float log_d = log(d); - float a = (log_d - 0.238604) * (log_d + 1.00667); - float b = 0.507522 - 0.15677 * log_d; - float c = 1.19692 * cos(a / b) + 1.37932 * log_d + 0.0625835; - return {0.862 - 0.143 * log_d * log_d, - 0.379685 * cos(c) + 0.344213, - 250.0, - 0.146209 * cos(3.38707 * log_d + 2.11193) + 0.316072 + 0.0778917 * log_d}; - } - - if (d < 5.0) { - /* Eq (19 - 22). */ - float log_d = log(d); - float temp = cos(5.68947 * (log(log_d) - 0.0292149)); - return {0.0604931 * log(log_d) + 0.940256, - 0.500411 - (0.081287 / (-2.0 * log_d + tan(log_d) + 1.27551)), - 7.30354 * log_d + 6.31675, - 0.026914 * (log_d - temp) + 0.3764}; - } - - /* Eq (7 - 10). */ - return {exp(-0.0990567 / (d - 1.67154)), - exp(-2.20679 / (d + 3.91029) - 0.428934), - exp(3.62489 - 8.29288 / (d + 5.52825)), - exp(-0.599085 / (d - 0.641583) - 0.665888)}; -} +#include "node_scatter.h" shader node_scatter_volume(string phase = "Henyey-Greenstein", color Color = color(0.8, 0.8, 0.8), @@ -58,25 +14,7 @@ shader node_scatter_volume(string phase = "Henyey-Greenstein", float Diameter = 20.0, output closure color Volume = 0) { - closure color scatter = 0; - if (phase == "Fournier-Forand") { - scatter = fournier_forand(Backscatter, IOR); - } - else if (phase == "Draine") { - scatter = draine(Anisotropy, Alpha); - } - else if (phase == "Rayleigh") { - scatter = rayleigh(); - } - else if (phase == "Mie") { - /* Approximation of Mie phase function for water droplets using a mix of Draine and H-G. - * See `kernel/svm/closure.h` for details. */ - MieParameters param = phase_mie_fitted_parameters(Diameter); - scatter = mix(henyey_greenstein(param.g_HG), draine(param.g_D, param.alpha), param.mixture); - } - else { - scatter = henyey_greenstein(Anisotropy); - } + closure color scatter_closure = scatter(phase, Anisotropy, IOR, Backscatter, Alpha, Diameter); - Volume = (Color * max(Density, 0.0)) * scatter; + Volume = (Color * max(Density, 0.0)) * scatter_closure; } diff --git a/intern/cycles/kernel/osl/shaders/node_volume_coefficients.osl b/intern/cycles/kernel/osl/shaders/node_volume_coefficients.osl new file mode 100644 index 00000000000..8c15e0936d8 --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_volume_coefficients.osl @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation + * + * SPDX-License-Identifier: Apache-2.0 */ + +#include "node_scatter.h" + +shader node_volume_coefficients(string phase = "Henyey-Greenstein", + vector AbsorptionCoefficients = vector(1.0, 1.0, 1.0), + vector ScatterCoefficients = vector(1.0, 1.0, 1.0), + float Anisotropy = 0.0, + float IOR = 1.33, + float Backscatter = 0.1, + float Alpha = 0.5, + float Diameter = 20.0, + vector EmissionCoefficients = vector(0.0, 0.0, 0.0), + output closure color Volume = 0) +{ + closure color scatter_closure = scatter(phase, Anisotropy, IOR, Backscatter, Alpha, Diameter); + + /* Add scattering and absorption closures. */ + Volume = color(ScatterCoefficients) * scatter_closure + + color(AbsorptionCoefficients) * absorption(); + + /* Compute emission. */ + Volume += color(EmissionCoefficients) * emission(); +} diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index f72a80153f5..2ad5b7e2eeb 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -1053,6 +1053,78 @@ ccl_device return offset; } +ccl_device_inline void svm_alloc_closure_volume_scatter(ccl_private ShaderData *sd, + ccl_private float *stack, + Spectrum weight, + const uint type, + const uint param1_offset, + const uint param_extra) +{ + switch (type) { + case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: { + ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *) + bsdf_alloc(sd, sizeof(HenyeyGreensteinVolume), weight); + if (volume) { + volume->g = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) : + __uint_as_float(param_extra); + sd->flag |= volume_henyey_greenstein_setup(volume); + } + } break; + case CLOSURE_VOLUME_FOURNIER_FORAND_ID: { + ccl_private FournierForandVolume *volume = (ccl_private FournierForandVolume *)bsdf_alloc( + sd, sizeof(FournierForandVolume), weight); + if (volume) { + const float IOR = stack_load_float(stack, param1_offset); + const float B = stack_load_float(stack, param_extra); + sd->flag |= volume_fournier_forand_setup(volume, B, IOR); + } + } break; + case CLOSURE_VOLUME_RAYLEIGH_ID: { + ccl_private RayleighVolume *volume = (ccl_private RayleighVolume *)bsdf_alloc( + sd, sizeof(RayleighVolume), weight); + if (volume) { + sd->flag |= volume_rayleigh_setup(volume); + } + break; + } + case CLOSURE_VOLUME_DRAINE_ID: { + ccl_private DraineVolume *volume = (ccl_private DraineVolume *)bsdf_alloc( + sd, sizeof(DraineVolume), weight); + if (volume) { + volume->g = stack_load_float(stack, param1_offset); + volume->alpha = stack_load_float(stack, param_extra); + sd->flag |= volume_draine_setup(volume); + } + } break; + case CLOSURE_VOLUME_MIE_ID: { + const float d = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) : + __uint_as_float(param_extra); + float g_HG; + float g_D; + float alpha; + float mixture; + phase_mie_fitted_parameters(d, &g_HG, &g_D, &alpha, &mixture); + ccl_private HenyeyGreensteinVolume *hg = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc( + sd, sizeof(HenyeyGreensteinVolume), weight * (1.0f - mixture)); + if (hg) { + hg->g = g_HG; + sd->flag |= volume_henyey_greenstein_setup(hg); + } + ccl_private DraineVolume *draine = (ccl_private DraineVolume *)bsdf_alloc( + sd, sizeof(DraineVolume), weight * mixture); + if (draine) { + draine->g = g_D; + draine->alpha = alpha; + sd->flag |= volume_draine_setup(draine); + } + } break; + default: { + kernel_assert(0); + break; + } + } +} + template ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg, ccl_private ShaderData *sd, @@ -1093,69 +1165,7 @@ ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg, /* Add closure for volume scattering. */ if (CLOSURE_IS_VOLUME_SCATTER(type)) { - switch (type) { - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: { - ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *) - bsdf_alloc(sd, sizeof(HenyeyGreensteinVolume), weight); - if (volume) { - volume->g = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) : - __uint_as_float(node.w); - sd->flag |= volume_henyey_greenstein_setup(volume); - } - } break; - case CLOSURE_VOLUME_FOURNIER_FORAND_ID: { - ccl_private FournierForandVolume *volume = (ccl_private FournierForandVolume *)bsdf_alloc( - sd, sizeof(FournierForandVolume), weight); - if (volume) { - const float IOR = stack_load_float(stack, param1_offset); - const float B = stack_load_float(stack, node.w); - sd->flag |= volume_fournier_forand_setup(volume, B, IOR); - } - } break; - case CLOSURE_VOLUME_RAYLEIGH_ID: { - ccl_private RayleighVolume *volume = (ccl_private RayleighVolume *)bsdf_alloc( - sd, sizeof(RayleighVolume), weight); - if (volume) { - sd->flag |= volume_rayleigh_setup(volume); - } - break; - } - case CLOSURE_VOLUME_DRAINE_ID: { - ccl_private DraineVolume *volume = (ccl_private DraineVolume *)bsdf_alloc( - sd, sizeof(DraineVolume), weight); - if (volume) { - volume->g = stack_load_float(stack, param1_offset); - volume->alpha = stack_load_float(stack, node.w); - sd->flag |= volume_draine_setup(volume); - } - } break; - case CLOSURE_VOLUME_MIE_ID: { - const float d = stack_valid(param1_offset) ? stack_load_float(stack, param1_offset) : - __uint_as_float(node.w); - float g_HG; - float g_D; - float alpha; - float mixture; - phase_mie_fitted_parameters(d, &g_HG, &g_D, &alpha, &mixture); - ccl_private HenyeyGreensteinVolume *hg = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc( - sd, sizeof(HenyeyGreensteinVolume), weight * (1.0f - mixture)); - if (hg) { - hg->g = g_HG; - sd->flag |= volume_henyey_greenstein_setup(hg); - } - ccl_private DraineVolume *draine = (ccl_private DraineVolume *)bsdf_alloc( - sd, sizeof(DraineVolume), weight * mixture); - if (draine) { - draine->g = g_D; - draine->alpha = alpha; - sd->flag |= volume_draine_setup(draine); - } - } break; - default: { - kernel_assert(0); - break; - } - } + svm_alloc_closure_volume_scatter(sd, stack, weight, type, param1_offset, node.w); } /* Sum total extinction weight. */ @@ -1163,6 +1173,62 @@ ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg, #endif } +template +ccl_device_noinline void svm_node_volume_coefficients(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + Spectrum scatter_coeffs, + const uint4 node, + const uint32_t path_flag) +{ +#ifdef __VOLUME__ + /* Only sum extinction for volumes, variable is shared with surface transparency. */ + if (shader_type != SHADER_TYPE_VOLUME) { + return; + } + + uint type; + uint empty_offset; + uint param1_offset; + uint mix_weight_offset; + svm_unpack_node_uchar4(node.y, &type, &empty_offset, ¶m1_offset, &mix_weight_offset); + const float mix_weight = (stack_valid(mix_weight_offset) ? + stack_load_float(stack, mix_weight_offset) : + 1.0f); + if (mix_weight == 0.0f) { + return; + } + + /* Compute scattering coefficient. */ + const float weight = mix_weight * object_volume_density(kg, sd->object); + + /* Add closure for volume scattering. */ + if (!is_zero(scatter_coeffs) && CLOSURE_IS_VOLUME_SCATTER(type)) { + svm_alloc_closure_volume_scatter( + sd, stack, weight * scatter_coeffs, type, param1_offset, node.z); + } + uint absorption_coeffs_offset; + uint emission_coeffs_offset; + svm_unpack_node_uchar4( + node.w, &absorption_coeffs_offset, &emission_coeffs_offset, &empty_offset, &empty_offset); + const float3 absorption_coeffs = stack_load_float3(stack, absorption_coeffs_offset); + volume_extinction_setup(sd, weight * (scatter_coeffs + absorption_coeffs)); + + const float3 emission_coeffs = stack_load_float3(stack, emission_coeffs_offset); + /* Compute emission. */ + if (path_flag & PATH_RAY_SHADOW) { + /* Don't need emission for shadows. */ + return; + } + + if (is_zero(emission_coeffs)) { + return; + } + emission_setup(sd, weight * emission_coeffs); + +#endif +} + template ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, ccl_private ShaderData *sd, diff --git a/intern/cycles/kernel/svm/node_types_template.h b/intern/cycles/kernel/svm/node_types_template.h index c33f27f2e94..6a3f927b793 100644 --- a/intern/cycles/kernel/svm/node_types_template.h +++ b/intern/cycles/kernel/svm/node_types_template.h @@ -50,6 +50,7 @@ SHADER_NODE_TYPE(NODE_CLOSURE_HOLDOUT) SHADER_NODE_TYPE(NODE_FRESNEL) SHADER_NODE_TYPE(NODE_LAYER_WEIGHT) SHADER_NODE_TYPE(NODE_CLOSURE_VOLUME) +SHADER_NODE_TYPE(NODE_VOLUME_COEFFICIENTS) SHADER_NODE_TYPE(NODE_PRINCIPLED_VOLUME) SHADER_NODE_TYPE(NODE_MATH) SHADER_NODE_TYPE(NODE_VECTOR_MATH) diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 2c693032733..4736caac6d8 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -296,6 +296,12 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, svm_node_closure_volume(kg, sd, stack, closure_weight, node); } break; + SVM_CASE(NODE_VOLUME_COEFFICIENTS) + IF_KERNEL_NODES_FEATURE(VOLUME) + { + svm_node_volume_coefficients(kg, sd, stack, closure_weight, node, path_flag); + } + break; SVM_CASE(NODE_PRINCIPLED_VOLUME) IF_KERNEL_NODES_FEATURE(VOLUME) { diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 176f63cca81..b26baed1c89 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -3513,6 +3513,11 @@ NODE_DEFINE(ScatterVolumeNode) return type; } +ScatterVolumeNode::ScatterVolumeNode(const NodeType *node_type) : VolumeNode(node_type) +{ + closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; +} + ScatterVolumeNode::ScatterVolumeNode() : VolumeNode(get_node_type()) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; @@ -3550,6 +3555,111 @@ void ScatterVolumeNode::compile(OSLCompiler &compiler) compiler.add(this, "node_scatter_volume"); } +/* Volume Coefficients Closure */ + +NODE_DEFINE(VolumeCoefficientsNode) +{ + NodeType *type = NodeType::add("volume_coefficients", create, NodeType::SHADER); + + SOCKET_IN_VECTOR(scatter_coeffs, "Scatter Coefficients", make_float3(1.0f, 1.0f, 1.0f)); + SOCKET_IN_VECTOR(absorption_coeffs, "Absorption Coefficients", make_float3(1.0f, 1.0f, 1.0f)); + SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); + SOCKET_IN_FLOAT(IOR, "IOR", 1.33f); + SOCKET_IN_FLOAT(backscatter, "Backscatter", 0.1f); + SOCKET_IN_FLOAT(alpha, "Alpha", 0.5f); + SOCKET_IN_FLOAT(diameter, "Diameter", 20.0f); + SOCKET_IN_VECTOR(emission_coeffs, "Emission Coefficients", make_float3(0.0f, 0.0f, 0.0f)); + + static NodeEnum phase_enum; + phase_enum.insert("Henyey-Greenstein", CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); + phase_enum.insert("Fournier-Forand", CLOSURE_VOLUME_FOURNIER_FORAND_ID); + phase_enum.insert("Draine", CLOSURE_VOLUME_DRAINE_ID); + phase_enum.insert("Rayleigh", CLOSURE_VOLUME_RAYLEIGH_ID); + phase_enum.insert("Mie", CLOSURE_VOLUME_MIE_ID); + SOCKET_ENUM(phase, "Phase", phase_enum, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); + + SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); + + SOCKET_OUT_CLOSURE(volume, "Volume"); + + return type; +} + +VolumeCoefficientsNode::VolumeCoefficientsNode() : ScatterVolumeNode(get_node_type()) +{ + closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; +} + +void VolumeCoefficientsNode::compile(SVMCompiler &compiler) +{ + closure = phase; + ShaderInput *param1 = nullptr; + ShaderInput *param2 = nullptr; + + switch (phase) { + case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: + param1 = input("Anisotropy"); + break; + case CLOSURE_VOLUME_FOURNIER_FORAND_ID: + param1 = input("IOR"); + param2 = input("Backscatter"); + break; + case CLOSURE_VOLUME_RAYLEIGH_ID: + break; + case CLOSURE_VOLUME_DRAINE_ID: + param1 = input("Anisotropy"); + param2 = input("Alpha"); + break; + case CLOSURE_VOLUME_MIE_ID: + param1 = input("Diameter"); + break; + default: + assert(false); + break; + } + ShaderInput *coeffs_in = input("Scatter Coefficients"); + ShaderInput *absorption_coeffs_in = input("Absorption Coefficients"); + ShaderInput *emission_coeffs_in = input("Emission Coefficients"); + + if (coeffs_in->link) { + compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(coeffs_in)); + } + else { + compiler.add_node(NODE_CLOSURE_SET_WEIGHT, scatter_coeffs); + } + + const uint mix_weight_ofs = compiler.closure_mix_weight_offset(); + + if (param2 == nullptr) { + /* More efficient packing if we don't need the second parameter. */ + const uint param1_ofs = (param1) ? compiler.stack_assign_if_linked(param1) : SVM_STACK_INVALID; + compiler.add_node(NODE_VOLUME_COEFFICIENTS, + compiler.encode_uchar4(closure, 0, param1_ofs, mix_weight_ofs), + __float_as_int((param1) ? get_float(param1->socket_type) : 0.0f), + compiler.encode_uchar4(compiler.stack_assign(absorption_coeffs_in), + compiler.stack_assign(emission_coeffs_in), + 0, + 0)); + } + else { + const uint param1_ofs = (param1) ? compiler.stack_assign(param1) : SVM_STACK_INVALID; + const uint param2_ofs = (param2) ? compiler.stack_assign(param2) : SVM_STACK_INVALID; + compiler.add_node(NODE_VOLUME_COEFFICIENTS, + compiler.encode_uchar4(closure, 0, param1_ofs, mix_weight_ofs), + param2_ofs, + compiler.encode_uchar4(compiler.stack_assign(absorption_coeffs_in), + compiler.stack_assign(emission_coeffs_in), + 0, + 0)); + } +} + +void VolumeCoefficientsNode::compile(OSLCompiler &compiler) +{ + compiler.parameter(this, "phase"); + compiler.add(this, "node_volume_coefficients"); +} + /* Principled Volume Closure */ NODE_DEFINE(PrincipledVolumeNode) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 31a8050f247..ff83148d433 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -836,6 +836,7 @@ class AbsorptionVolumeNode : public VolumeNode { class ScatterVolumeNode : public VolumeNode { public: + ScatterVolumeNode(const NodeType *node_type); SHADER_NODE_CLASS(ScatterVolumeNode) NODE_SOCKET_API(float, anisotropy) @@ -846,6 +847,15 @@ class ScatterVolumeNode : public VolumeNode { NODE_SOCKET_API(ClosureType, phase) }; +class VolumeCoefficientsNode : public ScatterVolumeNode { + public: + SHADER_NODE_CLASS(VolumeCoefficientsNode) + + NODE_SOCKET_API(float3, scatter_coeffs) + NODE_SOCKET_API(float3, absorption_coeffs) + NODE_SOCKET_API(float3, emission_coeffs) +}; + class PrincipledVolumeNode : public VolumeNode { public: SHADER_NODE_CLASS(PrincipledVolumeNode) diff --git a/scripts/startup/bl_ui/node_add_menu_shader.py b/scripts/startup/bl_ui/node_add_menu_shader.py index ce6d95d11e8..53ae4eb3ca3 100644 --- a/scripts/startup/bl_ui/node_add_menu_shader.py +++ b/scripts/startup/bl_ui/node_add_menu_shader.py @@ -232,6 +232,10 @@ class NODE_MT_category_shader_shader(Menu): layout, "ShaderNodeVolumeScatter", ) + node_add_menu.add_node_type( + layout, + "ShaderNodeVolumeCoefficients", + ) node_add_menu.draw_assets_for_catalog(layout, self.bl_label) diff --git a/source/blender/blenkernel/BKE_node_legacy_types.hh b/source/blender/blenkernel/BKE_node_legacy_types.hh index cab7a6521ab..36b15d698f6 100644 --- a/source/blender/blenkernel/BKE_node_legacy_types.hh +++ b/source/blender/blenkernel/BKE_node_legacy_types.hh @@ -133,6 +133,7 @@ #define SH_NODE_BSDF_RAY_PORTAL 714 #define SH_NODE_TEX_GABOR 715 #define SH_NODE_BSDF_METALLIC 716 +#define SH_NODE_VOLUME_COEFFICIENTS 717 /** \} */ diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 947da8a64c3..911b24b582a 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -629,6 +629,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_volume_absorption.glsl shaders/material/gpu_shader_material_volume_principled.glsl shaders/material/gpu_shader_material_volume_scatter.glsl + shaders/material/gpu_shader_material_volume_coefficients.glsl shaders/material/gpu_shader_material_voronoi.glsl shaders/material/gpu_shader_material_wireframe.glsl shaders/material/gpu_shader_material_world_normals.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_coefficients.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_coefficients.glsl new file mode 100644 index 00000000000..2976cec2a48 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_coefficients.glsl @@ -0,0 +1,32 @@ +/* SPDX-FileCopyrightText: 2019-2022 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_material_blackbody.glsl" + +void node_volume_coefficients(float weight, + float3 AbsorptionCoefficients, + float3 ScatterCoefficients, + float Anisotropy, + float IOR, + float Backscatter, + float Alpha, + float Diameter, + float3 EmissionCoefficients, + out Closure result) +{ + ClosureVolumeScatter volume_scatter_data; + volume_scatter_data.weight = weight; + volume_scatter_data.scattering = ScatterCoefficients; + volume_scatter_data.anisotropy = Anisotropy; + + ClosureVolumeAbsorption volume_absorption_data; + volume_absorption_data.weight = weight; + volume_absorption_data.absorption = AbsorptionCoefficients; + + ClosureEmission emission_data; + emission_data.weight = weight; + emission_data.emission = EmissionCoefficients; + + result = closure_eval(volume_scatter_data, volume_absorption_data, emission_data); +} diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index ffe05b76287..a3bd1d2ae56 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -6862,6 +6862,17 @@ static void def_scatter(BlenderRNA * /*brna*/, StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_volume_coefficients(BlenderRNA * /*brna*/, StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "phase", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "custom1"); + RNA_def_property_enum_items(prop, node_scatter_phase_items); + RNA_def_property_ui_text(prop, "Phase", "Phase function for the scattered light"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_toon(BlenderRNA * /*brna*/, StructRNA *srna) { PropertyRNA *prop; @@ -13617,6 +13628,7 @@ static void rna_def_nodes(BlenderRNA *brna) define("ShaderNode", "ShaderNodeVolumeInfo"); define("ShaderNode", "ShaderNodeVolumePrincipled"); define("ShaderNode", "ShaderNodeVolumeScatter", def_scatter); + define("ShaderNode", "ShaderNodeVolumeCoefficients", def_volume_coefficients); define("ShaderNode", "ShaderNodeWavelength"); define("ShaderNode", "ShaderNodeWireframe", def_sh_tex_wireframe); diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 30a91fdd424..174a6cba74b 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -110,6 +110,7 @@ set(SRC nodes/node_shader_volume_info.cc nodes/node_shader_volume_principled.cc nodes/node_shader_volume_scatter.cc + nodes/node_shader_volume_coefficients.cc nodes/node_shader_wavelength.cc nodes/node_shader_wireframe.cc diff --git a/source/blender/nodes/shader/node_shader_register.cc b/source/blender/nodes/shader/node_shader_register.cc index f8d9d327af2..9d6abe647fd 100644 --- a/source/blender/nodes/shader/node_shader_register.cc +++ b/source/blender/nodes/shader/node_shader_register.cc @@ -110,6 +110,7 @@ void register_shader_nodes() register_node_type_sh_volume_info(); register_node_type_sh_volume_principled(); register_node_type_sh_volume_scatter(); + register_node_type_sh_volume_coefficients(); register_node_type_sh_wavelength(); register_node_type_sh_wireframe(); } diff --git a/source/blender/nodes/shader/node_shader_register.hh b/source/blender/nodes/shader/node_shader_register.hh index 9f386710b4c..1cfe905f050 100644 --- a/source/blender/nodes/shader/node_shader_register.hh +++ b/source/blender/nodes/shader/node_shader_register.hh @@ -109,5 +109,6 @@ void register_node_type_sh_volume_absorption(); void register_node_type_sh_volume_info(); void register_node_type_sh_volume_principled(); void register_node_type_sh_volume_scatter(); +void register_node_type_sh_volume_coefficients(); void register_node_type_sh_wavelength(); void register_node_type_sh_wireframe(); diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index dbdc1777f33..0532fa6b254 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -969,6 +969,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node case SH_NODE_VOLUME_ABSORPTION: case SH_NODE_VOLUME_PRINCIPLED: case SH_NODE_VOLUME_SCATTER: + case SH_NODE_VOLUME_COEFFICIENTS: fromsock = ntree_shader_node_find_input(fromnode, "Weight"); /* Make "weight" sockets available so that links to it are available as well and are * not ignored in other places. */ @@ -1035,6 +1036,7 @@ static bool closure_node_filter(const bNode *node) case SH_NODE_VOLUME_ABSORPTION: case SH_NODE_VOLUME_PRINCIPLED: case SH_NODE_VOLUME_SCATTER: + case SH_NODE_VOLUME_COEFFICIENTS: return true; default: return false; diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_coefficients.cc b/source/blender/nodes/shader/nodes/node_shader_volume_coefficients.cc new file mode 100644 index 00000000000..8e86356b730 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_volume_coefficients.cc @@ -0,0 +1,159 @@ +/* SPDX-FileCopyrightText: 2005 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_shader_util.hh" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +#include "BKE_node_runtime.hh" + +namespace blender::nodes::node_shader_volume_coefficients_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.use_custom_socket_order(); + + b.add_output("Volume").translation_context(BLT_I18NCONTEXT_ID_ID); + + b.add_input("Weight").available(false); +#define SOCK_WEIGHT_ID 0 + + PanelDeclarationBuilder &abs = b.add_panel("Absorption").default_closed(false); + abs.add_input("Absorption Coefficients") + .default_value({1.0f, 1.0f, 1.0f}) + .min(0.0f) + .max(1000.0f) + .description( + "Probability density per color channel that light is absorbed per unit distance " + "traveled in the medium"); +#define SOCK_ABSORPTION_COEFFICIENTS_ID 1 + PanelDeclarationBuilder &sca = b.add_panel("Scatter").default_closed(false); + sca.add_layout([](uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { + uiItemR(layout, ptr, "phase", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + }); + sca.add_input("Scatter Coefficients") + .default_value({1.0f, 1.0f, 1.0f}) + .min(0.0f) + .max(1000.0f) + .description( + "Probability density per color channel of an out-scattering event occurring per unit " + "distance"); +#define SOCK_SCATTER_COEFFICIENTS_ID 2 + sca.add_input("Anisotropy") + .default_value(0.0f) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .description( + "Directionality of the scattering. Zero is isotropic, negative is backward, " + "positive is forward"); +#define SOCK_SCATTER_ANISOTROPY_ID 3 + sca.add_input("IOR") + .default_value(1.33f) + .min(1.0f) + .max(2.0f) + .subtype(PROP_FACTOR) + .description("Index Of Refraction of the scattering particles"); +#define SOCK_SCATTER_IOR_ID 4 + sca.add_input("Backscatter") + .default_value(0.1f) + .min(0.0f) + .max(0.5f) + .subtype(PROP_FACTOR) + .description("Fraction of light that is scattered backwards"); +#define SOCK_SCATTER_BACKSCATTER_ID 5 + sca.add_input("Alpha").default_value(0.5f).min(0.0f).max(500.0f); +#define SOCK_SCATTER_ALPHA_ID 6 + sca.add_input("Diameter") + .default_value(20.0f) + .min(0.0f) + .max(50.0f) + .description("Diameter of the water droplets, in micrometers"); +#define SOCK_SCATTER_DIAMETER_ID 7 + PanelDeclarationBuilder &emi = b.add_panel("Emission").default_closed(false); + emi.add_input("Emission Coefficients") + .default_value({0.0f, 0.0f, 0.0f}) + .min(0.0f) + .max(1000.0f) + .description("Emitted radiance per color channel that is added to a ray per unit distance"); +#define SOCK_EMISSION_COEFFICIENTS_ID 8 +} + +static void node_shader_init_coefficients(bNodeTree * /*ntree*/, bNode *node) +{ + node->custom1 = SHD_PHASE_HENYEY_GREENSTEIN; +} + +static void node_shader_update_coefficients(bNodeTree *ntree, bNode *node) +{ + const int phase_function = node->custom1; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (STR_ELEM(sock->name, "IOR", "Backscatter")) { + bke::node_set_socket_availability( + *ntree, *sock, phase_function == SHD_PHASE_FOURNIER_FORAND); + } + else if (STR_ELEM(sock->name, "Anisotropy")) { + bke::node_set_socket_availability( + *ntree, *sock, ELEM(phase_function, SHD_PHASE_HENYEY_GREENSTEIN, SHD_PHASE_DRAINE)); + } + else if (STR_ELEM(sock->name, "Alpha")) { + bke::node_set_socket_availability(*ntree, *sock, phase_function == SHD_PHASE_DRAINE); + } + else if (STR_ELEM(sock->name, "Diameter")) { + bke::node_set_socket_availability(*ntree, *sock, phase_function == SHD_PHASE_MIE); + } + } +} + +static int node_shader_gpu_volume_coefficients(GPUMaterial *mat, + bNode *node, + bNodeExecData * /*execdata*/, + GPUNodeStack *in, + GPUNodeStack *out) +{ + if (node_socket_not_black(in[SOCK_SCATTER_COEFFICIENTS_ID])) { + GPU_material_flag_set(mat, GPU_MATFLAG_VOLUME_SCATTER | GPU_MATFLAG_VOLUME_ABSORPTION); + } + if (node_socket_not_black(in[SOCK_ABSORPTION_COEFFICIENTS_ID])) { + GPU_material_flag_set(mat, GPU_MATFLAG_VOLUME_ABSORPTION); + } + return GPU_stack_link(mat, node, "node_volume_coefficients", in, out); +} + +#undef SOCK_WEIGHT_ID +#undef SOCK_ABSORPTION_COEFFICIENTS_ID +#undef SOCK_SCATTER_COEFFICIENTS_ID +#undef SOCK_SCATTER_ANISOTROPY_ID +#undef SOCK_SCATTER_IOR_ID +#undef SOCK_SCATTER_BACKSCATTER_ID +#undef SOCK_SCATTER_ALPHA_ID +#undef SOCK_SCATTER_DIAMETER_ID +#undef SOCK_EMISSION_COEFFICIENTS_ID + +} // namespace blender::nodes::node_shader_volume_coefficients_cc + +/* node type definition */ +void register_node_type_sh_volume_coefficients() +{ + namespace file_ns = blender::nodes::node_shader_volume_coefficients_cc; + + static blender::bke::bNodeType ntype; + + sh_node_type_base(&ntype, "ShaderNodeVolumeCoefficients", SH_NODE_VOLUME_COEFFICIENTS); + ntype.ui_name = "Volume Coefficients"; + ntype.ui_description = + "Model all three physical processes in a volume, represented by their coefficients"; + ntype.enum_name_legacy = "VOLUME_COEFFICIENTS"; + ntype.nclass = NODE_CLASS_SHADER; + ntype.declare = file_ns::node_declare; + ntype.add_ui_poll = object_shader_nodes_poll; + blender::bke::node_type_size_preset(ntype, blender::bke::eNodeSizePreset::Large); + ntype.initfunc = file_ns::node_shader_init_coefficients; + ntype.gpu_fn = file_ns::node_shader_gpu_volume_coefficients; + ntype.updatefunc = file_ns::node_shader_update_coefficients; + + blender::bke::node_register_type(ntype); +} diff --git a/tests/files/render/volume/cycles_renders/implicit_volume.png b/tests/files/render/volume/cycles_renders/implicit_volume.png new file mode 100644 index 00000000000..f3ffdfc2b0d --- /dev/null +++ b/tests/files/render/volume/cycles_renders/implicit_volume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da823f94d629db8b5e08b56771c4b2880455bfb97c9dd906f3266a51bf90b8e9 +size 45645 diff --git a/tests/files/render/volume/eevee_next_renders/implicit_volume.png b/tests/files/render/volume/eevee_next_renders/implicit_volume.png new file mode 100644 index 00000000000..9c1c80ad797 --- /dev/null +++ b/tests/files/render/volume/eevee_next_renders/implicit_volume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fef36fe90ddb0b5bda77d2dd27e7b25a5f59c24dd650a90f4292649d817d4621 +size 35428 diff --git a/tests/files/render/volume/implicit_volume.blend b/tests/files/render/volume/implicit_volume.blend new file mode 100644 index 00000000000..5f0731b0e47 --- /dev/null +++ b/tests/files/render/volume/implicit_volume.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb26d28bf50f45ad1685dcd651a28f1ccdb009274e8ecc4ebfdf5c2d90a36321 +size 1148670 diff --git a/tests/files/render/volume/storm_hydra_renders/implicit_volume.png b/tests/files/render/volume/storm_hydra_renders/implicit_volume.png new file mode 100644 index 00000000000..29cd523c482 --- /dev/null +++ b/tests/files/render/volume/storm_hydra_renders/implicit_volume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c95a03feeb04033214a57e70ebe1c610a31adc0f8abcd59c1f665142ade55c1 +size 5643 diff --git a/tests/files/render/volume/storm_usd_renders/implicit_volume.png b/tests/files/render/volume/storm_usd_renders/implicit_volume.png new file mode 100644 index 00000000000..a3bc32c0b64 --- /dev/null +++ b/tests/files/render/volume/storm_usd_renders/implicit_volume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e7e1083a1f335dad95444d5fe591703cc7f4cb982df0eb295465137d500b0c5 +size 5643 diff --git a/tests/files/render/volume/workbench_renders/implicit_volume.png b/tests/files/render/volume/workbench_renders/implicit_volume.png new file mode 100644 index 00000000000..2e66db38560 --- /dev/null +++ b/tests/files/render/volume/workbench_renders/implicit_volume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c38eafb2ca118a372b882c4afa35bd1d1b6f0f8a5efd6447f782cc153294aff4 +size 16317