From cf92af3ac44fd5e57fc3790c070e635f4abfe41a Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Wed, 9 Jul 2025 22:07:24 +0200 Subject: [PATCH] Cycles: Support Thin Film iridescence in the Glass BSDF Supporting this on the Metallic BSDF will require some extra work, and on the Glossy BSDF it doesn't make much sense conceptually (for that kind of shader setup, we'll want to support layering in SVM), but Glass BSDF just needs to be hooked up so might as well do that. Pull Request: https://projects.blender.org/blender/blender/pulls/140832 --- .../kernel/osl/shaders/node_glass_bsdf.osl | 19 ++++++++- intern/cycles/kernel/svm/closure.h | 9 ++-- intern/cycles/scene/shader_nodes.cpp | 10 ++++- intern/cycles/scene/shader_nodes.h | 2 + .../shader/nodes/node_shader_bsdf_glass.cc | 41 +++++++++++++++---- 5 files changed, 67 insertions(+), 14 deletions(-) diff --git a/intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl index d9c619e59fa..280b67edca3 100644 --- a/intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl +++ b/intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl @@ -9,6 +9,8 @@ shader node_glass_bsdf(color Color = 0.8, string distribution = "ggx", float Roughness = 0.2, float IOR = 1.45, + float ThinFilmThickness = 0.0, + float ThinFilmIOR = 1.33, normal Normal = N, output closure color BSDF = 0) { @@ -17,9 +19,22 @@ shader node_glass_bsdf(color Color = 0.8, r2 = r2 * r2; float eta = max(IOR, 1e-5); eta = backfacing() ? 1.0 / eta : eta; + float thinfilm_ior = backfacing() ? ThinFilmIOR / eta : ThinFilmIOR; color F0 = F0_from_ior(eta); color F90 = color(1.0); - BSDF = generalized_schlick_bsdf( - Normal, vector(0.0), base_color, base_color, r2, r2, F0, F90, -eta, distribution); + BSDF = generalized_schlick_bsdf(Normal, + vector(0.0), + base_color, + base_color, + r2, + r2, + F0, + F90, + -eta, + distribution, + "thinfilm_thickness", + ThinFilmThickness, + "thinfilm_ior", + thinfilm_ior); } diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index 4e1e472135e..91b2da4fa26 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -715,6 +715,10 @@ ccl_device const bool reflective_caustics = true; const bool refractive_caustics = true; #endif + + const float thinfilm_thickness = fmaxf(stack_load_float(stack, data_node.z), 1e-5f); + const float thinfilm_ior = fmaxf(stack_load_float(stack, data_node.w), 1e-5f); + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), make_spectrum(mix_weight)); ccl_private FresnelGeneralizedSchlick *fresnel = @@ -737,9 +741,8 @@ ccl_device fresnel->reflection_tint = reflective_caustics ? rgb_to_spectrum(color) : zero_spectrum(); fresnel->transmission_tint = refractive_caustics ? rgb_to_spectrum(color) : zero_spectrum(); - fresnel->thin_film.thickness = 0.0f; - fresnel->thin_film.ior = 0.0f; - + fresnel->thin_film.thickness = thinfilm_thickness; + fresnel->thin_film.ior = (sd->flag & SD_BACKFACING) ? thinfilm_ior / ior : thinfilm_ior; /* setup bsdf */ if (type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID) { sd->flag |= bsdf_microfacet_beckmann_glass_setup(bsdf); diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index f9b90de0337..d0c6365bddb 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -2334,6 +2334,9 @@ NODE_DEFINE(GlassBsdfNode) SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); SOCKET_IN_FLOAT(IOR, "IOR", 1.5f); + SOCKET_IN_FLOAT(thin_film_thickness, "Thin Film Thickness", 0.0f); + SOCKET_IN_FLOAT(thin_film_ior, "Thin Film IOR", 1.3f); + SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; @@ -2347,7 +2350,12 @@ GlassBsdfNode::GlassBsdfNode() : BsdfNode(get_node_type()) void GlassBsdfNode::compile(SVMCompiler &compiler) { closure = distribution; - BsdfNode::compile(compiler, input("Roughness"), input("IOR"), input("Color")); + BsdfNode::compile(compiler, + input("Roughness"), + input("IOR"), + input("Color"), + input("Thin Film Thickness"), + input("Thin Film IOR")); } void GlassBsdfNode::compile(OSLCompiler &compiler) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index dc3354f591e..823c213b66e 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -658,6 +658,8 @@ class GlassBsdfNode : public BsdfNode { NODE_SOCKET_API(float, roughness) NODE_SOCKET_API(float, IOR) + NODE_SOCKET_API(float, thin_film_thickness) + NODE_SOCKET_API(float, thin_film_ior) NODE_SOCKET_API(ClosureType, distribution) }; diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc index 1450de8e0f1..2e325a1921b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc @@ -8,6 +8,10 @@ namespace blender::nodes::node_shader_bsdf_glass_cc { static void node_declare(NodeDeclarationBuilder &b) { + b.use_custom_socket_order(); + + b.add_output("BSDF"); + b.add_input("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_input("Roughness") .default_value(0.0f) @@ -17,7 +21,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input("IOR").default_value(1.5f).min(0.0f).max(1000.0f); b.add_input("Normal").hide_value(); b.add_input("Weight").available(false); - b.add_output("BSDF"); + + PanelDeclarationBuilder &film = b.add_panel("Thin Film").default_closed(true); + film.add_input("Thin Film Thickness") + .default_value(0.0) + .min(0.0f) + .max(100000.0f) + .subtype(PROP_WAVELENGTH) + .description("Thickness of the film in nanometers"); + film.add_input("Thin Film IOR") + .default_value(1.33f) + .min(1.0f) + .max(1000.0f) + .description("Index of refraction (IOR) of the thin film"); } static void node_shader_init_glass(bNodeTree * /*ntree*/, bNode *node) @@ -53,14 +69,23 @@ NODE_SHADER_MATERIALX_BEGIN NodeItem roughness = get_input_value("Roughness", NodeItem::Type::Vector2); NodeItem ior = get_input_value("IOR", NodeItem::Type::Float); NodeItem normal = get_input_link("Normal", NodeItem::Type::Vector3); + NodeItem thin_film_thickness = get_input_value("Thin Film Thickness", NodeItem::Type::Float); + NodeItem thin_film_ior = get_input_value("Thin Film IOR", NodeItem::Type::Float); - return create_node("dielectric_bsdf", - NodeItem::Type::BSDF, - {{"normal", normal}, - {"tint", color}, - {"roughness", roughness}, - {"ior", ior}, - {"scatter_mode", val(std::string("RT"))}}); + NodeItem n_base_bsdf = create_node("dielectric_bsdf", + NodeItem::Type::BSDF, + {{"normal", normal}, + {"tint", color}, + {"roughness", roughness}, + {"ior", ior}, + {"scatter_mode", val(std::string("RT"))}}); + NodeItem n_thin_film_bsdf = create_node( + "thin_film_bsdf", + NodeItem::Type::BSDF, + {{"thickness", thin_film_thickness}, {"ior", thin_film_ior}}); + + return create_node( + "layer", NodeItem::Type::BSDF, {{"top", n_thin_film_bsdf}, {"base", n_base_bsdf}}); } #endif NODE_SHADER_MATERIALX_END