From 2bd06093c7f978052140842a72f435a7ae4fe4ed Mon Sep 17 00:00:00 2001 From: Amogh Shivaram Date: Mon, 29 Sep 2025 02:58:20 +0200 Subject: [PATCH] Cycles: Thin film iridescence for metals Applies thin film iridescence to metals in Metallic BSDF and Principled BSDF. To get the complex IOR values for each spectral band from F82 Tint colors, the code uses the parametrization from "Artist Friendly Metallic Fresnel", where the g parameter is set to F82. This IOR is used to find the phase shift, but reflectance is still calculated with the F82 Tint formula after adjusting F0 for the film's IOR. Co-authored-by: Lukas Stockner Co-authored-by: Weizhen Huang Co-authored-by: RobertMoerland Pull Request: https://projects.blender.org/blender/blender/pulls/141131 --- .../cycles/kernel/closure/bsdf_microfacet.h | 65 +++-- intern/cycles/kernel/closure/bsdf_util.h | 234 +++++++++++------- intern/cycles/kernel/osl/closures_setup.h | 6 + intern/cycles/kernel/osl/closures_template.h | 5 + .../kernel/osl/shaders/node_metallic_bsdf.osl | 27 +- .../osl/shaders/node_principled_bsdf.osl | 12 +- intern/cycles/kernel/svm/closure.h | 33 ++- intern/cycles/scene/shader.cpp | 26 +- intern/cycles/scene/shader_nodes.cpp | 8 +- intern/cycles/scene/shader_nodes.h | 2 + intern/cycles/util/math_float3.h | 5 + intern/cycles/util/types_float3.h | 5 + .../gpu_shader_material_metallic.glsl | 2 + .../shader/nodes/node_shader_bsdf_metallic.cc | 26 +- .../cycles_renders/metallic_thinfilm_f82.png | 3 + .../metallic_thinfilm_physical.png | 3 + .../eevee_renders/metallic_thinfilm_f82.png | 3 + .../metallic_thinfilm_physical.png | 3 + .../render/bsdf/metallic_thinfilm_f82.blend | 3 + .../bsdf/metallic_thinfilm_physical.blend | 3 + .../metallic_thinfilm_f82.png | 3 + .../metallic_thinfilm_physical.png | 3 + .../metallic_thinfilm_f82.png | 3 + .../metallic_thinfilm_physical.png | 3 + .../metallic_thinfilm_f82.png | 3 + .../metallic_thinfilm_physical.png | 3 + .../principled_bsdf_thinfilm_metallic.png | 3 + .../principled_bsdf_thinfilm_metallic.png | 3 + .../principled_bsdf_thinfilm_metallic.blend | 3 + .../principled_bsdf_thinfilm_metallic.png | 3 + .../principled_bsdf_thinfilm_metallic.png | 3 + .../principled_bsdf_thinfilm_metallic.png | 3 + tests/python/storm_render_tests.py | 1 + 33 files changed, 378 insertions(+), 133 deletions(-) create mode 100644 tests/files/render/bsdf/cycles_renders/metallic_thinfilm_f82.png create mode 100644 tests/files/render/bsdf/cycles_renders/metallic_thinfilm_physical.png create mode 100644 tests/files/render/bsdf/eevee_renders/metallic_thinfilm_f82.png create mode 100644 tests/files/render/bsdf/eevee_renders/metallic_thinfilm_physical.png create mode 100644 tests/files/render/bsdf/metallic_thinfilm_f82.blend create mode 100644 tests/files/render/bsdf/metallic_thinfilm_physical.blend create mode 100644 tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_f82.png create mode 100644 tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_physical.png create mode 100644 tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_f82.png create mode 100644 tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_physical.png create mode 100644 tests/files/render/bsdf/workbench_renders/metallic_thinfilm_f82.png create mode 100644 tests/files/render/bsdf/workbench_renders/metallic_thinfilm_physical.png create mode 100644 tests/files/render/principled_bsdf/cycles_renders/principled_bsdf_thinfilm_metallic.png create mode 100644 tests/files/render/principled_bsdf/eevee_renders/principled_bsdf_thinfilm_metallic.png create mode 100644 tests/files/render/principled_bsdf/principled_bsdf_thinfilm_metallic.blend create mode 100644 tests/files/render/principled_bsdf/storm_hydra_renders/principled_bsdf_thinfilm_metallic.png create mode 100644 tests/files/render/principled_bsdf/storm_usd_renders/principled_bsdf_thinfilm_metallic.png create mode 100644 tests/files/render/principled_bsdf/workbench_renders/principled_bsdf_thinfilm_metallic.png diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index c2d82ee436a..0159d66014c 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -29,11 +29,6 @@ enum MicrofacetFresnel { F82_TINT, }; -struct FresnelThinFilm { - float thickness; - float ior; -}; - struct FresnelDielectricTint { FresnelThinFilm thin_film; @@ -42,7 +37,8 @@ struct FresnelDielectricTint { }; struct FresnelConductor { - ComplexIOR ior; + FresnelThinFilm thin_film; + complex ior; }; struct FresnelGeneralizedSchlick { @@ -57,6 +53,8 @@ struct FresnelGeneralizedSchlick { }; struct FresnelF82Tint { + FresnelThinFilm thin_film; + /* Perpendicular reflectivity. */ Spectrum f0; /* Precomputed (1-cos)^6 factor for edge tint. */ @@ -250,7 +248,15 @@ ccl_device_forceinline void microfacet_fresnel(KernelGlobals kg, } else if (bsdf->fresnel_type == MicrofacetFresnel::CONDUCTOR) { ccl_private FresnelConductor *fresnel = (ccl_private FresnelConductor *)bsdf->fresnel; - *r_reflectance = fresnel_conductor(cos_theta_i, fresnel->ior); + + if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) { + *r_reflectance = fresnel_iridescence( + kg, 1.0f, fresnel->thin_film, fresnel->ior, nullptr, cos_theta_i, r_cos_theta_t); + } + else { + *r_reflectance = fresnel_conductor(cos_theta_i, fresnel->ior); + } + *r_transmittance = zero_spectrum(); } else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) { @@ -258,7 +264,24 @@ ccl_device_forceinline void microfacet_fresnel(KernelGlobals kg, * Essentially, this is the usual Schlick Fresnel with an additional cosI*(1-cosI)^6 * term which modulates the reflectivity around acos(1/7) degrees (ca. 82°). */ ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel; - *r_reflectance = fresnel_f82(cos_theta_i, fresnel->f0, fresnel->b); + + if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) { + /* Estimate n and k by reinterpreting F0 and F82 as r and g from "Artist Friendly Metallic + * Fresnel" by Ole Gulbrandsen. */ + const Spectrum r = min(fresnel->f0, make_float3(0.999f)); + const Spectrum g = fresnel_f82(1.0f / 7.0f, fresnel->f0, fresnel->b); + + const Spectrum sqrt_r = sqrt(r); + const Spectrum n = mix((1.0f + sqrt_r) / (1.0f - sqrt_r), (1.0f - r) / (1.0f + r), g); + const Spectrum k = safe_sqrt((r * sqr(n + 1) - sqr(n - 1)) / (1.0f - r)); + + *r_reflectance = fresnel_iridescence( + kg, 1.0f, fresnel->thin_film, {n, k}, &g, cos_theta_i, r_cos_theta_t); + } + else { + *r_reflectance = fresnel_f82(cos_theta_i, fresnel->f0, fresnel->b); + } + *r_transmittance = zero_spectrum(); } else if (bsdf->fresnel_type == MicrofacetFresnel::GENERALIZED_SCHLICK) { @@ -270,13 +293,8 @@ ccl_device_forceinline void microfacet_fresnel(KernelGlobals kg, * Principled BSDF for now, so it's fine to not support custom exponents and F90. */ kernel_assert(fresnel->exponent < 0.0f); kernel_assert(fresnel->f90 == one_spectrum()); - F = fresnel_iridescence(kg, - 1.0f, - fresnel->thin_film.ior, - bsdf->ior, - cos_theta_i, - fresnel->thin_film.thickness, - r_cos_theta_t); + F = fresnel_iridescence( + kg, 1.0f, fresnel->thin_film, {bsdf->ior, 0.0f}, nullptr, cos_theta_i, r_cos_theta_t); /* Apply F0 scaling (here per-channel, since iridescence produces colored output). * Note that the usual approach (as used below) cannot be used here, since F may be below * F0_real. Therefore, use a different approach: Scale the result by (F0 / F0_real), with @@ -446,11 +464,18 @@ ccl_device Spectrum bsdf_microfacet_estimate_albedo(KernelGlobals kg, } else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) { ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel; - const float rough = sqrtf(sqrtf(bsdf->alpha_x * bsdf->alpha_y)); - const float s = lookup_table_read_3D( - kg, rough, cos_NI, 0.5f, kernel_data.tables.ggx_gen_schlick_s, 16, 16, 16); - /* TODO: Precompute B factor term and account for it here. */ - reflectance = mix(fresnel->f0, one_spectrum(), s); + + if (fresnel->thin_film.thickness > THINFILM_THICKNESS_CUTOFF) { + /* Precomputing LUTs for thin-film iridescence isn't viable, so fall back to the specular + * reflection approximation from the microfacet_fresnel call above in that case. */ + } + else { + const float rough = sqrtf(sqrtf(bsdf->alpha_x * bsdf->alpha_y)); + const float s = lookup_table_read_3D( + kg, rough, cos_NI, 0.5f, kernel_data.tables.ggx_gen_schlick_s, 16, 16, 16); + /* TODO: Precompute B factor term and account for it here. */ + reflectance = mix(fresnel->f0, one_spectrum(), s); + } } else if ((bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC || bsdf->fresnel_type == MicrofacetFresnel::DIELECTRIC_TINT) && diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index 819348cbda7..3fd639b758c 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -16,19 +16,37 @@ CCL_NAMESPACE_BEGIN -template struct ComplexIOR { - T eta; - T k; +struct FresnelThinFilm { + float thickness; + float ior; +}; + +template struct complex { + T re; + T im; + + ccl_device_inline_method complex operator*=(ccl_private const complex &other) + { + const T im = this->re * other.im + this->im * other.re; + this->re = this->re * other.re - this->im * other.im; + this->im = im; + return *this; + } + + ccl_device_inline_method complex operator*(ccl_private const float &other) + { + return complex{this->re * other, this->im * other}; + } }; /* Compute fresnel reflectance for perpendicular (aka S-) and parallel (aka P-) polarized light. - * If requested by the caller, r_phi is set to the phase shift on reflection. + * If requested by the caller, r_cos_phi is set to the cosine of the phase shift on reflection. * Also returns the dot product of the refracted ray and the normal as `cos_theta_t`, as it is * used when computing the direction of the refracted ray. */ ccl_device float2 fresnel_dielectric_polarized(float cos_theta_i, const float eta, ccl_private float *r_cos_theta_t, - ccl_private float2 *r_phi) + ccl_private float2 *r_cos_phi) { kernel_assert(!isnan_safe(cos_theta_i)); @@ -37,7 +55,7 @@ ccl_device float2 fresnel_dielectric_polarized(float cos_theta_i, const float eta_cos_theta_t_sq = sqr(eta) - (1.0f - sqr(cos_theta_i)); if (eta_cos_theta_t_sq <= 0) { /* Total internal reflection. */ - if (r_phi) { + if (r_cos_phi) { /* The following code would compute the proper phase shift on TIR. * However, for the current user of this computation (the iridescence code), * this doesn't actually affect the result, so don't bother with the computation for now. @@ -46,7 +64,7 @@ ccl_device float2 fresnel_dielectric_polarized(float cos_theta_i, * `r_phi->x = -2.0f * atanf(fac / cosThetaI);` * `r_phi->y = -2.0f * atanf(fac / (cosThetaI * sqr(eta)));` */ - *r_phi = zero_float2(); + *r_cos_phi = one_float2(); } return one_float2(); } @@ -63,8 +81,8 @@ ccl_device float2 fresnel_dielectric_polarized(float cos_theta_i, const float r_s = (cos_theta_i + eta * cos_theta_t) / (cos_theta_i - eta * cos_theta_t); const float r_p = (cos_theta_t + eta * cos_theta_i) / (eta * cos_theta_i - cos_theta_t); - if (r_phi) { - *r_phi = make_float2(r_s < 0.0f, r_p < 0.0f) * M_PI_F; + if (r_cos_phi) { + *r_cos_phi = make_float2(2 * (r_s >= 0.0f) - 1, 2 * (r_p >= 0.0f) - 1); } /* Return squared amplitude to get the fraction of reflected energy. */ @@ -117,23 +135,23 @@ ccl_device_inline float fresnel_dielectric_Fss(const float eta) return (eta - 1.0f) / (4.08567f + 1.00071f * eta); } -/* Evaluates the Fresnel equations at a dielectric-conductor interface. If requested by the caller, - * sets r_R_s and r_R_p to the reflectances for perpendicular and parallel polarized light, and - * sets r_phi_s and r_phi_p to the phase shifts due to reflection. +/* Evaluates the Fresnel equations at a dielectric-conductor interface, calculating reflectances + * and phase shifts due to reflection if requested. The phase shifts phi_s and phi_p are returned + * as phasor_s = exp(i * phi_s) and phasor_p = exp(i * phi_p). * This code is based on equations from section 14.4.1 of Principles of Optics 7th ed. by Born and * Wolf, but uses `n + ik` instead of `n(1 + ik)` for IOR. The phase shifts are calculated so that * phi_p = phi_s at 90 degree incidence to match fresnel_dielectric_polarized. */ ccl_device void fresnel_conductor_polarized(const float cosi, const float ambient_ior, - const ComplexIOR conductor_ior, + const complex conductor_ior, ccl_private Spectrum *r_R_s, ccl_private Spectrum *r_R_p, - ccl_private Spectrum *r_phi_s, - ccl_private Spectrum *r_phi_p) + ccl_private complex *r_phasor_s, + ccl_private complex *r_phasor_p) { const float eta1 = ambient_ior; - const Spectrum eta2 = conductor_ior.eta; - const Spectrum k2 = conductor_ior.k; + const Spectrum eta2 = conductor_ior.re; + const Spectrum k2 = conductor_ior.im; const float eta1_sq = sqr(eta1); const Spectrum eta2_sq = sqr(eta2); @@ -143,35 +161,37 @@ ccl_device void fresnel_conductor_polarized(const float cosi, const Spectrum t1 = eta2_sq - k2_sq - eta1_sq * (1.0f - sqr(cosi)); const Spectrum t2 = sqrt(sqr(t1) + sqr(two_eta2_k2)); - const Spectrum u_sq = max(0.5f * (t2 + t1), zero_float3()); - const Spectrum v_sq = max(0.5f * (t2 - t1), zero_float3()); + const Spectrum u_sq = max(0.5f * (t2 + t1), zero_spectrum()); + const Spectrum v_sq = max(0.5f * (t2 - t1), zero_spectrum()); const Spectrum u = sqrt(u_sq); const Spectrum v = sqrt(v_sq); if (r_R_s && r_R_p) { - *r_R_s = (sqr(eta1 * cosi - u) + v_sq) / (sqr(eta1 * cosi + u) + v_sq); + *r_R_s = safe_divide(sqr(eta1 * cosi - u) + v_sq, sqr(eta1 * cosi + u) + v_sq); const Spectrum t3 = (eta2_sq - k2_sq) * cosi; const Spectrum t4 = two_eta2_k2 * cosi; - const Spectrum R_p = (sqr(t3 - eta1 * u) + sqr(t4 - eta1 * v)) / - (sqr(t3 + eta1 * u) + sqr(t4 + eta1 * v)); - const auto mask = isequal_mask(eta2, zero_spectrum()) & isequal_mask(k2, zero_spectrum()); - *r_R_p = select(mask, one_spectrum(), R_p); + *r_R_p = safe_divide(sqr(t3 - eta1 * u) + sqr(t4 - eta1 * v), + sqr(t3 + eta1 * u) + sqr(t4 + eta1 * v)); } - if (r_phi_s && r_phi_p) { - const Spectrum s_numerator = 2.0f * eta1 * cosi * v; - const Spectrum s_denominator = u_sq + v_sq - sqr(eta1 * cosi); - *r_phi_s = atan2(-s_numerator, -s_denominator); + if (r_phasor_s && r_phasor_p) { + const Spectrum re_s = -u_sq - v_sq + sqr(eta1 * cosi); + const Spectrum im_s = -2.0f * eta1 * cosi * v; + const Spectrum mag_s = sqrt(sqr(re_s) + sqr(im_s)); + r_phasor_s->re = select(is_zero_mask(mag_s), one_spectrum(), re_s / mag_s); + r_phasor_s->im = select(is_zero_mask(mag_s), zero_spectrum(), im_s / mag_s); - const Spectrum p_numerator = 2.0f * eta1 * cosi * (two_eta2_k2 * u - (eta2_sq - k2_sq) * v); - const Spectrum p_denominator = sqr((eta2_sq + k2_sq) * cosi) - eta1_sq * (u_sq + v_sq); - *r_phi_p = atan2(p_numerator, p_denominator); + const Spectrum re_p = sqr((eta2_sq + k2_sq) * cosi) - eta1_sq * (u_sq + v_sq); + const Spectrum im_p = 2.0f * eta1 * cosi * (two_eta2_k2 * u - (eta2_sq - k2_sq) * v); + const Spectrum mag_p = sqrt(sqr(re_p) + sqr(im_p)); + r_phasor_p->re = select(is_zero_mask(mag_p), one_spectrum(), re_p / mag_p); + r_phasor_p->im = select(is_zero_mask(mag_p), zero_spectrum(), im_p / mag_p); } } /* Calculates Fresnel reflectance at a dielectric-conductor interface given the relative IOR. */ -ccl_device Spectrum fresnel_conductor(const float cosi, const ComplexIOR ior) +ccl_device Spectrum fresnel_conductor(const float cosi, const complex ior) { Spectrum R_s, R_p; fresnel_conductor_polarized(cosi, 1.0f, ior, &R_s, &R_p, nullptr, nullptr); @@ -218,7 +238,7 @@ ccl_device_inline Spectrum fresnel_f82(const float cosi, const Spectrum F0, cons } /* Approximates the average single-scattering Fresnel for a physical conductor. */ -ccl_device_inline Spectrum fresnel_conductor_Fss(const ComplexIOR ior) +ccl_device_inline Spectrum fresnel_conductor_Fss(const complex ior) { /* In order to estimate Fss of the conductor, we fit the F82 model to it based on the * value at 0° and ~82° and then use the analytic expression for its Fss. */ @@ -398,108 +418,150 @@ ccl_device_inline Spectrum closure_layering_weight(const Spectrum layer_albedo, /** * Evaluate the sensitivity functions for the Fourier-space spectral integration. - * The code here uses the Gaussian fit for the CIE XYZ curves that is provided - * in the reference implementation. - * For details on what this actually represents, see the paper. - * In theory we should pre-compute the sensitivity functions for the working RGB - * color-space, remap them to be functions of (light) frequency, take their Fourier - * transform and store them as a LUT that gets looked up here. - * In practice, using the XYZ fit and converting the result from XYZ to RGB is easier. */ -ccl_device_inline Spectrum iridescence_lookup_sensitivity(KernelGlobals kg, - const float OPD, - const float shift) +ccl_device_inline complex iridescence_lookup_sensitivity(KernelGlobals kg, + const float OPD) { /* The LUT covers 0 to 60 um. */ float x = M_2PI_F * OPD / 60000.0f; const int size = THIN_FILM_TABLE_SIZE; - const float3 mag = make_float3( + const float3 re = make_float3( lookup_table_read(kg, x, kernel_data.tables.thin_film_table + 0 * size, size), lookup_table_read(kg, x, kernel_data.tables.thin_film_table + 1 * size, size), lookup_table_read(kg, x, kernel_data.tables.thin_film_table + 2 * size, size)); - const float3 phase = make_float3( + const float3 im = make_float3( lookup_table_read(kg, x, kernel_data.tables.thin_film_table + 3 * size, size), lookup_table_read(kg, x, kernel_data.tables.thin_film_table + 4 * size, size), lookup_table_read(kg, x, kernel_data.tables.thin_film_table + 5 * size, size)); - return mag * cos(phase - shift); + return {re, im}; } +template ccl_device_inline float3 iridescence_airy_summation(KernelGlobals kg, - const float T121, const float R12, - const float R23, + const SpectrumOrFloat R23, const float OPD, - const float phi) + const complex phasor) { - if (R23 == 1.0f) { - /* Shortcut for TIR on the bottom interface. */ - return one_float3(); - } + const float T121 = 1.0f - R12; - const float R123 = R12 * R23; - const float r123 = sqrtf(R123); - const float Rs = sqr(T121) * R23 / (1.0f - R123); + const SpectrumOrFloat R123 = R12 * R23; + const SpectrumOrFloat r123 = sqrt(R123); + const SpectrumOrFloat Rs = sqr(T121) * R23 / (1.0f - R123); + + /* Initialize complex number for exp(i * phi)^m, equivalent to {cos(m * phi), sin(m * phi)} as + * used in equation 10. */ + complex accumulator = phasor; /* Perform summation over path order differences (equation 10). */ - float3 R = make_float3(R12 + Rs); /* C0 */ - float Cm = (Rs - T121); + Spectrum R = make_spectrum(Rs + R12); /* C0 */ + SpectrumOrFloat Cm = (Rs - T121); + /* Truncate after m=3, higher differences have barely any impact. */ for (int m = 1; m < 4; m++) { Cm *= r123; - R += Cm * 2.0f * iridescence_lookup_sensitivity(kg, m * OPD, m * phi); + const complex S = iridescence_lookup_sensitivity(kg, m * OPD); + R += Cm * 2.0f * (accumulator.re * S.re + accumulator.im * S.im); + accumulator *= phasor; } return R; } +/* Template meta-programming helper to be able to have an if-constexpr expression + * to switch between conductive (for Spectrum) or dielectric (for float) Fresnel. + * Essentially std::is_same, but also works on GPU. */ +template struct fresnel_info; +template<> struct fresnel_info { + ccl_static_constexpr bool conductive = false; +}; +template<> struct fresnel_info { + ccl_static_constexpr bool conductive = true; +}; + +template ccl_device Spectrum fresnel_iridescence(KernelGlobals kg, - float eta1, - float eta2, - float eta3, - float cos_theta_1, - const float thickness, + const float ambient_ior, + const FresnelThinFilm thin_film, + const complex substrate_ior, + ccl_private const Spectrum *F82, + const float cos_theta_1, ccl_private float *r_cos_theta_3) { - /* For films below 30nm, the wave-optic-based Airy summation approach no longer applies, + /* For films below 1nm, the wave-optic-based Airy summation approach no longer applies, * so blend towards the case without coating. */ - if (thickness < 30.0f) { - eta2 = mix(eta1, eta2, smoothstep(0.0f, 30.0f, thickness)); + float film_ior = thin_film.ior; + if (thin_film.thickness < 1.0f) { + film_ior = mix(ambient_ior, film_ior, smoothstep(0.0f, 1.0f, thin_film.thickness)); } float cos_theta_2; - float2 phi12; - float2 phi23; + /* The real component of exp(i * phi12), equivalent to cos(phi12). */ + float2 phasor12_real; /* Compute reflection at the top interface (ambient to film). */ - const float2 R12 = fresnel_dielectric_polarized(cos_theta_1, eta2 / eta1, &cos_theta_2, &phi12); + const float2 R12 = fresnel_dielectric_polarized( + cos_theta_1, film_ior / ambient_ior, &cos_theta_2, &phasor12_real); if (isequal(R12, one_float2())) { /* TIR at the top interface. */ return one_spectrum(); } - /* Compute optical path difference inside the thin film. */ - const float OPD = -2.0f * eta2 * thickness * cos_theta_2; + /* Compute reflection at the bottom interface (film to substrate). */ + SpectrumOrFloat R23_s, R23_p; + complex phasor23_s, phasor23_p; + if constexpr (fresnel_info::conductive) { + /* Material is a conductor. */ + if (F82 != nullptr) { + /* Calculate reflectance using the F82 model if the caller requested it. */ - /* Compute reflection at the bottom interface (film to medium). */ - const float2 R23 = fresnel_dielectric_polarized( - -cos_theta_2, eta3 / eta2, r_cos_theta_3, &phi23); - if (isequal(R23, one_float2())) { - /* TIR at the bottom interface. - * All the Airy summation math still simplifies to 1.0 in this case. */ - return one_spectrum(); + /* Scale n and k by the film ior, and recompute F0. */ + const Spectrum n = substrate_ior.re / film_ior; + const Spectrum k_sq = sqr(substrate_ior.im / film_ior); + const Spectrum F0 = (sqr(n - 1.0f) + k_sq) / (sqr(n + 1.0f) + k_sq); + R23_s = fresnel_f82(-cos_theta_2, F0, fresnel_f82_B(F0, *F82)); + R23_p = R23_s; + + fresnel_conductor_polarized( + -cos_theta_2, film_ior, substrate_ior, nullptr, nullptr, &phasor23_s, &phasor23_p); + } + else { + fresnel_conductor_polarized( + -cos_theta_2, film_ior, substrate_ior, &R23_s, &R23_p, &phasor23_s, &phasor23_p); + } + } + else { + /* Material is a dielectric. */ + float2 phasor23_real; + const float2 R23 = fresnel_dielectric_polarized( + -cos_theta_2, substrate_ior.re / film_ior, r_cos_theta_3, &phasor23_real); + + if (isequal(R23, one_float2())) { + /* TIR at the bottom interface. + * All the Airy summation math still simplifies to 1.0 in this case. */ + return one_spectrum(); + } + + R23_s = R23.x; + R23_p = R23.y; + phasor23_s = {phasor23_real.x, 0.0f}; + phasor23_p = {phasor23_real.y, 0.0f}; } - /* Compute helper parameters. */ - const float2 T121 = one_float2() - R12; - const float2 phi = make_float2(M_PI_F, M_PI_F) - phi12 + phi23; + /* Compute optical path difference inside the thin film. */ + const float OPD = -2.0f * film_ior * thin_film.thickness * cos_theta_2; + + /* Compute full phase shifts due to reflection, as a complex number exp(i * (phi23 + phi21)). + * This complex form avoids the atan2 and cos calls needed to directly get the phase shift. */ + const complex phasor_s = phasor23_s * -phasor12_real.x; + const complex phasor_p = phasor23_p * -phasor12_real.y; /* Perform Airy summation and average the polarizations. */ - float3 R = mix(iridescence_airy_summation(kg, T121.x, R12.x, R23.x, OPD, phi.x), - iridescence_airy_summation(kg, T121.y, R12.y, R23.y, OPD, phi.y), - 0.5f); + const Spectrum R_s = iridescence_airy_summation(kg, R12.x, R23_s, OPD, phasor_s); + const Spectrum R_p = iridescence_airy_summation(kg, R12.y, R23_p, OPD, phasor_p); - return saturate(R); + return saturate(mix(R_s, R_p, 0.5f)); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/closures_setup.h b/intern/cycles/kernel/osl/closures_setup.h index 3cd2cb5fa50..4a88c6c8880 100644 --- a/intern/cycles/kernel/osl/closures_setup.h +++ b/intern/cycles/kernel/osl/closures_setup.h @@ -381,6 +381,9 @@ ccl_device void osl_closure_conductor_bsdf_setup(KernelGlobals kg, preserve_energy = (closure->distribution == make_string("multi_ggx", 16842698693386468366ull)); } + fresnel->thin_film.thickness = closure->thinfilm_thickness; + fresnel->thin_film.ior = closure->thinfilm_ior; + fresnel->ior = {rgb_to_spectrum(closure->ior), rgb_to_spectrum(closure->extinction)}; bsdf_microfacet_setup_fresnel_conductor(kg, bsdf, sd, fresnel, preserve_energy); } @@ -607,6 +610,9 @@ ccl_device void osl_closure_microfacet_f82_tint_setup( } fresnel->f0 = rgb_to_spectrum(closure->f0); + fresnel->thin_film.thickness = closure->thinfilm_thickness; + fresnel->thin_film.ior = closure->thinfilm_ior; + bsdf_microfacet_setup_fresnel_f82_tint( kg, bsdf, sd, fresnel, rgb_to_spectrum(closure->f82), preserve_energy); } diff --git a/intern/cycles/kernel/osl/closures_template.h b/intern/cycles/kernel/osl/closures_template.h index 3b0e91f74f3..c30987490a9 100644 --- a/intern/cycles/kernel/osl/closures_template.h +++ b/intern/cycles/kernel/osl/closures_template.h @@ -79,6 +79,8 @@ OSL_CLOSURE_STRUCT_BEGIN(ConductorBSDF, conductor_bsdf) OSL_CLOSURE_STRUCT_MEMBER(ConductorBSDF, VECTOR, packed_float3, ior, nullptr) OSL_CLOSURE_STRUCT_MEMBER(ConductorBSDF, VECTOR, packed_float3, extinction, nullptr) OSL_CLOSURE_STRUCT_MEMBER(ConductorBSDF, STRING, DeviceString, distribution, nullptr) + OSL_CLOSURE_STRUCT_MEMBER(ConductorBSDF, FLOAT, float, thinfilm_thickness, "thinfilm_thickness") + OSL_CLOSURE_STRUCT_MEMBER(ConductorBSDF, FLOAT, float, thinfilm_ior, "thinfilm_ior") OSL_CLOSURE_STRUCT_END(ConductorBSDF, conductor_bsdf) OSL_CLOSURE_STRUCT_BEGIN(GeneralizedSchlickBSDF, generalized_schlick_bsdf) @@ -117,6 +119,9 @@ OSL_CLOSURE_STRUCT_BEGIN(MicrofacetF82Tint, microfacet_f82_tint) OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, FLOAT, float, alpha_y, nullptr) OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, VECTOR, packed_float3, f0, nullptr) OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, VECTOR, packed_float3, f82, nullptr) + OSL_CLOSURE_STRUCT_MEMBER( + MicrofacetF82Tint, FLOAT, float, thinfilm_thickness, "thinfilm_thickness") + OSL_CLOSURE_STRUCT_MEMBER(MicrofacetF82Tint, FLOAT, float, thinfilm_ior, "thinfilm_ior") OSL_CLOSURE_STRUCT_END(MicrofacetF82Tint, microfacet) OSL_CLOSURE_STRUCT_BEGIN(MicrofacetMultiGGXGlass, microfacet_multi_ggx_glass) diff --git a/intern/cycles/kernel/osl/shaders/node_metallic_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_metallic_bsdf.osl index 9dfa10ea19b..b4f0cecebb8 100644 --- a/intern/cycles/kernel/osl/shaders/node_metallic_bsdf.osl +++ b/intern/cycles/kernel/osl/shaders/node_metallic_bsdf.osl @@ -14,6 +14,8 @@ shader node_metallic_bsdf(color BaseColor = color(0.617, 0.577, 0.540), float Roughness = 0.5, float Anisotropy = 0.0, float Rotation = 0.0, + float ThinFilmThickness = 0.0, + float ThinFilmIOR = 1.33, normal Normal = N, normal Tangent = 0.0, output closure color BSDF = 0) @@ -35,10 +37,29 @@ shader node_metallic_bsdf(color BaseColor = color(0.617, 0.577, 0.540), if (fresnel_type == "f82") { color F0 = clamp(BaseColor, color(0.0), color(1.0)); color F82 = clamp(EdgeTint, color(0.0), color(1.0)); - BSDF = microfacet_f82_tint(distribution, Normal, T, alpha_x, alpha_y, F0, F82); + BSDF = microfacet_f82_tint(distribution, + Normal, + T, + alpha_x, + alpha_y, + F0, + F82, + "thinfilm_thickness", + ThinFilmThickness, + "thinfilm_ior", + ThinFilmIOR); } else { - BSDF = conductor_bsdf( - Normal, T, alpha_x, alpha_y, max(IOR, 0.0), max(Extinction, 0.0), distribution); + BSDF = conductor_bsdf(Normal, + T, + alpha_x, + alpha_y, + max(IOR, 0.0), + max(Extinction, 0.0), + distribution, + "thinfilm_thickness", + ThinFilmThickness, + "thinfilm_ior", + ThinFilmIOR); } } diff --git a/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl index 1536dd1f612..b8f1ef04090 100644 --- a/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl +++ b/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl @@ -155,7 +155,17 @@ shader node_principled_bsdf(string distribution = "multi_ggx", if (metallic > CLOSURE_WEIGHT_CUTOFF) { color F0 = clamped_base_color; color F82 = min(specular_tint, color(1.0)); - MetallicBSDF = microfacet_f82_tint(distribution, Normal, T, alpha_x, alpha_y, F0, F82); + MetallicBSDF = microfacet_f82_tint(distribution, + Normal, + T, + alpha_x, + alpha_y, + F0, + F82, + "thinfilm_thickness", + ThinFilmThickness, + "thinfilm_ior", + ThinFilmIOR); BSDF = mix(BSDF, MetallicBSDF, metallic); } diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index de326bdfbb5..44f14cd12ac 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -354,6 +354,9 @@ ccl_device fresnel->f0 = rgb_to_spectrum(clamped_base_color); const Spectrum f82 = min(specular_tint, one_spectrum()); + fresnel->thin_film.thickness = thinfilm_thickness; + fresnel->thin_film.ior = thinfilm_ior; + /* setup bsdf */ sd->flag |= bsdf_microfacet_ggx_setup(bsdf); const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); @@ -576,7 +579,21 @@ ccl_device bsdf->N = valid_reflection_N; bsdf->ior = 1.0f; - const ClosureType distribution = (ClosureType)data_node.z; + uint distribution_int; + uint thin_film_thickness_offset; + uint thin_film_ior_offset; + uint unused; + svm_unpack_node_uchar4(data_node.z, + &distribution_int, + &thin_film_thickness_offset, + &thin_film_ior_offset, + &unused); + + const float thin_film_thickness = fmaxf( + stack_load_float(stack, thin_film_thickness_offset), 1e-5f); + const float thin_film_ior = fmaxf(stack_load_float(stack, thin_film_ior_offset), 1e-5f); + + const ClosureType distribution = (ClosureType)distribution_int; /* Setup BSDF */ if (distribution == CLOSURE_BSDF_MICROFACET_BECKMANN_ID) { sd->flag |= bsdf_microfacet_beckmann_setup(bsdf); @@ -591,6 +608,13 @@ ccl_device ccl_private FresnelConductor *fresnel = (ccl_private FresnelConductor *) closure_alloc_extra(sd, sizeof(FresnelConductor)); + if (!fresnel) { + break; + } + + fresnel->thin_film.thickness = thin_film_thickness; + fresnel->thin_film.ior = thin_film_ior; + const float3 n = max(stack_load_float3(stack, base_ior_offset), zero_float3()); const float3 k = max(stack_load_float3(stack, edge_tint_k_offset), zero_float3()); @@ -601,6 +625,13 @@ ccl_device ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)closure_alloc_extra( sd, sizeof(FresnelF82Tint)); + if (!fresnel) { + break; + } + + fresnel->thin_film.thickness = thin_film_thickness; + fresnel->thin_film.ior = thin_film_ior; + const float3 color = saturate(stack_load_float3(stack, base_ior_offset)); const float3 tint = saturate(stack_load_float3(stack, edge_tint_k_offset)); diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index 5fc1b0d6f3f..4353d633dc3 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -945,15 +945,13 @@ void ShaderManager::compute_thin_film_table(const Transform &xyz_to_rgb) * the XYZ-to-RGB matrix to get the RGB LUT. * * That's what this function does: We load the precomputed values, convert to RGB, normalize - * the result to make the DC term equal to 1, convert from real/imaginary to magnitude/phase - * since that form is smoother and therefore interpolates more nicely, and then store that - * into the final table that's used by the kernel. + * the result to make the DC term equal to 1, and then store that into the final table that's + * used by the kernel. */ assert(sizeof(table_thin_film_cmf) == 6 * THIN_FILM_TABLE_SIZE * sizeof(float)); thin_film_table.resize(6 * THIN_FILM_TABLE_SIZE); float3 normalization; - float3 prevPhase = zero_float3(); for (int i = 0; i < THIN_FILM_TABLE_SIZE; i++) { const float *table_row = table_thin_film_cmf[i]; /* Load precomputed resampled Fourier-transformed XYZ CMFs. */ @@ -971,21 +969,13 @@ void ShaderManager::compute_thin_film_table(const Transform &xyz_to_rgb) normalization = 1.0f / rgbReal; } - /* Convert the complex value into magnitude/phase representation. */ - const float3 rgbMag = sqrt(sqr(rgbReal) + sqr(rgbImag)); - float3 rgbPhase = atan2(rgbImag, rgbReal); - - /* Unwrap phase to avoid jumps. */ - rgbPhase -= M_2PI_F * round((rgbPhase - prevPhase) * M_1_2PI_F); - prevPhase = rgbPhase; - /* Store in lookup table. */ - thin_film_table[i + 0 * THIN_FILM_TABLE_SIZE] = rgbMag.x * normalization.x; - thin_film_table[i + 1 * THIN_FILM_TABLE_SIZE] = rgbMag.y * normalization.y; - thin_film_table[i + 2 * THIN_FILM_TABLE_SIZE] = rgbMag.z * normalization.z; - thin_film_table[i + 3 * THIN_FILM_TABLE_SIZE] = rgbPhase.x; - thin_film_table[i + 4 * THIN_FILM_TABLE_SIZE] = rgbPhase.y; - thin_film_table[i + 5 * THIN_FILM_TABLE_SIZE] = rgbPhase.z; + thin_film_table[i + 0 * THIN_FILM_TABLE_SIZE] = rgbReal.x * normalization.x; + thin_film_table[i + 1 * THIN_FILM_TABLE_SIZE] = rgbReal.y * normalization.y; + thin_film_table[i + 2 * THIN_FILM_TABLE_SIZE] = rgbReal.z * normalization.z; + thin_film_table[i + 3 * THIN_FILM_TABLE_SIZE] = rgbImag.x * normalization.x; + thin_film_table[i + 4 * THIN_FILM_TABLE_SIZE] = rgbImag.y * normalization.y; + thin_film_table[i + 5 * THIN_FILM_TABLE_SIZE] = rgbImag.z * normalization.z; } } diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index 4fa4b2d3750..5f05698c215 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -2339,6 +2339,9 @@ NODE_DEFINE(MetallicBsdfNode) SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); SOCKET_IN_FLOAT(rotation, "Rotation", 0.0f); + SOCKET_IN_FLOAT(thin_film_thickness, "Thin Film Thickness", 0.0f); + SOCKET_IN_FLOAT(thin_film_ior, "Thin Film IOR", 1.33f); + SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; @@ -2386,6 +2389,9 @@ void MetallicBsdfNode::compile(SVMCompiler &compiler) compiler.stack_assign(input("Extinction")) : compiler.stack_assign(input("Edge Tint")); + const int thin_film_thickness_offset = compiler.stack_assign(input("Thin Film Thickness")); + const int thin_film_ior_offset = compiler.stack_assign(input("Thin Film IOR")); + ShaderInput *roughness_in = input("Roughness"); ShaderInput *anisotropy_in = input("Anisotropy"); @@ -2404,7 +2410,7 @@ void MetallicBsdfNode::compile(SVMCompiler &compiler) normal_offset, compiler.encode_uchar4( base_color_ior_offset, edge_tint_k_offset, rotation_offset, tangent_offset), - distribution); + compiler.encode_uchar4(distribution, thin_film_thickness_offset, thin_film_ior_offset)); } void MetallicBsdfNode::compile(OSLCompiler &compiler) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index 38986291fe2..776c504c873 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -622,6 +622,8 @@ class MetallicBsdfNode : public BsdfNode { NODE_SOCKET_API(float, roughness) NODE_SOCKET_API(float, anisotropy) NODE_SOCKET_API(float, rotation) + NODE_SOCKET_API(float, thin_film_thickness) + NODE_SOCKET_API(float, thin_film_ior) NODE_SOCKET_API(ClosureType, distribution) NODE_SOCKET_API(ClosureType, fresnel_type) diff --git a/intern/cycles/util/math_float3.h b/intern/cycles/util/math_float3.h index 15c5e343aa6..ce517c6d764 100644 --- a/intern/cycles/util/math_float3.h +++ b/intern/cycles/util/math_float3.h @@ -515,6 +515,11 @@ ccl_device_inline float3 faceforward(const float3 vector, } #endif +ccl_device_inline float3 safe_sqrt(const float3 a) +{ + return sqrt(max(a, zero_float3())); +} + ccl_device_inline float3 project(const float3 v, const float3 v_proj) { const float len_squared = dot(v_proj, v_proj); diff --git a/intern/cycles/util/types_float3.h b/intern/cycles/util/types_float3.h index ce4f3f40619..44a4c57f78d 100644 --- a/intern/cycles/util/types_float3.h +++ b/intern/cycles/util/types_float3.h @@ -117,6 +117,11 @@ ccl_device_inline float3 make_float3(const int3 i) #endif } +ccl_device_inline float3 make_float3(const float3 a) +{ + return a; +} + ccl_device_inline void print_float3(const ccl_private char *label, const float3 a) { #ifdef __KERNEL_PRINTF__ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_metallic.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_metallic.glsl index 2286c6cd143..be6a56950bd 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_metallic.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_metallic.glsl @@ -30,6 +30,8 @@ void node_bsdf_metallic(float4 base_color, float3 N, float3 T, float weight, + float thin_film_thickness, + float thin_film_ior, const float do_multiscatter, const float use_complex_ior, out Closure result) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_metallic.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_metallic.cc index bc5bfa6dae1..11176705ab6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_metallic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_metallic.cc @@ -11,6 +11,11 @@ namespace blender::nodes::node_shader_bsdf_metallic_cc { static void node_declare(NodeDeclarationBuilder &b) { + b.use_custom_socket_order(); + + b.add_output("BSDF"); + b.add_default_layout(); + b.add_input("Base Color") .default_value({0.617f, 0.577f, 0.540f, 1.0f}) .description("Color of the material"); @@ -36,7 +41,6 @@ static void node_declare(NodeDeclarationBuilder &b) .description( "Microfacet roughness of the surface (0.0 is a perfect mirror reflection, 1.0 is " "completely rough)"); - ; b.add_input("Anisotropy") .default_value(0.0f) .min(0.0f) @@ -54,7 +58,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input("Normal").hide_value(); b.add_input("Tangent").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_buts_metallic(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) @@ -123,6 +139,8 @@ NODE_SHADER_MATERIALX_BEGIN 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); + 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); NodeItem ior_out, extinction_out; if (node_->custom2 == SHD_PHYSICAL_CONDUCTOR) { @@ -143,7 +161,9 @@ NODE_SHADER_MATERIALX_BEGIN {"tangent", tangent}, {"ior", ior_out}, {"extinction", extinction_out}, - {"roughness", roughness}}); + {"roughness", roughness}, + {"thinfilm_thickness", thin_film_thickness}, + {"thinfilm_ior", thin_film_ior}}); } #endif NODE_SHADER_MATERIALX_END diff --git a/tests/files/render/bsdf/cycles_renders/metallic_thinfilm_f82.png b/tests/files/render/bsdf/cycles_renders/metallic_thinfilm_f82.png new file mode 100644 index 00000000000..06e0e3452ed --- /dev/null +++ b/tests/files/render/bsdf/cycles_renders/metallic_thinfilm_f82.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ed7599a3f938a557bacee0bcc2e0fe271976313bb11cd6656e8d0567edb742e +size 33637 diff --git a/tests/files/render/bsdf/cycles_renders/metallic_thinfilm_physical.png b/tests/files/render/bsdf/cycles_renders/metallic_thinfilm_physical.png new file mode 100644 index 00000000000..a81bf4443d6 --- /dev/null +++ b/tests/files/render/bsdf/cycles_renders/metallic_thinfilm_physical.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9de91f44c4562f42ebd24d707978c5795b81331ed803bf6ad69e0748a71bee99 +size 34058 diff --git a/tests/files/render/bsdf/eevee_renders/metallic_thinfilm_f82.png b/tests/files/render/bsdf/eevee_renders/metallic_thinfilm_f82.png new file mode 100644 index 00000000000..a206d55f370 --- /dev/null +++ b/tests/files/render/bsdf/eevee_renders/metallic_thinfilm_f82.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b28145db38c2044d746a3e4bf6d9e63c5f9dd0b201e9c9bf7161fe0691b27d5 +size 22784 diff --git a/tests/files/render/bsdf/eevee_renders/metallic_thinfilm_physical.png b/tests/files/render/bsdf/eevee_renders/metallic_thinfilm_physical.png new file mode 100644 index 00000000000..e8b3dd6cc07 --- /dev/null +++ b/tests/files/render/bsdf/eevee_renders/metallic_thinfilm_physical.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92e74fef9d838f2b95aad41b180c67b5445bf281c1daf366637575e794086853 +size 22552 diff --git a/tests/files/render/bsdf/metallic_thinfilm_f82.blend b/tests/files/render/bsdf/metallic_thinfilm_f82.blend new file mode 100644 index 00000000000..d50ff2ca3f7 --- /dev/null +++ b/tests/files/render/bsdf/metallic_thinfilm_f82.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7dafcce17464cc614918539bec88d727803280113c0fd5ecbe6b6c6a2e69aac +size 154024 diff --git a/tests/files/render/bsdf/metallic_thinfilm_physical.blend b/tests/files/render/bsdf/metallic_thinfilm_physical.blend new file mode 100644 index 00000000000..37909a03278 --- /dev/null +++ b/tests/files/render/bsdf/metallic_thinfilm_physical.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60c98a06a3d40f67688ef3a9a5d45ba02a91aa8a664f67d3da2c3f30d217df63 +size 153976 diff --git a/tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_f82.png b/tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_f82.png new file mode 100644 index 00000000000..61628de2e93 --- /dev/null +++ b/tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_f82.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:168a5b94a4d4c70f8c7a51bf7878f9a24786836d851debd08898fc6263f27b61 +size 22192 diff --git a/tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_physical.png b/tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_physical.png new file mode 100644 index 00000000000..39e27da38cf --- /dev/null +++ b/tests/files/render/bsdf/storm_hydra_renders/metallic_thinfilm_physical.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0705c9be51854411f21dd21c28d44db2ad9fc50ee6a0ecaa2b4a1a56d598b4a5 +size 22059 diff --git a/tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_f82.png b/tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_f82.png new file mode 100644 index 00000000000..bd4d04f82dd --- /dev/null +++ b/tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_f82.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:318a0e1a1bee0657dc5533162d99f537ff965d1c691248756092e4c12c1c1033 +size 24886 diff --git a/tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_physical.png b/tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_physical.png new file mode 100644 index 00000000000..60f0e16369e --- /dev/null +++ b/tests/files/render/bsdf/storm_usd_renders/metallic_thinfilm_physical.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb3216d909bbbefe91e9acc6aadf4e276c5f0851fd74b4fb235db2b6e4cc8bd7 +size 24915 diff --git a/tests/files/render/bsdf/workbench_renders/metallic_thinfilm_f82.png b/tests/files/render/bsdf/workbench_renders/metallic_thinfilm_f82.png new file mode 100644 index 00000000000..40298379e15 --- /dev/null +++ b/tests/files/render/bsdf/workbench_renders/metallic_thinfilm_f82.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:242449591bbb91707a49ba85ffcb407ed7fad5f516b7ba97221c47c5fd37d77f +size 20031 diff --git a/tests/files/render/bsdf/workbench_renders/metallic_thinfilm_physical.png b/tests/files/render/bsdf/workbench_renders/metallic_thinfilm_physical.png new file mode 100644 index 00000000000..c69eecb1004 --- /dev/null +++ b/tests/files/render/bsdf/workbench_renders/metallic_thinfilm_physical.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e1fbacb22c5649ded7b2247f1801c5705629b37a76e25fe4559ceb073cac720 +size 20036 diff --git a/tests/files/render/principled_bsdf/cycles_renders/principled_bsdf_thinfilm_metallic.png b/tests/files/render/principled_bsdf/cycles_renders/principled_bsdf_thinfilm_metallic.png new file mode 100644 index 00000000000..671b256140a --- /dev/null +++ b/tests/files/render/principled_bsdf/cycles_renders/principled_bsdf_thinfilm_metallic.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:107e7ce80ca2ce25a41218fad6ce563fcec84a8fabccb5a5f2ec8e1b0ad20b94 +size 29725 diff --git a/tests/files/render/principled_bsdf/eevee_renders/principled_bsdf_thinfilm_metallic.png b/tests/files/render/principled_bsdf/eevee_renders/principled_bsdf_thinfilm_metallic.png new file mode 100644 index 00000000000..7767d0a1148 --- /dev/null +++ b/tests/files/render/principled_bsdf/eevee_renders/principled_bsdf_thinfilm_metallic.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:524788f21430d2bf7113ef1cc4ad608ca92834f57d8bbc1e51f9d2b53845c556 +size 20686 diff --git a/tests/files/render/principled_bsdf/principled_bsdf_thinfilm_metallic.blend b/tests/files/render/principled_bsdf/principled_bsdf_thinfilm_metallic.blend new file mode 100644 index 00000000000..8a37be62118 --- /dev/null +++ b/tests/files/render/principled_bsdf/principled_bsdf_thinfilm_metallic.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28020cbf5fd4ede535d57e5fdf8351344c2740e14ed8c7c92cac689e1e64b18c +size 143896 diff --git a/tests/files/render/principled_bsdf/storm_hydra_renders/principled_bsdf_thinfilm_metallic.png b/tests/files/render/principled_bsdf/storm_hydra_renders/principled_bsdf_thinfilm_metallic.png new file mode 100644 index 00000000000..3fe294edd81 --- /dev/null +++ b/tests/files/render/principled_bsdf/storm_hydra_renders/principled_bsdf_thinfilm_metallic.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fb748491f777f5f0a013427dfd8b0c90bfd95d347b3e93e0950692ce8d54c05 +size 16758 diff --git a/tests/files/render/principled_bsdf/storm_usd_renders/principled_bsdf_thinfilm_metallic.png b/tests/files/render/principled_bsdf/storm_usd_renders/principled_bsdf_thinfilm_metallic.png new file mode 100644 index 00000000000..54eb77ee479 --- /dev/null +++ b/tests/files/render/principled_bsdf/storm_usd_renders/principled_bsdf_thinfilm_metallic.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:329523290ca15a887b99c2de09b54ad94dd245274d316e266fe60c705af7454a +size 17689 diff --git a/tests/files/render/principled_bsdf/workbench_renders/principled_bsdf_thinfilm_metallic.png b/tests/files/render/principled_bsdf/workbench_renders/principled_bsdf_thinfilm_metallic.png new file mode 100644 index 00000000000..5f1d7ea2476 --- /dev/null +++ b/tests/files/render/principled_bsdf/workbench_renders/principled_bsdf_thinfilm_metallic.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d60987abccdbb047eba274621c030d4ae2041b677bef07d655f4e50860a7fc67 +size 13956 diff --git a/tests/python/storm_render_tests.py b/tests/python/storm_render_tests.py index 1a17339c74e..d27c40cb94f 100644 --- a/tests/python/storm_render_tests.py +++ b/tests/python/storm_render_tests.py @@ -34,6 +34,7 @@ BLOCKLIST_USD = [ BLOCKLIST_METAL = [ # Thinfilm "principled.*thinfilm.*.blend", + "metallic.*thinfilm.*.blend", # Transparency "transparent.blend", "transparent_shadow.blend",