diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index c3221886166..a436bea101d 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -166,13 +166,13 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg, case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: case CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID: label = bsdf_microfacet_ggx_sample( - sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta); + kg, sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta); break; case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: label = bsdf_microfacet_beckmann_sample( - sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta); + kg, sc, Ng, sd->wi, rand, eval, wo, pdf, sampled_roughness, eta); break; case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: label = bsdf_ashikhmin_shirley_sample( @@ -506,12 +506,12 @@ ccl_device_inline /* For consistency with eval() this should be using sd->Ng, but that causes * artifacts (see shadow_terminator_metal test). Needs deeper investigation * for how to solve this. */ - eval = bsdf_microfacet_ggx_eval(sc, sd->N, sd->wi, wo, pdf); + eval = bsdf_microfacet_ggx_eval(kg, sc, sd->N, sd->wi, wo, pdf); break; case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID: - eval = bsdf_microfacet_beckmann_eval(sc, sd->N, sd->wi, wo, pdf); + eval = bsdf_microfacet_beckmann_eval(kg, sc, sd->N, sd->wi, wo, pdf); break; case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: eval = bsdf_ashikhmin_shirley_eval(sc, sd->N, sd->wi, wo, pdf); diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index 7228f335385..68eded71cc9 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -29,7 +29,14 @@ enum MicrofacetFresnel { F82_TINT, }; +typedef struct FresnelThinFilm { + float thickness; + float ior; +} FresnelThinFilm; + typedef struct FresnelDielectricTint { + FresnelThinFilm thin_film; + Spectrum reflection_tint; Spectrum transmission_tint; } FresnelDielectricTint; @@ -39,6 +46,8 @@ typedef struct FresnelConductor { } FresnelConductor; typedef struct FresnelGeneralizedSchlick { + FresnelThinFilm thin_film; + Spectrum reflection_tint; Spectrum transmission_tint; /* Reflectivity at perpendicular (F0) and glancing (F90) angles. */ @@ -217,7 +226,8 @@ ccl_device_forceinline float3 microfacet_ggx_sample_vndf(const float3 wi, * the incoming angle `cos_theta_i`. * Also returns the cosine of the angle between the normal and the refracted ray as `r_cos_theta_t` * if provided. */ -ccl_device_forceinline void microfacet_fresnel(ccl_private const MicrofacetBsdf *bsdf, +ccl_device_forceinline void microfacet_fresnel(KernelGlobals kg, + ccl_private const MicrofacetBsdf *bsdf, const float cos_theta_i, ccl_private float *r_cos_theta_t, ccl_private Spectrum *r_reflectance, @@ -258,13 +268,41 @@ ccl_device_forceinline void microfacet_fresnel(ccl_private const MicrofacetBsdf else if (bsdf->fresnel_type == MicrofacetFresnel::GENERALIZED_SCHLICK) { ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *) bsdf->fresnel; - float s; - if (fresnel->exponent < 0.0f) { + Spectrum F; + if (fresnel->thin_film.thickness > 0.1f) { + /* Iridescence doesn't combine well with the general case. We only expose it through the + * 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); + /* 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 + * the strength of the scaling depending on how close F is to F0_real. + * There isn't one single "correct" way to do this, it's just for artistic control anyways. + */ + const float F0_real = F0_from_ior(bsdf->ior); + if (F0_real > 1e-5f && !isequal(F, one_spectrum())) { + FOREACH_SPECTRUM_CHANNEL (i) { + const float s = saturatef(inverse_lerp(1.0f, F0_real, GET_SPECTRUM_CHANNEL(F, i))); + const float factor = GET_SPECTRUM_CHANNEL(fresnel->f0, i) / F0_real; + GET_SPECTRUM_CHANNEL(F, i) *= mix(1.0f, factor, s); + } + } + } + else if (fresnel->exponent < 0.0f) { /* Special case: Use real Fresnel curve to determine the interpolation between F0 and F90. - * Used by Principled v1. */ + * Used by Principled BSDF. */ const float F_real = fresnel_dielectric(cos_theta_i, bsdf->ior, r_cos_theta_t); const float F0_real = F0_from_ior(bsdf->ior); - s = saturatef(inverse_lerp(F0_real, 1.0f, F_real)); + const float s = saturatef(inverse_lerp(F0_real, 1.0f, F_real)); + F = mix(fresnel->f0, fresnel->f90, s); } else { /* Regular case: Generalized Schlick term. */ @@ -282,9 +320,10 @@ ccl_device_forceinline void microfacet_fresnel(ccl_private const MicrofacetBsdf /* TODO(lukas): Is a special case for exponent==5 worth it? */ /* When going from a higher to a lower IOR, we must use the transmitted angle. */ - s = powf(1.0f - ((bsdf->ior < 1.0f) ? cos_theta_t : cos_theta_i), fresnel->exponent); + const float fresnel_angle = ((bsdf->ior < 1.0f) ? cos_theta_t : cos_theta_i); + const float s = powf(1.0f - fresnel_angle, fresnel->exponent); + F = mix(fresnel->f0, fresnel->f90, s); } - const Spectrum F = mix(fresnel->f0, fresnel->f90, s); *r_reflectance = F * fresnel->reflection_tint; *r_transmittance = (one_spectrum() - F) * fresnel->transmission_tint; } @@ -374,7 +413,7 @@ ccl_device Spectrum bsdf_microfacet_estimate_albedo(KernelGlobals kg, { const float cos_NI = dot(sd->wi, bsdf->N); Spectrum reflectance, transmittance; - microfacet_fresnel(bsdf, cos_NI, nullptr, &reflectance, &transmittance); + microfacet_fresnel(kg, bsdf, cos_NI, nullptr, &reflectance, &transmittance); reflectance *= (float)eval_reflection; transmittance *= (float)eval_transmission; @@ -384,19 +423,25 @@ ccl_device Spectrum bsdf_microfacet_estimate_albedo(KernelGlobals kg, ccl_private FresnelGeneralizedSchlick *fresnel = (ccl_private FresnelGeneralizedSchlick *) bsdf->fresnel; - float rough = sqrtf(sqrtf(bsdf->alpha_x * bsdf->alpha_y)); - float s; - if (fresnel->exponent < 0.0f) { - float z = sqrtf(fabsf((bsdf->ior - 1.0f) / (bsdf->ior + 1.0f))); - s = lookup_table_read_3D( - kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_ior_s, 16, 16, 16); + if (fresnel->thin_film.thickness > 0.1f) { + /* 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 { - float z = 1.0f / (0.2f * fresnel->exponent + 1.0f); - s = lookup_table_read_3D( - kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_s, 16, 16, 16); + float rough = sqrtf(sqrtf(bsdf->alpha_x * bsdf->alpha_y)); + float s; + if (fresnel->exponent < 0.0f) { + float z = sqrtf(fabsf((bsdf->ior - 1.0f) / (bsdf->ior + 1.0f))); + s = lookup_table_read_3D( + kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_ior_s, 16, 16, 16); + } + else { + float z = 1.0f / (0.2f * fresnel->exponent + 1.0f); + s = lookup_table_read_3D( + kg, rough, cos_NI, z, kernel_data.tables.ggx_gen_schlick_s, 16, 16, 16); + } + reflectance = mix(fresnel->f0, fresnel->f90, s) * fresnel->reflection_tint; } - reflectance = mix(fresnel->f0, fresnel->f90, s) * fresnel->reflection_tint; } else if (bsdf->fresnel_type == MicrofacetFresnel::F82_TINT) { ccl_private FresnelF82Tint *fresnel = (ccl_private FresnelF82Tint *)bsdf->fresnel; @@ -498,7 +543,8 @@ ccl_device_forceinline int bsdf_microfacet_eval_flag(const ccl_private Microface } template -ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc, +ccl_device Spectrum bsdf_microfacet_eval(KernelGlobals kg, + ccl_private const ShaderClosure *sc, const float3 Ng, const float3 wi, const float3 wo, @@ -544,7 +590,7 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc, /* Compute Fresnel coefficients. */ const float cos_HI = dot(H, wi); Spectrum reflectance, transmittance; - microfacet_fresnel(bsdf, cos_HI, nullptr, &reflectance, &transmittance); + microfacet_fresnel(kg, bsdf, cos_HI, nullptr, &reflectance, &transmittance); if (is_zero(reflectance) && is_zero(transmittance)) { return zero_spectrum(); @@ -587,7 +633,8 @@ ccl_device Spectrum bsdf_microfacet_eval(ccl_private const ShaderClosure *sc, } template -ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc, +ccl_device int bsdf_microfacet_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 wi, const float3 rand, @@ -647,7 +694,7 @@ ccl_device int bsdf_microfacet_sample(ccl_private const ShaderClosure *sc, float cos_HO; /* Compute Fresnel coefficients. */ Spectrum reflectance, transmittance; - microfacet_fresnel(bsdf, cos_HI, &cos_HO, &reflectance, &transmittance); + microfacet_fresnel(kg, bsdf, cos_HI, &cos_HO, &reflectance, &transmittance); if (is_zero(reflectance) && is_zero(transmittance)) { return LABEL_NONE; @@ -920,17 +967,19 @@ ccl_device void bsdf_microfacet_blur(ccl_private ShaderClosure *sc, float roughn bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); } -ccl_device Spectrum bsdf_microfacet_ggx_eval(ccl_private const ShaderClosure *sc, +ccl_device Spectrum bsdf_microfacet_ggx_eval(KernelGlobals kg, + ccl_private const ShaderClosure *sc, const float3 Ng, const float3 wi, const float3 wo, ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; - return bsdf->energy_scale * bsdf_microfacet_eval(sc, Ng, wi, wo, pdf); + return bsdf->energy_scale * bsdf_microfacet_eval(kg, sc, Ng, wi, wo, pdf); } -ccl_device int bsdf_microfacet_ggx_sample(ccl_private const ShaderClosure *sc, +ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 wi, const float3 rand, @@ -942,7 +991,7 @@ ccl_device int bsdf_microfacet_ggx_sample(ccl_private const ShaderClosure *sc, { int label = bsdf_microfacet_sample( - sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta); + kg, sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta); *eval *= ((ccl_private const MicrofacetBsdf *)sc)->energy_scale; return label; } @@ -985,16 +1034,18 @@ ccl_device int bsdf_microfacet_beckmann_glass_setup(ccl_private MicrofacetBsdf * return SD_BSDF | SD_BSDF_HAS_TRANSMISSION | bsdf_microfacet_eval_flag(bsdf); } -ccl_device Spectrum bsdf_microfacet_beckmann_eval(ccl_private const ShaderClosure *sc, +ccl_device Spectrum bsdf_microfacet_beckmann_eval(KernelGlobals kg, + ccl_private const ShaderClosure *sc, const float3 Ng, const float3 wi, const float3 wo, ccl_private float *pdf) { - return bsdf_microfacet_eval(sc, Ng, wi, wo, pdf); + return bsdf_microfacet_eval(kg, sc, Ng, wi, wo, pdf); } -ccl_device int bsdf_microfacet_beckmann_sample(ccl_private const ShaderClosure *sc, +ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 wi, const float3 rand, @@ -1005,7 +1056,7 @@ ccl_device int bsdf_microfacet_beckmann_sample(ccl_private const ShaderClosure * ccl_private float *eta) { return bsdf_microfacet_sample( - sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta); + kg, sc, Ng, wi, rand, eval, wo, pdf, sampled_roughness, eta); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index 60a958028f2..889140665f3 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -9,9 +9,14 @@ CCL_NAMESPACE_BEGIN -/* Compute fresnel reflectance. Also return 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 float fresnel_dielectric(float cos_theta_i, float eta, ccl_private float *r_cos_theta_t) +/* 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. + * 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, + float eta, + ccl_private float *r_cos_theta_t, + ccl_private float2 *r_phi) { kernel_assert(!isnan_safe(cos_theta_i)); @@ -20,7 +25,18 @@ ccl_device float fresnel_dielectric(float cos_theta_i, float eta, ccl_private fl 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. */ - return 1.0f; + if (r_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. + * + * const float fac = sqrtf(1.0f - sqr(cosThetaI) - sqr(eta)); + * r_phi->x = -2.0f * atanf(fac / cosThetaI); + * r_phi->y = -2.0f * atanf(fac / (cosThetaI * sqr(eta))); + */ + *r_phi = zero_float2(); + } + return one_float2(); } cos_theta_i = fabsf(cos_theta_i); @@ -33,9 +49,22 @@ ccl_device float fresnel_dielectric(float cos_theta_i, float eta, ccl_private fl /* Amplitudes of reflected waves. */ 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) / (cos_theta_t - eta * cos_theta_i); + const float r_p = (cos_theta_t + eta * cos_theta_i) / (eta * cos_theta_i - cos_theta_t); - return 0.5f * (sqr(r_s) + sqr(r_p)); + if (r_phi) { + *r_phi = make_float2(r_s < 0.0f, r_p < 0.0f) * M_PI_F; + } + + /* Return squared amplitude to get the fraction of reflected energy. */ + return make_float2(sqr(r_s), sqr(r_p)); +} + +/* Compute fresnel reflectance for unpolarized light. */ +ccl_device_forceinline float fresnel_dielectric(float cos_theta_i, + float eta, + ccl_private float *r_cos_theta_t) +{ + return average(fresnel_dielectric_polarized(cos_theta_i, eta, r_cos_theta_t, nullptr)); } /* Refract the incident ray, given the cosine of the refraction angle and the relative refractive @@ -235,4 +264,123 @@ ccl_device_inline Spectrum closure_layering_weight(const Spectrum layer_albedo, return weight * saturatef(1.0f - reduce_max(safe_divide_color(layer_albedo, weight))); } +/* ******** Thin-film iridescence implementation ******** + * + * Based on "A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence" + * by Laurent Belcour and Pascal Barla. + * https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html. + */ + +/* 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 + * colorspace, remap them to be functions of (light) frequency, take their Fourier + * transform and store them as a LUT that gets looked up here. + * In practise, using the XYZ fit and converting the result from XYZ to RGB is easier. + */ +ccl_device_inline Spectrum iridescence_lookup_sensitivity(float OPD, float shift) +{ + float phase = M_2PI_F * OPD * 1e-9f; + float3 val = make_float3(5.4856e-13f, 4.4201e-13f, 5.2481e-13f); + float3 pos = make_float3(1.6810e+06f, 1.7953e+06f, 2.2084e+06f); + float3 var = make_float3(4.3278e+09f, 9.3046e+09f, 6.6121e+09f); + + float3 xyz = val * sqrt(M_2PI_F * var) * cos(pos * phase + shift) * exp(-sqr(phase) * var); + xyz.x += 1.64408e-8f * cosf(2.2399e+06f * phase + shift) * expf(-4.5282e+09f * sqr(phase)); + return xyz / 1.0685e-7f; +} + +ccl_device_inline float3 +iridescence_airy_summation(float T121, float R12, float R23, float OPD, float phi) +{ + if (R23 == 1.0f) { + /* Shortcut for TIR on the bottom interface. */ + return one_float3(); + } + + float R123 = R12 * R23; + float r123 = sqrtf(R123); + float Rs = sqr(T121) * R23 / (1.0f - R123); + + /* Perform summation over path order differences (equation 10). */ + float3 R = make_float3(R12 + Rs); /* C0 */ + float 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(m * OPD, m * phi); + } + return R; +} + +ccl_device Spectrum fresnel_iridescence(KernelGlobals kg, + float eta1, + float eta2, + float eta3, + float cos_theta_1, + float thickness, + ccl_private float *r_cos_theta_3) +{ + /* For films below 30nm, 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 cos_theta_2; + float2 phi12, phi23; + + /* Compute reflection at the top interface (ambient to film). */ + float2 R12 = fresnel_dielectric_polarized(cos_theta_1, eta2 / eta1, &cos_theta_2, &phi12); + if (isequal(R12, one_float2())) { + /* TIR at the top interface. */ + return one_spectrum(); + } + + /* Compute optical path difference inside the thin film. */ + float OPD = -2.0f * eta2 * thickness * cos_theta_2; + + /* Compute reflection at the bottom interface (film to medium). */ + 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(); + } + + /* Compute helper parameters. */ + float2 T121 = one_float2() - R12; + float2 phi = make_float2(M_PI_F, M_PI_F) - phi12 + phi23; + + /* Perform Airy summation and average the polarizations. */ + float3 R = mix(iridescence_airy_summation(T121.x, R12.x, R23.x, OPD, phi.x), + iridescence_airy_summation(T121.y, R12.y, R23.y, OPD, phi.y), + 0.5f); + + /* Color space conversion here is tricky. + * In theory, the correct thing would be to compute the spectral color matching functions + * for the RGB channels, take their Fourier transform in wavelength parametrization, and + * then use that in iridescence_lookup_sensitivity(). + * To avoid this complexity, the code here instead uses the reference implementation's + * Gaussian fit of the CIE XYZ curves. However, this means that at this point, R is in + * XYZ values, not RGB. + * Additionally, since I is a reflectivity, not a luminance, the spectral color matching + * functions should be multiplied by the reference illuminant. Since the fit is based on + * the "raw" CIE XYZ curves, the reference illuminant implicitly is a constant spectrum, + * meaning Illuminant E. + * Therefore, we can't just use the regular XYZ->RGB conversion here, we need to include + * a chromatic adaption from E to whatever the white point of the working color space is. + * The proper way to do this would be a Von Kries-style transform, but to keep it simple, + * we just multiply by the white point here. + * + * Note: The reference implementation sidesteps all this by just hardcoding a XYZ->CIE RGB + * matrix. Since CIE RGB uses E as its white point, this sidesteps the chromatic adaption + * topic, but the primary colors don't match (unless you happen to actually work in CIE RGB.) + */ + R *= float4_to_float3(kernel_data.film.white_xyz); + return saturate(xyz_to_rgb(kg, R)); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/data_template.h b/intern/cycles/kernel/data_template.h index e4235369bc2..29ca68f84e7 100644 --- a/intern/cycles/kernel/data_template.h +++ b/intern/cycles/kernel/data_template.h @@ -69,6 +69,7 @@ KERNEL_STRUCT_MEMBER(film, float4, xyz_to_r) KERNEL_STRUCT_MEMBER(film, float4, xyz_to_g) KERNEL_STRUCT_MEMBER(film, float4, xyz_to_b) KERNEL_STRUCT_MEMBER(film, float4, rgb_to_y) +KERNEL_STRUCT_MEMBER(film, float4, white_xyz) /* Rec709 to rendering color space. */ KERNEL_STRUCT_MEMBER(film, float4, rec709_to_r) KERNEL_STRUCT_MEMBER(film, float4, rec709_to_g) diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index b86970f469e..3dce1c1e3c0 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -598,7 +598,8 @@ mnee_sample_bsdf_dh(ClosureType type, float alpha_x, float alpha_y, float sample * We assume here that the pdf (in half-vector measure) is the same as * the one calculation when sampling the microfacet normals from the * specular chain above: this allows us to simplify the bsdf weight */ -ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(ccl_private ShaderClosure *closure, +ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(KernelGlobals kg, + ccl_private ShaderClosure *closure, float3 wi, float3 wo) { @@ -623,7 +624,7 @@ ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(ccl_private ShaderCl } Spectrum reflectance, transmittance; - microfacet_fresnel(bsdf, cosHI, nullptr, &reflectance, &transmittance); + microfacet_fresnel(kg, bsdf, cosHI, nullptr, &reflectance, &transmittance); /* * bsdf_do = (1 - F) * D_do * G * |h.wi| / (n.wi * n.wo) @@ -895,7 +896,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, /* Evaluate product term inside eq.6 at solution interface. vi * divided by corresponding sampled pdf: * fr(vi)_do / pdf_dh(vi) x |do/dh| x |n.wo / n.h| */ - Spectrum bsdf_contribution = mnee_eval_bsdf_contribution(v.bsdf, wi, wo); + Spectrum bsdf_contribution = mnee_eval_bsdf_contribution(kg, v.bsdf, wi, wo); bsdf_eval_mul(throughput, bsdf_contribution); } diff --git a/intern/cycles/kernel/osl/closures_setup.h b/intern/cycles/kernel/osl/closures_setup.h index 1cae22d0b39..2ae2fbe37f9 100644 --- a/intern/cycles/kernel/osl/closures_setup.h +++ b/intern/cycles/kernel/osl/closures_setup.h @@ -274,6 +274,8 @@ ccl_device void osl_closure_dielectric_bsdf_setup(KernelGlobals kg, fresnel->reflection_tint = rgb_to_spectrum(closure->reflection_tint); fresnel->transmission_tint = rgb_to_spectrum(closure->transmission_tint); + fresnel->thin_film.thickness = 0.0f; + fresnel->thin_film.ior = 0.0f; bsdf_microfacet_setup_fresnel_dielectric_tint(kg, bsdf, sd, fresnel, preserve_energy); if (layer_albedo != NULL) { @@ -423,6 +425,8 @@ ccl_device void osl_closure_generalized_schlick_bsdf_setup( fresnel->f0 = rgb_to_spectrum(closure->f0); fresnel->f90 = rgb_to_spectrum(closure->f90); fresnel->exponent = closure->exponent; + fresnel->thin_film.thickness = 0.0f; + fresnel->thin_film.ior = 0.0f; bsdf_microfacet_setup_fresnel_generalized_schlick(kg, bsdf, sd, fresnel, preserve_energy); if (layer_albedo != NULL) { diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index 8c64ccd6383..0d2653f837c 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -83,8 +83,8 @@ ccl_device uint specular_ior_level_offset, roughness_offset, specular_tint_offset, anisotropic_offset, sheen_weight_offset, sheen_tint_offset, sheen_roughness_offset, coat_weight_offset, coat_roughness_offset, coat_ior_offset, eta_offset, transmission_weight_offset, - anisotropic_rotation_offset, coat_tint_offset, coat_normal_offset, dummy, alpha_offset, - emission_strength_offset, emission_offset, unused; + anisotropic_rotation_offset, coat_tint_offset, coat_normal_offset, alpha_offset, + emission_strength_offset, emission_offset, thinfilm_thickness_offset, unused; uint4 data_node2 = read_node(kg, &offset); float3 T = stack_load_float3(stack, data_node.y); @@ -147,21 +147,24 @@ ccl_device // get the subsurface scattering data uint4 data_subsurf = read_node(kg, &offset); - uint4 data_alpha_emission = read_node(kg, &offset); - svm_unpack_node_uchar4(data_alpha_emission.x, + uint4 data_alpha_emission_thin = read_node(kg, &offset); + svm_unpack_node_uchar4(data_alpha_emission_thin.x, &alpha_offset, &emission_strength_offset, &emission_offset, - &dummy); + &thinfilm_thickness_offset); float alpha = stack_valid(alpha_offset) ? stack_load_float(stack, alpha_offset) : - __uint_as_float(data_alpha_emission.y); + __uint_as_float(data_alpha_emission_thin.y); alpha = saturatef(alpha); float emission_strength = stack_valid(emission_strength_offset) ? stack_load_float(stack, emission_strength_offset) : - __uint_as_float(data_alpha_emission.z); + __uint_as_float(data_alpha_emission_thin.z); float3 emission = stack_load_float3(stack, emission_offset) * emission_strength; + float thinfilm_thickness = fmaxf(stack_load_float(stack, thinfilm_thickness_offset), 1e-5f); + float thinfilm_ior = fmaxf(stack_load_float(stack, data_alpha_emission_thin.w), 1e-5f); + Spectrum weight = closure_weight * mix_weight; float alpha_x = sqr(roughness), alpha_y = sqr(roughness); @@ -323,6 +326,9 @@ ccl_device fresnel->transmission_tint = refractive_caustics ? sqrt(rgb_to_spectrum(clamped_base_color)) : zero_spectrum(); + fresnel->thin_film.thickness = thinfilm_thickness; + fresnel->thin_film.ior = (sd->flag & SD_BACKFACING) ? thinfilm_ior / ior : + thinfilm_ior; /* setup bsdf */ sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf); @@ -346,7 +352,7 @@ ccl_device } /* Specular component */ - if (reflective_caustics && eta != 1.0f) { + if (reflective_caustics && (eta != 1.0f || thinfilm_thickness > 0.1f)) { ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight); ccl_private FresnelGeneralizedSchlick *fresnel = @@ -366,6 +372,8 @@ ccl_device fresnel->exponent = -eta; fresnel->reflection_tint = one_spectrum(); fresnel->transmission_tint = zero_spectrum(); + fresnel->thin_film.thickness = thinfilm_thickness; + fresnel->thin_film.ior = thinfilm_ior; /* setup bsdf */ sd->flag |= bsdf_microfacet_ggx_setup(bsdf); @@ -601,6 +609,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; /* setup bsdf */ if (type == CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID) { diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index 6666d2b3b90..3ba12587a9f 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -642,6 +642,7 @@ void ShaderManager::device_update_common(Device * /*device*/, kfilm->xyz_to_g = float3_to_float4(xyz_to_g); kfilm->xyz_to_b = float3_to_float4(xyz_to_b); kfilm->rgb_to_y = float3_to_float4(rgb_to_y); + kfilm->white_xyz = float3_to_float4(white_xyz); kfilm->rec709_to_r = float3_to_float4(rec709_to_r); kfilm->rec709_to_g = float3_to_float4(rec709_to_g); kfilm->rec709_to_b = float3_to_float4(rec709_to_b); @@ -894,6 +895,7 @@ void ShaderManager::init_xyz_transforms() xyz_to_g = float4_to_float3(xyz_to_rec709.y); xyz_to_b = float4_to_float3(xyz_to_rec709.z); rgb_to_y = make_float3(0.2126729f, 0.7151522f, 0.0721750f); + white_xyz = make_float3(0.95047f, 1.0f, 1.08883f); rec709_to_r = make_float3(1.0f, 0.0f, 0.0f); rec709_to_g = make_float3(0.0f, 1.0f, 0.0f); @@ -950,6 +952,7 @@ void ShaderManager::init_xyz_transforms() const Transform rgb_to_xyz = transform_inverse(xyz_to_rgb); rgb_to_y = float4_to_float3(rgb_to_xyz.y); + white_xyz = transform_direction(&rgb_to_xyz, one_float3()); const Transform rec709_to_rgb = xyz_to_rgb * transform_inverse(xyz_to_rec709); rec709_to_r = float4_to_float3(rec709_to_rgb.x); diff --git a/intern/cycles/scene/shader.h b/intern/cycles/scene/shader.h index 7fc99d1badd..f29d351ccaf 100644 --- a/intern/cycles/scene/shader.h +++ b/intern/cycles/scene/shader.h @@ -250,6 +250,7 @@ class ShaderManager { float3 xyz_to_g; float3 xyz_to_b; float3 rgb_to_y; + float3 white_xyz; float3 rec709_to_r; float3 rec709_to_g; float3 rec709_to_b; diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index ed8897ac6c2..70e5ab55f2a 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -2661,6 +2661,9 @@ NODE_DEFINE(PrincipledBsdfNode) SOCKET_IN_COLOR(emission_color, "Emission Color", one_float3()); SOCKET_IN_FLOAT(emission_strength, "Emission Strength", 0.0f); + SOCKET_IN_FLOAT(thin_film_thickness, "Thin Film Thickness", 0.0f); + SOCKET_IN_FLOAT(thin_film_ior, "Thin Film IOR", 1.3f); + SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); @@ -2762,6 +2765,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler) int alpha_offset = compiler.stack_assign_if_linked(alpha_in); int emission_strength_offset = compiler.stack_assign_if_linked(emission_strength_in); int emission_color_offset = compiler.stack_assign(input("Emission Color")); + int thin_film_thickness_offset = compiler.stack_assign(input("Thin Film Thickness")); + int thin_film_ior_offset = compiler.stack_assign(input("Thin Film IOR")); compiler.add_node( NODE_CLOSURE_BSDF, @@ -2800,12 +2805,13 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler) subsurface_scale_offset, subsurface_anisotropy_offset); - compiler.add_node( - compiler.encode_uchar4( - alpha_offset, emission_strength_offset, emission_color_offset, SVM_STACK_INVALID), - __float_as_int(get_float(alpha_in->socket_type)), - __float_as_int(get_float(emission_strength_in->socket_type)), - SVM_STACK_INVALID); + compiler.add_node(compiler.encode_uchar4(alpha_offset, + emission_strength_offset, + emission_color_offset, + thin_film_thickness_offset), + __float_as_int(get_float(alpha_in->socket_type)), + __float_as_int(get_float(emission_strength_in->socket_type)), + thin_film_ior_offset); } void PrincipledBsdfNode::compile(OSLCompiler &compiler) diff --git a/intern/cycles/scene/shader_nodes.h b/intern/cycles/scene/shader_nodes.h index d62cbbfccc4..8540bbb3f30 100644 --- a/intern/cycles/scene/shader_nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -536,6 +536,8 @@ class PrincipledBsdfNode : public BsdfBaseNode { NODE_SOCKET_API(float3, emission_color) NODE_SOCKET_API(float, emission_strength) NODE_SOCKET_API(float, surface_mix_weight) + NODE_SOCKET_API(float, thin_film_thickness) + NODE_SOCKET_API(float, thin_film_ior) public: void attributes(Shader *shader, AttributeRequestSet *attributes); diff --git a/intern/cycles/util/math_float2.h b/intern/cycles/util/math_float2.h index d1aaab3b461..b81e0b70c3b 100644 --- a/intern/cycles/util/math_float2.h +++ b/intern/cycles/util/math_float2.h @@ -119,17 +119,26 @@ ccl_device_inline bool is_zero(const float2 a) return (a.x == 0.0f && a.y == 0.0f); } -ccl_device_inline float average(const float2 a) -{ - return (a.x + a.y) * (1.0f / 2.0f); -} - ccl_device_inline float dot(const float2 a, const float2 b) { return a.x * b.x + a.y * b.y; } #endif +ccl_device_inline float average(const float2 a) +{ + return (a.x + a.y) * (1.0f / 2.0f); +} + +ccl_device_inline bool isequal(const float2 a, const float2 b) +{ +#if defined(__KERNEL_METAL__) + return all(a == b); +#else + return a == b; +#endif +} + ccl_device_inline float len(const float2 a) { return sqrtf(dot(a, a)); diff --git a/intern/cycles/util/math_float3.h b/intern/cycles/util/math_float3.h index 81943900ad7..581a29094b9 100644 --- a/intern/cycles/util/math_float3.h +++ b/intern/cycles/util/math_float3.h @@ -371,6 +371,11 @@ ccl_device_inline float3 log(float3 v) return make_float3(logf(v.x), logf(v.y), logf(v.z)); } +ccl_device_inline float3 cos(float3 v) +{ + return make_float3(cosf(v.x), cosf(v.y), cosf(v.z)); +} + ccl_device_inline float3 reflect(const float3 incident, const float3 normal) { float3 unit_normal = normalize(normal); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index e3aeecd9c71..b9cda51a73a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -55,6 +55,8 @@ void node_bsdf_principled(vec4 base_color, vec4 sheen_tint, vec4 emission, float emission_strength, + float thin_film_thickness, + float thin_film_ior, const float do_diffuse, const float do_coat, const float do_refraction, diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index 7fb9f097903..df34a6901da 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -258,6 +258,17 @@ static void node_declare(NodeDeclarationBuilder &b) "Strength of the emitted light. A value of 1.0 ensures " "that the object in the image has the exact same color as the Emission Color"); #define SOCK_EMISSION_STRENGTH_ID 27 + + /* Panel for Thin Film settings. */ + 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); +#define SOCK_THIN_FILM_THICKNESS_ID 28 + film.add_input("Thin Film IOR").default_value(1.33f).min(1.0f).max(1000.0f); +#define SOCK_THIN_FILM_IOR_ID 29 } static void node_shader_init_principled(bNodeTree * /*ntree*/, bNode *node)