diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index e318b09d87c..52ecbb8d932 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -548,6 +548,35 @@ static ShaderNode *add_node(Scene *scene, node = subsurface; } + else if (b_node.is_a(&RNA_ShaderNodeBsdfConductor)) { + BL::ShaderNodeBsdfConductor b_conductor_node(b_node); + ConductorBsdfNode *conductor = graph->create_node(); + + switch (b_conductor_node.distribution()) { + case BL::ShaderNodeBsdfConductor::distribution_BECKMANN: + conductor->set_distribution(CLOSURE_BSDF_MICROFACET_BECKMANN_ID); + break; + case BL::ShaderNodeBsdfConductor::distribution_GGX: + conductor->set_distribution(CLOSURE_BSDF_MICROFACET_GGX_ID); + break; + case BL::ShaderNodeBsdfConductor::distribution_MULTI_GGX: + conductor->set_distribution(CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); + break; + } + + switch (b_conductor_node.fresnel_type()) { + case BL::ShaderNodeBsdfConductor::fresnel_type_ARTISTIC_CONDUCTOR: + conductor->set_fresnel_type(CLOSURE_BSDF_ARTISTIC_CONDUCTOR); + break; + case BL::ShaderNodeBsdfConductor::fresnel_type_CONDUCTOR: + conductor->set_fresnel_type(CLOSURE_BSDF_CONDUCTOR); + break; + case BL::ShaderNodeBsdfConductor::fresnel_type_F82: + conductor->set_fresnel_type(CLOSURE_BSDF_CONDUCTOR_F82); + break; + } + node = conductor; + } else if (b_node.is_a(&RNA_ShaderNodeBsdfAnisotropic)) { BL::ShaderNodeBsdfAnisotropic b_glossy_node(b_node); GlossyBsdfNode *glossy = graph->create_node(); diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index 60a958028f2..7bbc4e8fb2e 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -74,6 +74,43 @@ ccl_device Spectrum fresnel_conductor(float cosi, const Spectrum eta, const Spec return (Rparl2 + Rperp2) * 0.5f; } +ccl_device float3 conductor_ior_from_color(float3 r, const float3 edge_tint) +{ + r = clamp(r, zero_float3(), make_float3(0.99)); + const float3 r_sqrt = sqrt(r); + const float3 one = one_float3(); + + const float3 n_min = (one - r) / (one + r); + const float3 n_max = (one + r_sqrt) / (one - r_sqrt); + + return mix(n_max, n_min, edge_tint); +} + +ccl_device float3 conductor_extinction_from_color(float3 r, const float3 eta) +{ + r = clamp(r, zero_float3(), make_float3(0.99)); + const float3 one = one_float3(); + + const float3 np1 = eta + one; + const float3 nm1 = eta - one; + float3 k2 = ((r * np1 * np1) - (nm1 * nm1)) / (one - r); + k2 = max(k2, zero_float3()); + + return sqrt(k2); +} + +ccl_device void complex_ior_from_base_edge(const float3 reflectivity, + const float3 edge_tint, + ccl_private float3 *eta, + ccl_private float3 *k) +{ + /* Equations from "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014 + * https://jcgt.org/published/0003/04/03/paper.pdf */ + + *eta = conductor_ior_from_color(reflectivity, edge_tint); + *k = conductor_extinction_from_color(reflectivity, *eta); +} + ccl_device float ior_from_F0(float f0) { const float sqrt_f0 = sqrtf(clamp(f0, 0.0f, 0.99f)); diff --git a/intern/cycles/kernel/osl/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt index 3f2c2fc294f..2fe0655a44e 100644 --- a/intern/cycles/kernel/osl/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt @@ -20,6 +20,7 @@ set(SRC_OSL node_combine_rgb.osl node_combine_hsv.osl node_combine_xyz.osl + node_conductor_bsdf.osl node_convert_from_color.osl node_convert_from_float.osl node_convert_from_int.osl diff --git a/intern/cycles/kernel/osl/shaders/node_conductor_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_conductor_bsdf.osl new file mode 100644 index 00000000000..ca3d90f7926 --- /dev/null +++ b/intern/cycles/kernel/osl/shaders/node_conductor_bsdf.osl @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: Apache-2.0 */ + +#include "node_fresnel.h" +#include "stdcycles.h" + +shader node_conductor_bsdf(color BaseColor = 1.0, + color EdgeTint = 1.0, + vector IOR = vector(0.183, 0.421, 1.373), + vector Extinction = vector(3.424, 2.346, 1.770), + string distribution = "multi_ggx", + string fresnel_type = "conductor", + float Roughness = 0.5, + float Anisotropy = 0.0, + float Rotation = 0.0, + normal Normal = N, + normal Tangent = 0.0, + output closure color BSDF = 0) +{ + float r2 = clamp(Roughness, 0.0, 1.0); + r2 *= r2; + float alpha_x = r2, alpha_y = r2; + + /* Handle anisotropy. */ + vector T = Tangent; + if (Anisotropy > 0.0) { + float aspect = sqrt(1.0 - clamp(Anisotropy, 0.0, 1.0) * 0.9); + alpha_x /= aspect; + alpha_y *= aspect; + if (Rotation != 0.0) + T = rotate(T, Rotation * M_2PI, point(0.0, 0.0, 0.0), Normal); + } + + color F0 = clamp(BaseColor, color(0.0), color(1.0)); + color F82 = clamp(EdgeTint, color(0.0), color(1.0)); + + if (fresnel_type == "f82") { + BSDF = microfacet_f82_tint(distribution, Normal, T, alpha_x, alpha_y, F0, F82); + } + else { + vector n, k; + if (fresnel_type == "conductor") { + n = max(IOR, 0.0); + k = max(Extinction, 0.0); + } + else { + n = conductor_ior_from_color(F0, F82); + k = conductor_extinction_from_color(F0, n); + } + + BSDF = conductor_bsdf(Normal, T, alpha_x, alpha_y, n, k, distribution); + } +} diff --git a/intern/cycles/kernel/osl/shaders/node_fresnel.h b/intern/cycles/kernel/osl/shaders/node_fresnel.h index c0af4fc81c8..c3125e01f51 100644 --- a/intern/cycles/kernel/osl/shaders/node_fresnel.h +++ b/intern/cycles/kernel/osl/shaders/node_fresnel.h @@ -37,6 +37,36 @@ color fresnel_conductor(float cosi, color eta, color k) return (Rparl2 + Rperp2) * 0.5; } +vector conductor_ior_from_color(color reflectivity, color edge_tint) +{ + /* "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014 + * https://jcgt.org/published/0003/04/03/paper.pdf */ + + vector r = clamp(reflectivity, 0.0, 0.99); + vector r_sqrt = sqrt(r); + vector one = 1.0; + + vector n_min = (one - r) / (one + r); + vector n_max = (one + r_sqrt) / (one - r_sqrt); + + return mix(n_max, n_min, edge_tint); +} + +vector conductor_extinction_from_color(color reflectivity, vector n) +{ + /* "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014 + * https://jcgt.org/published/0003/04/03/paper.pdf */ + + vector r = clamp(reflectivity, 0.0, 0.99); + + vector np1 = n + 1.0; + vector nm1 = n - 1.0; + vector k2 = ((r * np1 * np1) - (nm1 * nm1)) / (1.0 - r); + k2 = max(k2, 0.0); + + return sqrt(k2); +} + float F0_from_ior(float eta) { float f0 = (eta - 1.0) / (eta + 1.0); diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index a19e371779a..ada7f3bd35d 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -454,6 +454,85 @@ ccl_device bsdf_transparent_setup(sd, weight, path_flag); break; } + case CLOSURE_BSDF_CONDUCTOR: + case CLOSURE_BSDF_CONDUCTOR_F82: + case CLOSURE_BSDF_ARTISTIC_CONDUCTOR: { +#ifdef __CAUSTICS_TRICKS__ + if (!kernel_data.integrator.caustics_reflective && (path_flag & PATH_RAY_DIFFUSE)) + break; +#endif + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(make_float3(mix_weight))); + + if (!(bsdf == NULL)) { + uint base_ior_offest, edge_tint_k_offset, rotation_offset, tangent_offset; + svm_unpack_node_uchar4( + node.z, &base_ior_offest, &edge_tint_k_offset, &rotation_offset, &tangent_offset); + + float3 valid_reflection_N = maybe_ensure_valid_specular_reflection(sd, N); + float3 T = stack_load_float3(stack, tangent_offset); + const float anisotropy = saturatef(param2); + const float roughness = saturatef(param1); + float alpha_x = sqr(roughness), alpha_y = sqr(roughness); + if (anisotropy > 0.0f) { + float aspect = sqrtf(1.0f - anisotropy * 0.9f); + alpha_x /= aspect; + alpha_y *= aspect; + float anisotropic_rotation = stack_load_float(stack, rotation_offset); + if (anisotropic_rotation != 0.0f) + T = rotate_around_axis(T, N, anisotropic_rotation * M_2PI_F); + } + + bsdf->N = valid_reflection_N; + bsdf->ior = 1.0f; + bsdf->T = T; + bsdf->alpha_x = alpha_x; + bsdf->alpha_y = alpha_y; + + ClosureType distribution = (ClosureType)node.w; + /* setup bsdf */ + if (distribution == CLOSURE_BSDF_MICROFACET_BECKMANN_ID) { + sd->flag |= bsdf_microfacet_beckmann_setup(bsdf); + } + else { + sd->flag |= bsdf_microfacet_ggx_setup(bsdf); + } + + const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); + + if (type == CLOSURE_BSDF_CONDUCTOR || type == CLOSURE_BSDF_ARTISTIC_CONDUCTOR) { + ccl_private FresnelConductor *fresnel = (ccl_private FresnelConductor *) + closure_alloc_extra(sd, sizeof(FresnelConductor)); + + float3 n, k; + if (type == CLOSURE_BSDF_CONDUCTOR) { + n = max(stack_load_float3(stack, base_ior_offest), zero_float3()); + k = max(stack_load_float3(stack, edge_tint_k_offset), zero_float3()); + } + else { + const float3 color = saturate(stack_load_float3(stack, base_ior_offest)); + const float3 tint = saturate(stack_load_float3(stack, edge_tint_k_offset)); + complex_ior_from_base_edge(color, tint, &n, &k); + } + + fresnel->n = rgb_to_spectrum(n); + fresnel->k = rgb_to_spectrum(k); + bsdf_microfacet_setup_fresnel_conductor(kg, bsdf, sd, fresnel, is_multiggx); + } + else { + ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)closure_alloc_extra( + sd, sizeof(FresnelF82Tint)); + + const float3 color = saturate(stack_load_float3(stack, base_ior_offest)); + const float3 tint = saturate(stack_load_float3(stack, edge_tint_k_offset)); + + fresnel->f0 = rgb_to_spectrum(color); + const Spectrum f82 = rgb_to_spectrum(tint); + bsdf_microfacet_setup_fresnel_f82_tint(kg, bsdf, sd, fresnel, f82, is_multiggx); + } + } + break; + } case CLOSURE_BSDF_MICROFACET_GGX_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index 90421ac1860..e39f4467334 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -426,6 +426,9 @@ typedef enum ClosureType { CLOSURE_BSDF_TRANSLUCENT_ID, /* Glossy */ + CLOSURE_BSDF_CONDUCTOR, /* virtual closure */ + CLOSURE_BSDF_ARTISTIC_CONDUCTOR, /* virtual closure */ + CLOSURE_BSDF_CONDUCTOR_F82, /* virtual closure */ CLOSURE_BSDF_MICROFACET_GGX_ID, CLOSURE_BSDF_MICROFACET_BECKMANN_ID, CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID, /* virtual closure */ diff --git a/intern/cycles/scene/shader_graph.cpp b/intern/cycles/scene/shader_graph.cpp index 20cf3a38d09..16edc8f239d 100644 --- a/intern/cycles/scene/shader_graph.cpp +++ b/intern/cycles/scene/shader_graph.cpp @@ -1197,7 +1197,10 @@ int ShaderGraph::get_num_closures() * for the volume steps. */ num_closures += MAX_VOLUME_STACK_SIZE; } - else if (closure_type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID || + else if (closure_type == CLOSURE_BSDF_CONDUCTOR || + closure_type == CLOSURE_BSDF_ARTISTIC_CONDUCTOR || + closure_type == CLOSURE_BSDF_CONDUCTOR_F82 || + closure_type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID || closure_type == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID || closure_type == CLOSURE_BSDF_HAIR_CHIANG_ID || closure_type == CLOSURE_BSDF_HAIR_HUANG_ID) diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 304eab9ddbe..74f678c9b37 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -2327,6 +2327,122 @@ void BsdfNode::compile(OSLCompiler & /*compiler*/) assert(0); } +/* Conductor BSDF Closure */ + +NODE_DEFINE(ConductorBsdfNode) +{ + NodeType *type = NodeType::add("Conductor_bsdf", create, NodeType::SHADER); + + SOCKET_IN_COLOR(color, "Base Color", make_float3(1.0f, 1.0f, 1.0f)); + SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); + SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); + + static NodeEnum distribution_enum; + distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID); + distribution_enum.insert("ggx", CLOSURE_BSDF_MICROFACET_GGX_ID); + distribution_enum.insert("multi_ggx", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); + SOCKET_ENUM( + distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); + + static NodeEnum fresnel_type_enum; + fresnel_type_enum.insert("f82", CLOSURE_BSDF_CONDUCTOR_F82); + fresnel_type_enum.insert("artist_conductor", CLOSURE_BSDF_ARTISTIC_CONDUCTOR); + fresnel_type_enum.insert("conductor", CLOSURE_BSDF_CONDUCTOR); + SOCKET_ENUM(fresnel_type, "fresnel_type", fresnel_type_enum, CLOSURE_BSDF_ARTISTIC_CONDUCTOR); + + SOCKET_IN_COLOR(edge_tint, "Edge Tint", make_float3(1.0f, 1.0f, 1.0f)); + + SOCKET_IN_VECTOR(ior, "IOR", make_float3(0.183f, 0.421f, 1.373f)); + SOCKET_IN_VECTOR(k, "Extinction", make_float3(3.424f, 2.346f, 1.770f)); + + SOCKET_IN_VECTOR(tangent, "Tangent", zero_float3(), SocketType::LINK_TANGENT); + + SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); + SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); + SOCKET_IN_FLOAT(rotation, "Rotation", 0.0f); + + SOCKET_OUT_CLOSURE(BSDF, "BSDF"); + + return type; +} + +ConductorBsdfNode::ConductorBsdfNode() : BsdfNode(get_node_type()) +{ + closure = CLOSURE_BSDF_CONDUCTOR; +} + +bool ConductorBsdfNode::is_isotropic() +{ + ShaderInput *anisotropy_input = input("Anisotropy"); + /* Keep in sync with the thresholds in OSL's node_conductor_bsdf and SVM's svm_node_closure_bsdf. + */ + return (!anisotropy_input->link && fabsf(anisotropy) <= 1e-4f); +} + +void ConductorBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) +{ + if (shader->has_surface_link()) { + ShaderInput *tangent_in = input("Tangent"); + if (!tangent_in->link && !is_isotropic()) { + attributes->add(ATTR_STD_GENERATED); + } + } + + ShaderNode::attributes(shader, attributes); +} + +void ConductorBsdfNode::simplify_settings(Scene * /* scene */) +{ + /* If the anisotropy is close enough to zero, fall back to the isotropic case. */ + ShaderInput *tangent_input = input("Tangent"); + if (tangent_input->link && is_isotropic()) { + tangent_input->disconnect(); + } +} + +void ConductorBsdfNode::compile(SVMCompiler &compiler) +{ + compiler.add_node(NODE_CLOSURE_SET_WEIGHT, one_float3()); + + ShaderInput *base_color_in = input("Base Color"); + ShaderInput *edge_tint_in = input("Edge Tint"); + ShaderInput *ior_in = input("IOR"); + ShaderInput *k_in = input("Extinction"); + + int base_color_ior_offset = fresnel_type == CLOSURE_BSDF_CONDUCTOR ? + compiler.stack_assign(ior_in) : + compiler.stack_assign(base_color_in); + int edge_tint_k_offset = fresnel_type == CLOSURE_BSDF_CONDUCTOR ? + compiler.stack_assign(k_in) : + compiler.stack_assign(edge_tint_in); + + ShaderInput *anisotropy_in = input("Anisotropy"); + ShaderInput *rotation_in = input("Rotation"); + ShaderInput *roughness_in = input("Roughness"); + ShaderInput *tangent_in = input("Tangent"); + + int normal_offset = compiler.stack_assign_if_linked(input("Normal")); + + compiler.add_node(NODE_CLOSURE_BSDF, + compiler.encode_uchar4(fresnel_type, + compiler.stack_assign(roughness_in), + compiler.stack_assign(anisotropy_in), + compiler.closure_mix_weight_offset()), + compiler.encode_uchar4(base_color_ior_offset, + edge_tint_k_offset, + compiler.stack_assign(rotation_in), + compiler.stack_assign(tangent_in)), + distribution); + compiler.add_node(normal_offset); +} + +void ConductorBsdfNode::compile(OSLCompiler &compiler) +{ + compiler.parameter(this, "distribution"); + compiler.parameter(this, "fresnel_type"); + compiler.add(this, "node_conductor_bsdf"); +} + /* Glossy BSDF Closure */ NODE_DEFINE(GlossyBsdfNode) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 842a7feb5b2..3b13a1da254 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -574,6 +574,35 @@ class SheenBsdfNode : public BsdfNode { } }; +class ConductorBsdfNode : public BsdfNode { + public: + SHADER_NODE_CLASS(ConductorBsdfNode) + + void simplify_settings(Scene *scene); + ClosureType get_closure_type() + { + return closure; + } + + NODE_SOCKET_API(float3, edge_tint) + NODE_SOCKET_API(float3, ior) + NODE_SOCKET_API(float3, k) + NODE_SOCKET_API(float3, tangent) + NODE_SOCKET_API(float, roughness) + NODE_SOCKET_API(float, anisotropy) + NODE_SOCKET_API(float, rotation) + NODE_SOCKET_API(ClosureType, distribution) + NODE_SOCKET_API(ClosureType, fresnel_type) + + void attributes(Shader *shader, AttributeRequestSet *attributes); + bool has_attribute_dependency() + { + return true; + } + + bool is_isotropic(); +}; + class GlossyBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(GlossyBsdfNode) diff --git a/intern/cycles/util/math_float3.h b/intern/cycles/util/math_float3.h index 38f86de6054..262feb0ae3a 100644 --- a/intern/cycles/util/math_float3.h +++ b/intern/cycles/util/math_float3.h @@ -341,6 +341,11 @@ ccl_device_inline float3 mix(const float3 a, const float3 b, float t) return a + t * (b - a); } +ccl_device_inline float3 mix(const float3 a, const float3 b, float3 t) +{ + return make_float3(mix(a.x, b.x, t.x), mix(a.y, b.y, t.y), mix(a.z, b.z, t.z)); +} + ccl_device_inline float3 rcp(const float3 a) { # ifdef __KERNEL_SSE__ diff --git a/scripts/startup/bl_ui/node_add_menu_shader.py b/scripts/startup/bl_ui/node_add_menu_shader.py index b1a530510cd..36e92d71907 100644 --- a/scripts/startup/bl_ui/node_add_menu_shader.py +++ b/scripts/startup/bl_ui/node_add_menu_shader.py @@ -137,6 +137,11 @@ class NODE_MT_category_shader_shader(Menu): "ShaderNodeBackground", poll=world_shader_nodes_poll(context), ) + node_add_menu.add_node_type( + layout, + "ShaderNodeBsdfConductor", + poll=object_shader_nodes_poll(context), + ) node_add_menu.add_node_type( layout, "ShaderNodeBsdfDiffuse", diff --git a/source/blender/blenkernel/BKE_node.hh b/source/blender/blenkernel/BKE_node.hh index 43580ee1b94..83d5f648b5f 100644 --- a/source/blender/blenkernel/BKE_node.hh +++ b/source/blender/blenkernel/BKE_node.hh @@ -974,6 +974,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i #define SH_NODE_COMBINE_COLOR 711 #define SH_NODE_SEPARATE_COLOR 712 #define SH_NODE_MIX 713 +#define SH_NODE_BSDF_CONDUCTOR 714 /** \} */ diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index b265fab17b4..e8891ffc82b 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -506,6 +506,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_combine_hsv.glsl shaders/material/gpu_shader_material_combine_rgb.glsl shaders/material/gpu_shader_material_combine_xyz.glsl + shaders/material/gpu_shader_material_conductor.glsl shaders/material/gpu_shader_material_diffuse.glsl shaders/material/gpu_shader_material_displacement.glsl shaders/material/gpu_shader_material_eevee_specular.glsl diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_conductor.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_conductor.glsl new file mode 100644 index 00000000000..b02cd635180 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_conductor.glsl @@ -0,0 +1,63 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +vec4 fresnel_conductor(float cosi, const vec3 eta, const vec3 k) +{ + const vec4 cosiv4 = vec4(cosi); + const vec4 etav4 = vec4(eta, 1.0); + const vec4 kv4 = vec4(k, 1.0); + + const vec4 cosi2 = vec4(cosi * cosi); + const vec4 one = vec4(1.0); + const vec4 tmp_f = (etav4 * etav4) + (kv4 * kv4); + + const vec4 tmp = tmp_f * cosi2; + const vec4 Rparl2 = (tmp - (2.0 * etav4 * cosiv4) + one) / (tmp + (2.0 * etav4 * cosiv4) + one); + const vec4 Rperp2 = (tmp_f - (2.0 * etav4 * cosiv4) + cosi2) / + (tmp_f + (2.0 * etav4 * cosiv4) + cosi2); + return (Rparl2 + Rperp2) * 0.5; +} + +void node_bsdf_conductor(vec4 base_color, + vec4 edge_tint, + vec3 ior, + vec3 extinction, + float roughness, + float anisotropy, + float rotation, + vec3 N, + vec3 T, + float weight, + const float do_multiscatter, + const float use_complex_ior, + out Closure result) +{ + if (use_complex_ior != 0.0) { + base_color = fresnel_conductor(1.0, ior, extinction); + edge_tint = fresnel_conductor(1.0 / 7.0, ior, extinction); + } + + /* Clamp to match Cycles */ + base_color = saturate(base_color); + edge_tint = saturate(edge_tint); + roughness = saturate(roughness); + /* Not used by EEVEE */ + /* anisotropy = saturate(anisotropy); */ + + N = safe_normalize(N); + vec3 V = coordinate_incoming(g_data.P); + float NV = dot(N, V); + + ClosureReflection reflection_data; + reflection_data.N = N; + reflection_data.roughness = roughness; + vec3 F0 = base_color.rgb; + vec3 F82 = edge_tint.rgb; + vec3 metallic_brdf; + brdf_f82_tint_lut(F0, F82, NV, roughness, do_multiscatter != 0.0, metallic_brdf); + reflection_data.color = metallic_brdf; + reflection_data.weight = weight; + + result = closure_eval(reflection_data); +} diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 370e6c8da1e..bd3c65cd007 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2032,6 +2032,13 @@ enum { CMP_NODE_CHANNEL_MATTE_CS_YCC = 4, }; +/* Conductive fresnel types */ +enum { + SHD_CONDUCTOR = 0, + SHD_ARTISTIC_CONDUCTOR = 1, + SHD_CONDUCTOR_F82 = 2, +}; + /* glossy distributions */ enum { SHD_GLOSSY_BECKMANN = 0, diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 0b7971d3cdd..e3509b45c21 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -4048,6 +4048,29 @@ static const EnumPropertyItem node_ycc_items[] = { {0, nullptr, 0, nullptr, nullptr}, }; +static const EnumPropertyItem node_conductor_distrobution_items[] = { + {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""}, + {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""}, + {SHD_GLOSSY_MULTI_GGX, + "MULTI_GGX", + 0, + "Multiscatter GGX", + "GGX with additional correction to account for multiple scattering, preserve energy and " + "prevent unexpected darkening at high roughness"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + +static const EnumPropertyItem node_conductor_fresnel_type_items[] = { + {SHD_CONDUCTOR, "CONDUCTOR", 0, "Conductor Fresnel", ""}, + {SHD_ARTISTIC_CONDUCTOR, + "ARTISTIC_CONDUCTOR", + 0, + "Conductor Fresnel - Artistic", + "Conductive Fresnel with artist friendly color inputs"}, + {SHD_CONDUCTOR_F82, "F82", 0, "F82 Tint Fresnel", ""}, + {0, nullptr, 0, nullptr, nullptr}, +}; + static const EnumPropertyItem node_glossy_items[] = { {SHD_GLOSSY_BECKMANN, "BECKMANN", 0, "Beckmann", ""}, {SHD_GLOSSY_GGX, "GGX", 0, "GGX", ""}, @@ -5473,6 +5496,23 @@ static void def_sh_tex_pointdensity(StructRNA *srna) RNA_def_function_output(func, parm); } +static void def_conductor(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "distribution", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "custom1"); + RNA_def_property_enum_items(prop, node_conductor_distrobution_items); + RNA_def_property_ui_text(prop, "Distribution", "Light scattering distribution on rough surface"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "fresnel_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "custom2"); + RNA_def_property_enum_items(prop, node_conductor_fresnel_type_items); + RNA_def_property_ui_text(prop, "Fresnel Type", "PLACE HOLDER"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_glossy(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 9d11295c250..8ee1b0b2b89 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -61,6 +61,7 @@ DefNode(ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATT DefNode(ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "Compute how much the hemisphere above the shading point is occluded, for example to add weathering effects to corners.\nNote: For Cycles, this may slow down renders significantly") DefNode(ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "Add background light emission.\nNote: This node should only be used for the world surface output") DefNode(ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "Create a \"hole\" in the image with zero alpha transparency, which is useful for compositing.\nNote: the holdout shader can only create alpha when transparency is enabled in the film settings") +DefNode(ShaderNode, SH_NODE_BSDF_CONDUCTOR, def_conductor, "BSDF_CONDUCTOR", BsdfConductor, "Conductor BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors") DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "Lambertian and Oren-Nayar diffuse reflection") DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials, based on the Disney principled model also known as the \"PBR\" shader") DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfAnisotropic, "Glossy BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors") diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 5578c5f4a7b..26030356c92 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -34,6 +34,7 @@ set(SRC nodes/node_shader_bsdf_diffuse.cc nodes/node_shader_bsdf_glass.cc nodes/node_shader_bsdf_glossy.cc + nodes/node_shader_bsdf_conductor.cc nodes/node_shader_bsdf_hair.cc nodes/node_shader_bsdf_hair_principled.cc nodes/node_shader_bsdf_principled.cc diff --git a/source/blender/nodes/shader/node_shader_register.cc b/source/blender/nodes/shader/node_shader_register.cc index 24831da5f70..044a26af029 100644 --- a/source/blender/nodes/shader/node_shader_register.cc +++ b/source/blender/nodes/shader/node_shader_register.cc @@ -19,6 +19,7 @@ void register_shader_nodes() register_node_type_sh_bevel(); register_node_type_sh_blackbody(); register_node_type_sh_brightcontrast(); + register_node_type_sh_bsdf_conductor(); register_node_type_sh_bsdf_diffuse(); register_node_type_sh_bsdf_glass(); register_node_type_sh_bsdf_glossy(); diff --git a/source/blender/nodes/shader/node_shader_register.hh b/source/blender/nodes/shader/node_shader_register.hh index 268720045c5..296152b2e9a 100644 --- a/source/blender/nodes/shader/node_shader_register.hh +++ b/source/blender/nodes/shader/node_shader_register.hh @@ -15,6 +15,7 @@ void register_node_type_sh_background(); void register_node_type_sh_bevel(); void register_node_type_sh_blackbody(); void register_node_type_sh_brightcontrast(); +void register_node_type_sh_bsdf_conductor(); void register_node_type_sh_bsdf_diffuse(); void register_node_type_sh_bsdf_glass(); void register_node_type_sh_bsdf_glossy(); diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index 3d0831dc113..60d86bf1b06 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -916,6 +916,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node break; } case SH_NODE_BACKGROUND: + case SH_NODE_BSDF_CONDUCTOR: case SH_NODE_BSDF_DIFFUSE: case SH_NODE_BSDF_GLASS: case SH_NODE_BSDF_GLOSSY: @@ -973,6 +974,7 @@ static bool closure_node_filter(const bNode *node) case SH_NODE_ADD_SHADER: case SH_NODE_MIX_SHADER: case SH_NODE_BACKGROUND: + case SH_NODE_BSDF_CONDUCTOR: case SH_NODE_BSDF_DIFFUSE: case SH_NODE_BSDF_GLASS: case SH_NODE_BSDF_GLOSSY: diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_conductor.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_conductor.cc new file mode 100644 index 00000000000..ec62a5460c7 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_conductor.cc @@ -0,0 +1,159 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_shader_util.hh" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +namespace blender::nodes::node_shader_bsdf_conductor_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Base Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("Edge Tint").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input("IOR") + .default_value({0.183f, 0.421f, 1.373f}) + .min(0.0f) + .max(100.0f) + .description("PLACEHOLDER"); + b.add_input("Extinction") + .default_value({3.424f, 2.346f, 1.770f}) + .min(0.0f) + .max(100.0f) + .description("PLACE HOLDER"); + b.add_input("Roughness") + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input("Anisotropy") + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input("Rotation") + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input("Normal").hide_value(); + b.add_input("Tangent").hide_value(); + b.add_input("Weight").unavailable(); + b.add_output("BSDF"); +} + +static void node_shader_buts_conductor(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "fresnel_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void node_shader_init_conductor(bNodeTree * /*ntree*/, bNode *node) +{ + node->custom1 = SHD_GLOSSY_MULTI_GGX; + node->custom2 = SHD_ARTISTIC_CONDUCTOR; +} + +static int node_shader_gpu_bsdf_conductor(GPUMaterial *mat, + bNode *node, + bNodeExecData * /*execdata*/, + GPUNodeStack *in, + GPUNodeStack *out) +{ + if (!in[7].link) { + GPU_link(mat, "world_normals_get", &in[7].link); + } + + GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY); + + float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; + float use_complex_ior = (node->custom2 == SHD_CONDUCTOR) ? 1.0f : 0.0f; + + return GPU_stack_link(mat, + node, + "node_bsdf_conductor", + in, + out, + GPU_constant(&use_multi_scatter), + GPU_constant(&use_complex_ior)); +} + +static void node_shader_update_conductor(bNodeTree *ntree, bNode *node) +{ + const int fresnel_method = node->custom2; + + bke::nodeSetSocketAvailability( + ntree, nodeFindSocket(node, SOCK_IN, "Base Color"), fresnel_method != SHD_CONDUCTOR); + bke::nodeSetSocketAvailability( + ntree, nodeFindSocket(node, SOCK_IN, "Edge Tint"), fresnel_method != SHD_CONDUCTOR); + bke::nodeSetSocketAvailability( + ntree, nodeFindSocket(node, SOCK_IN, "IOR"), fresnel_method == SHD_CONDUCTOR); + bke::nodeSetSocketAvailability( + ntree, nodeFindSocket(node, SOCK_IN, "Extinction"), fresnel_method == SHD_CONDUCTOR); +} + +NODE_SHADER_MATERIALX_BEGIN +#ifdef WITH_MATERIALX +{ + if (to_type_ != NodeItem::Type::BSDF) { + return empty(); + } + + NodeItem color = get_input_value("Base Color", NodeItem::Type::Color3); + NodeItem edge_tint = get_input_value("Edge Tint", NodeItem::Type::Color3); + NodeItem roughness = get_input_value("Roughness", NodeItem::Type::Vector2); + NodeItem anisotropy = get_input_value("Anisotropy", NodeItem::Type::Color3); + NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3); + NodeItem tangent = get_input_link("Tangent", NodeItem::Type::Vector3); + + /* TODO: Figure out how to switch between artisitic_ior and using + * the IOR and Extinction values directly. + * `node` is not defined here so we can't use `node->custom2` */ + + /* if (node->custom2 == SHD_CONDUCTOR) { + USE_IOR_AND_EXTINCTION; + } + else { + USE ARTISTIC_IOR; + }*/ + + NodeItem artistic_ior = create_node("artistic_ior", + NodeItem::Type::Multioutput, + {{"reflectivity", color}, {"edge_color", edge_tint}}); + NodeItem ior_out = artistic_ior.add_output("ior", NodeItem::Type::Color3); + NodeItem extinction_out = artistic_ior.add_output("extinction", NodeItem::Type::Color3); + + return create_node("conductor_bsdf", + NodeItem::Type::BSDF, + {{"normal", normal}, + {"tangent", tangent}, + {"ior", ior_out}, + {"extinction", extinction_out}, + {"roughness", roughness}}); +} +#endif +NODE_SHADER_MATERIALX_END + +} // namespace blender::nodes::node_shader_bsdf_conductor_cc + +/* node type definition */ +void register_node_type_sh_bsdf_conductor() +{ + namespace file_ns = blender::nodes::node_shader_bsdf_conductor_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_BSDF_CONDUCTOR, "Conductor BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + ntype.add_ui_poll = object_shader_nodes_poll; + ntype.draw_buttons = file_ns::node_shader_buts_conductor; + blender::bke::node_type_size_preset(&ntype, blender::bke::eNodeSizePreset::LARGE); + ntype.initfunc = file_ns::node_shader_init_conductor; + ntype.gpu_fn = file_ns::node_shader_gpu_bsdf_conductor; + ntype.updatefunc = file_ns::node_shader_update_conductor; + ntype.materialx_fn = file_ns::node_shader_materialx; + + nodeRegisterType(&ntype); +}